Package: django-candv-choices
Use complex constants built withcandvlibrary instead of standardchoicesfields forDjangomodels.Tryonline live demo! Usedemo/demoas login/pass for authentication.Table of contentsInstallationProblem overviewSolutionCaveatsThings to think aboutChangelogInstallationInstall fromPyPI:$pipinstalldjango-candv-choicesProblem overviewWell, you need to define some constant choices for your Django model field.
Let’s start from defining constants themselves:# constants.pyfromdjango.utils.translationimportugettext_lazyas_AUTH_TYPE_BASIC='BASIC'AUTH_TYPE_DIGEST='DIGEST'AUTH_TYPE_CLIENT_CERT='CLIENT-CERT'AUTH_TYPE_FORM='FORM'AUTH_TYPES=((AUTH_TYPE_BASIC,_("HTTP Basic Authentication")),(AUTH_TYPE_DIGEST,_("HTTP Digest Authentication ")),(AUTH_TYPE_CLIENT_CERT,_("HTTPS Client Authentication ")),(AUTH_TYPE_FORM,_("Form Based Authentication ")),)Here we define constant names and attach verbose names to them. Bloated
definition, no docstring for constants group, no docstings per constant. What
if you need to define some help text per constant? 4 more definitions? Well,
then just imagine, how you will attach them. And what about other attributes?
And what about adding some methods for constants? How about getting constant by
its name? By value? And how about performing some operations on the whole
constants group?Only at this point you may end up with one big module which will work only with
one group of constants. And this work will be a big pain.But OK, let’s go further and define some DB model field:# models.pyfromdjango.dbimportmodelsfromdjango.utils.translationimportugettext_lazyas_from.constantsimportAUTH_TYPES,AUTH_TYPE_BASICclassRequest(models.Model):auth_type=models.CharField(verbose_name=_("Auth type"),help_text=_("Example of default constants"),choices=AUTH_TYPES,blank=False,max_length=11,default=AUTH_TYPE_BASIC)3 things to mention here:you have to import constant group itself;you may have to import dafault value too;you need go back to constants definition, iterate over each constant,
calculate its length and select the longest value to pass it asmax_lengthargument. And don’t try to make a mistake, or you will be
punished otherwise.I useCharFieldhere intentionally. It can be good to useIntegerField,PositiveSmallIntegerFieldand so on, but it is very probable that you will
be willing someone to kill you due to hidden bugs.Now it’s showtime! Let’s render our field:<ul>{%forrinrequests%}<li>{{r.auth_type}}</li>{%endfor%}</ul>What do you see?BASIC,DIGEST,FORM, etc. Oops! How to get our
human messages likeHTTP Basic Authentication?You need to convert constants group todictand pass it to template’s
context! But wait, this is not the end. You can not access dict values directly
within templates. You need to create a library of template tags, register a
filter and load the library to template:# templatetags/custom_tags.pyfromdjangoimporttemplateregister=template.Library()@register.filterdeflookup(d,key):returnd[key]{%loadcustom_tags%}<ul>{%forrinrequests%}<li>{{AUTH_TYPES|lookup:r.auth_type}}</li>{%endfor%}</ul>This is madness!SolutionThe solution is to usecandvand this library. The former allows you to
define stand-alone groups of complex constants and latter allows you to use
those constants as choises.Let’s examine some simple example and define some constants:# constants.pyfromcandvimportSimpleConstant,ConstantsclassMETHOD_TYPE(Constants):"""
    Available HTTP methods.
    """GET=SimpleConstant()PUT=SimpleConstant()POST=SimpleConstant()DELETE=SimpleConstant()TRACE=SimpleConstant()Here we defined a group of constants with no attributes. Looks pretty, let’s
use it:# models.pyfromcandv_x.django.choicesimportChoicesFieldfromdjango.dbimportmodelsfromdjango.utils.translationimportugettext_lazyas_from.constantsimportMETHOD_TYPEclassRequest(models.Model):method=ChoicesField(verbose_name=_("method"),help_text=_("Example of simple candv constants"),choices=METHOD_TYPE,blank=False,)That’s all. You can pass some default value if you want,
e.g.default=METHOD_TYPE.GET.Now you can render it:<ul>{%forrinrequests%}<li>{{r.method.name}}</li>{%endfor%}</ul>The output will containGET,PUT,POST, etc. Want more? Let’s add
values, verbose names and help texts:# constants.pyfromcandvimportVerboseValueConstant,Valuesfromdjango.utils.translationimportugettext_lazyas_classRESULT_TYPE(Values):"""
    Possible operation results.
    """SUCCESS=VerboseValueConstant(value='2C7517',verbose_name=_("Success"),help_text=_("Yay! Everything is good!"))FAILURE=VerboseValueConstant(value='A30D0D',verbose_name=_("Failure"),help_text=_("Oops! Something went wrong!"))PENDING=VerboseValueConstant(value='E09F26',verbose_name=_("Pending"),help_text=_("Still waiting for the task to complete..."))Please, refer tocandv usageto learn how to define and use constants.
You may findcandv customizationuseful too.Here we have usedValuesas container andVerboseValueConstantas class
for items. Each constant has aname(e.g.SUCCESS), a value, a verbose
text and a help text. All of this you can access directly from everywhere.Field definition does not differ much from previous:# models.pyfromcandv_x.django.choicesimportChoicesFieldfromdjango.dbimportmodelsfromdjango.utils.translationimportugettext_lazyas_from.constantsimportRESULT_TYPEclassRequest(models.Model):result=ChoicesField(verbose_name=_("result"),help_text=_("Example of complex candv constants with verbose names, ""help texts and inner values"),choices=RESULT_TYPE,blank=False,default=RESULT_TYPE.SUCCESS,)You may useblank=Trueif you wish, there’s no problem. Let’s output our
data:<table>{%forrinrequests%}<tr>
    <td style="color: #{{r.result.value}};" title="{{r.result.help_text}}">{{r.result.verbose_name}}</td>
  </tr>{%endfor%}</table>Not so hard, innit?You can pass any constants toChoicesFieldfrom your old projects or
external libraries. Enjoy!CaveatsDjango admin renders choices by converting them to strings. So,__str__and__unicode__methods will be automatically overriden for
constant items. It will return the name of the constant. By default,
constants incandvdo not have those methods at all (I cannot find a
reason why the should to), so it seems not to be a problem. Just be aware.candvsupports creatinghierarchies of constants. If you have some
reason to use them as choices for DB field, take into accout that choices
will be built only from top-level group of constants.Things to think aboutDjango hasMultipleChoiceFieldandTypedMultipleChoiceField. I haven’t
used used them, but I think it can be useful to implement analogues for
‘candv’, especially forMultipleChoiceField.I think, there is a place to think about implementation of full support of
hierarchies. Maybe it’s possible to make some nested choices, or at least
flatten them.ChangelogYou can click a version name to see a diff with the previous one.1.1.5(Aug 1, 2015)Fix usage of default values for migrations in Django >= 1.7
(issue #8).Implement field serializer for restframework as a separate librarydjango-rf-candv-choices(issue #9).1.1.4(Jul 2, 2015)Add support for Python 3 (issue #6).Add support for migrations in Django >= 1.7 (issue #7).Imports which will become deprecated in Django 1.9 are not used now.1.1.3(Oct 11, 2014)candvdependency updated up tov1.2.0.Add support for South (issue #4).Choices’ form field can display help text as option’s title now
(issue #1).1.1.0(Jul 19, 2014)rename package tochoicesand move intocandv_x.django(xstands forextensions)1.0.0(Jun 22, 2014)Initial version