How to properly use the “choices” field option in Django

前端 未结 7 2111
孤独总比滥情好
孤独总比滥情好 2020-12-12 23:17

I\'m reading the tutorial here: https://docs.djangoproject.com/en/1.5/ref/models/fields/#choices and i\'m trying to create a box where the user can select the month he was b

相关标签:
7条回答
  • 2020-12-12 23:55

    I would suggest to use django-model-utils instead of Django built-in solution. The main advantage of this solution is the lack of string declaration duplication. All choice items are declared exactly once. Also this is the easiest way for declaring choices using 3 values and storing database value different than usage in source code.

    from django.utils.translation import ugettext_lazy as _
    from model_utils import Choices
    
    class MyModel(models.Model):
       MONTH = Choices(
           ('JAN', _('January')),
           ('FEB', _('February')),
           ('MAR', _('March')),
       )
       # [..]
       month = models.CharField(
           max_length=3,
           choices=MONTH,
           default=MONTH.JAN,
       )
    

    And with usage IntegerField instead:

    from django.utils.translation import ugettext_lazy as _
    from model_utils import Choices
    
    class MyModel(models.Model):
       MONTH = Choices(
           (1, 'JAN', _('January')),
           (2, 'FEB', _('February')),
           (3, 'MAR', _('March')),
       )
       # [..]
       month = models.PositiveSmallIntegerField(
           choices=MONTH,
           default=MONTH.JAN,
       )
    
    • This method has one small disadvantage: in any IDE (eg. PyCharm) there will be no code completion for available choices (it’s because those values aren’t standard members of Choices class).
    0 讨论(0)
  • 2020-12-12 23:59

    I think no one actually has answered to the first question:

    Why did they create those variables?

    Those variables aren't strictly necessary. It's true. You can perfectly do something like this:

    MONTH_CHOICES = (
        ("JANUARY", "January"),
        ("FEBRUARY", "February"),
        ("MARCH", "March"),
        # ....
        ("DECEMBER", "December"),
    )
    
    month = models.CharField(max_length=9,
                      choices=MONTH_CHOICES,
                      default="JANUARY")
    

    Why using variables is better? Error prevention and logic separation.

    JAN = "JANUARY"
    FEB = "FEBRUARY"
    MAR = "MAR"
    # (...)
    
    MONTH_CHOICES = (
        (JAN, "January"),
        (FEB, "February"),
        (MAR, "March"),
        # ....
        (DEC, "December"),
    )
    

    Now, imagine you have a view where you create a new Model instance. Instead of doing this:

    new_instance = MyModel(month='JANUARY')
    

    You'll do this:

    new_instance = MyModel(month=MyModel.JAN)
    

    In the first option you are hardcoding the value. If there is a set of values you can input, you should limit those options when coding. Also, if you eventually need to change the code at the Model layer, now you don't need to make any change in the Views layer.

    0 讨论(0)
  • 2020-12-13 00:00

    For Django3.0+, use models.TextChoices (see docs-v3.0 for enumeration types)

    from django.db import models
    
    class MyModel(models.Model):
        class Month(models.TextChoices):
            JAN = '1', "JANUARY"
            FEB = '2', "FEBRUARY"
            MAR = '3', "MAR"
            # (...)
    
        month = models.CharField(
            max_length=2,
            choices=Month.choices,
            default=Month.JAN
        )
    
    0 讨论(0)
  • 2020-12-13 00:00

    You can't have bare words in the code, that's the reason why they created variables (your code will fail with NameError).

    The code you provided would create a database table named month (plus whatever prefix django adds to that), because that's the name of the CharField.

    But there are better ways to create the particular choices you want. See a previous Stack Overflow question.

    import calendar
    tuple((m, m) for m in calendar.month_name[1:])
    
    0 讨论(0)
  • 2020-12-13 00:01

    $ pip install django-better-choices

    For those who are interested, I have created django-better-choices library, that provides a nice interface to work with Django choices for Python 3.7+. It supports custom parameters, lots of useful features and is very IDE friendly.

    You can define your choices as a class:

    from django_better_choices import Choices
    
    
    class PAGE_STATUS(Choices):
        CREATED = 'Created'
        PENDING = Choices.Value('Pending', help_text='This set status to pending')
        ON_HOLD = Choices.Value('On Hold', value='custom_on_hold')
    
        VALID = Choices.Subset('CREATED', 'ON_HOLD')
    
        class INTERNAL_STATUS(Choices):
            REVIEW = 'On Review'
    
        @classmethod
        def get_help_text(cls):
            return tuple(
                value.help_text
                for value in cls.values()
                if hasattr(value, 'help_text')
            )
    

    Then do the following operations and much much more:

    print( PAGE_STATUS.CREATED )                # 'created'
    print( PAGE_STATUS.ON_HOLD )                # 'custom_on_hold'
    print( PAGE_STATUS.PENDING.display )        # 'Pending'
    print( PAGE_STATUS.PENDING.help_text )      # 'This set status to pending'
    
    'custom_on_hold' in PAGE_STATUS.VALID       # True
    PAGE_STATUS.CREATED in PAGE_STATUS.VALID    # True
    
    PAGE_STATUS.extract('CREATED', 'ON_HOLD')   # ~= PAGE_STATUS.VALID
    
    for value, display in PAGE_STATUS:
        print( value, display )
    
    PAGE_STATUS.get_help_text()
    PAGE_STATUS.VALID.get_help_text()
    

    And of course, it is fully supported by Django and Django Migrations:

    class Page(models.Model):
        status = models.CharField(choices=PAGE_STATUS, default=PAGE_STATUS.CREATED)
    

    Full documentation here: https://pypi.org/project/django-better-choices/

    0 讨论(0)
  • 2020-12-13 00:03

    The cleanest solution is to use the django-model-utils library:

    from model_utils import Choices
    
    class Article(models.Model):
        STATUS = Choices('draft', 'published')
        status = models.CharField(choices=STATUS, default=STATUS.draft, max_length=20)
    

    https://django-model-utils.readthedocs.io/en/latest/utilities.html#choices

    0 讨论(0)
提交回复
热议问题