150 votes

Comment utiliser correctement l'option de champ "choix" dans Django

Je suis en train de lire le tutoriel ici : https://docs.djangoproject.com/en/1.5/ref/models/fields/#choices et j'essaie de créer une boîte où l'utilisateur peut sélectionner le mois de sa naissance. Voici ce que j'ai essayé

 MONTH_CHOICES = (
    (JANUARY, "January"),
    (FEBRUARY, "February"),
    (MARCH, "March"),
    ....
    (DECEMBER, "December"),
)

month = CharField(max_length=9,
                  choices=MONTHS_CHOICES,
                  default=JANUARY)

Est-ce correct ? Je vois que dans le tutoriel que j'ai lu, pour une raison quelconque, ils ont créé les variables en premier, comme ceci

FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'

Pourquoi ont-ils créé ces variables ? Le code que j'ai fourni créerait-il donc une colonne "Choix des mois" dans la base de données appelée "Personnes" et indiquerait-il le mois de naissance de l'utilisateur après qu'il ait cliqué sur l'un des mois et soumis le formulaire ?

237voto

JCJS Points 1083

Je pense que personne n'a vraiment répondu à la première question :

Pourquoi ont-ils créé ces variables ?

Ces variables ne sont pas strictement nécessaires. C'est vrai. Tu peux parfaitement faire quelque chose comme ça :

MONTH_CHOICES = (
    ("JANUARY", "January"),
    ("FEBRUARY", "February"),
    ("MARCH", "March"),
    # ....
    ("DECEMBER", "December"),
)

month = models.CharField(max_length=9,
                  choices=MONTH_CHOICES,
                  default="JANUARY")

Pourquoi l'utilisation de variables est préférable ? Prévention des erreurs et séparation logique.

JAN = "JANUARY"
FEB = "FEBRUARY"
MAR = "MAR"
# (...)

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

Maintenant, imaginez que vous avez une vue où vous créez une nouvelle instance de modèle. Au lieu de faire cela :

new_instance = MyModel(month='JANUARY')

Tu vas le faire :

new_instance = MyModel(month=MyModel.JAN)

Dans la première option, vous codifiez en dur la valeur. S'il existe un ensemble de valeurs que vous pouvez saisir, vous devez limiter ces options lors du codage. En outre, si vous devez éventuellement modifier le code au niveau de la couche Modèle, vous n'avez plus besoin d'effectuer de modifications dans la couche Vues.

163voto

Waket Zheng Points 1253

Pour Django3.0+, utilisez models.TextChoices (voir docs-v3.0 para types d'énumération )

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
    )

Utilisation : :

>>> obj = MyModel.objects.create(month='1')
>>> assert obj.month == obj.Month.JAN == '1'
>>> assert MyModel.Month(obj.month) is obj.Month.JAN
>>> assert MyModel.Month(obj.month).value is '1'
>>> assert MyModel.Month(obj.month).label == 'JANUARY'
>>> assert MyModel.Month(obj.month).name == 'JAN'
>>> assert MyModel.objects.filter(month=MyModel.Month.JAN).count() >= 1

>>> obj2 = MyModel(month=MyModel.Month.FEB)
>>> assert obj2.get_month_display() == obj2.Month(obj2.month).label

Disons que nous savons que l'étiquette est 'JANUARY', comment obtenir le nom 'JAN' et la valeur '1' ?

label = "JANUARY"
name = {i.label: i.name for i in MyModel.Month}[label]
print(repr(name))  # 'JAN'
value = {i.label: i.value for i in MyModel.Month}[label]
print(repr(value))  # '1'

Personnellement, je préfère utiliser models.IntegerChoices

class MyModel(models.Model):
    class Month(models.IntegerChoices):
        JAN = 1, "JANUARY"
        FEB = 2, "FEBRUARY"
        MAR = 3, "MAR"
        # (...)

    month = models.PositiveSmallIntegerField(
        choices=Month.choices,
        default=Month.JAN
    )

69voto

alecxe Points 50783

Selon le documentation :

Champ.choix

Un itérable (par exemple, une liste ou un tuple) constitué lui-même de itérables d'exactement deux éléments (par exemple, [(A, B), (A, B) ...]) à utiliser comme choix pour ce champ. Si cette option est donnée, le widget de formulaire par défaut sera une boîte de sélection avec ces choix à la place. sera une boîte de sélection avec ces choix au lieu du champ de texte standard.

Le premier élément de chaque tuple est la valeur réelle à stocker, et le deuxième élément est le nom lisible par l'homme.

Ainsi, votre code est correct, sauf que vous devriez soit définir des variables JANUARY , FEBRUARY etc. ou utiliser calendar pour définir MONTH_CHOICES :

import calendar
...

class MyModel(models.Model):
    ...

    MONTH_CHOICES = [(str(i), calendar.month_name[i]) for i in range(1,13)]

    month = models.CharField(max_length=9, choices=MONTH_CHOICES, default='1')

22voto

Kai Points 350

Mar, 2022 Mise à jour :

Le moyen le plus simple, le plus facile, le meilleur et le plus récent est d'utiliser "models.TextChoices" qui est intégré ce qui signifie "Vous n'avez pas besoin d'installer de paquet" .

"models.py" :

from django.db import models

class MyModel(models.Model):

    class Months(models.TextChoices):
        JANUARY = 'JAN', 'January'
        FEBRUARY = 'FEB', 'February'
        MARCH = 'MAR', 'March'
        APRIL = 'APR', 'April'
        MAY = 'MAY', 'May'

    month = models.CharField(
        max_length=3,
        choices=Months.choices,
        default=Months.APRIL 
    )

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', 'Freshman'
        SOPHOMORE = 'SO', 'Sophomore'
        JUNIOR = 'JR', 'Junior'
        SENIOR = 'SR', 'Senior'
        GRADUATE = 'GR', 'Graduate'

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.SOPHOMORE,
    )

J'ai également réécrit le code ci-dessus dans l'ancienne méthode qui est aussi intégré .

"models.py" :

from django.db import models

class MyModel(models.Model):

    JANUARY = 'JAN'
    FEBRUARY = 'FEB'
    MARCH = 'MAR'
    APRIL = 'APR'
    MAY = 'MAY'

    MANTHS = [
        (JANUARY, 'January'),
        (FEBRUARY, 'February'),
        (MARCH, 'March'),
        (APRIL, 'April'),
        (MAY, 'May'),
    ]

    month = models.CharField(
        max_length=3,
        choices=MANTHS,
        default=APRIL # or "default=MANTHS[4]"
    )

    FRESHMAN = 'FR'
    SOPHOMORE = 'SO'
    JUNIOR = 'JR'
    SENIOR = 'SR'
    GRADUATE = 'GR'

    YEAR_IN_SCHOOL_CHOICES = [
        (FRESHMAN, 'Freshman'),
        (SOPHOMORE, 'Sophomore'),
        (JUNIOR, 'Junior'),
        (SENIOR, 'Senior'),
        (GRADUATE, 'Graduate'),
    ]

    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=SOPHOMORE # or "default=YEAR_IN_SCHOOL_CHOICES[1]"
    )

Comme vous pouvez le constater, la nouvelle méthode est beaucoup plus simple que l'ancienne.

13voto

Babken Vardanyan Points 544

La solution la plus propre est d'utiliser le django-model-utils bibliothèque :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X