101 votes

Spécifier un ENUM mySQL dans un modèle Django

Comment procéder pour spécifier et utiliser un ENUM dans un modèle Django ?

4 votes

Steve, si vous voulez dire utiliser le type ENUM de MySQL, alors vous n'avez pas de chance, pour autant que je sache Django ne fournit pas de support pour cela (cette fonctionnalité n'est pas disponible dans toutes les bases de données supportées par Django). La réponse fournie par Paul fonctionne, mais elle ne le fera pas définir le type dans la BD.

113voto

fulmicoton Points 5389

Desde el Documentation sur Django :

MAYBECHOICE = (
    ('y', 'Yes'),
    ('n', 'No'),
    ('u', 'Unknown'),
)

Et vous définissez un champ de char dans votre modèle :

married = models.CharField(max_length=1, choices=MAYBECHOICE)

Vous pouvez faire de même avec des champs entiers si vous n'aimez pas avoir de lettres. dans votre base de données.

Dans ce cas, réécrivez vos choix :

MAYBECHOICE = (
    (0, 'Yes'),
    (1, 'No'),
    (2, 'Unknown'),
)

9 votes

Cela n'empêche pas les valeurs "fausses" d'être enregistrées si elles n'ont pas été nettoyées auparavant, n'est-ce pas ?

0 votes

@Strayer : oui, je suppose que cela n'est utile que pour l'utilisation de formulaires modèles.

0 votes

Notez que le style Django recommandé implique que les caractères doivent être des constantes : docs.djangoproject.com/fr/dev/internals/contributing/

37voto

from django.db import models

class EnumField(models.Field):
    """
    A field class that maps to MySQL's ENUM type.

    Usage:

    class Card(models.Model):
        suit = EnumField(values=('Clubs', 'Diamonds', 'Spades', 'Hearts'))

    c = Card()
    c.suit = 'Clubs'
    c.save()
    """
    def __init__(self, *args, **kwargs):
        self.values = kwargs.pop('values')
        kwargs['choices'] = [(v, v) for v in self.values]
        kwargs['default'] = self.values[0]
        super(EnumField, self).__init__(*args, **kwargs)

    def db_type(self):
        return "enum({0})".format( ','.join("'%s'" % v for v in self.values) )

35voto

Carl Meyer Points 30736

Utilisation de la choices n'utilisera pas le type de données ENUM ; il créera simplement un VARCHAR ou un INTEGER, selon que vous utilisez ou non le paramètre choices avec un CharField ou un IntegerField. En général, c'est très bien. S'il est important pour vous que le type ENUM soit utilisé au niveau de la base de données, vous avez trois options :

  1. Utilisez "./manage.py sql appname" pour voir le SQL généré par Django, modifiez-le manuellement pour utiliser le type ENUM, et exécutez-le vous-même. Si vous créez d'abord la table manuellement, "./manage.py syncdb" ne s'en occupera pas.
  2. Si vous ne voulez pas le faire manuellement à chaque fois que vous générez votre BD, mettez du SQL personnalisé dans appname/sql/modelname.sql pour exécuter la commande ALTER TABLE appropriée.
  3. Créer un type de champ personnalisé et définissez la méthode db_type de manière appropriée.

Avec l'une ou l'autre de ces options, il vous incombe de gérer les implications de la portabilité entre les bases de données. Dans l'option 2, vous pourriez utiliser SQL personnalisé spécifique à la base de données pour s'assurer que votre ALTER TABLE n'est exécuté que sur MySQL. Dans l'option 3, votre méthode db_type devrait vérifier le moteur de la base de données et définir le type de colonne db à un type qui existe réellement dans cette base de données.

UPDATE : Depuis que le framework de migrations a été ajouté dans Django 1.7, les options 1 et 2 ci-dessus sont entièrement obsolètes. L'option 3 a toujours été la meilleure de toute façon. La nouvelle version des options 1/2 impliquerait une migration personnalisée complexe utilisant SeparateDatabaseAndState -- mais vous voulez vraiment l'option 3.

11voto

David Cain Points 5164

Réglage de choices sur le champ permettra une certaine validation du côté de Django, mais elle ne le fera pas définir toute forme d'un type énuméré du côté de la base de données.

Comme d'autres l'ont mentionné, la solution consiste à spécifier db_type sur un champ personnalisé.

Si vous utilisez un backend SQL (par exemple MySQL), vous pouvez procéder comme suit :

from django.db import models

class EnumField(models.Field):
    def __init__(self, *args, **kwargs):
        super(EnumField, self).__init__(*args, **kwargs)
        assert self.choices, "Need choices for enumeration"

    def db_type(self, connection):
        if not all(isinstance(col, basestring) for col, _ in self.choices):
            raise ValueError("MySQL ENUM values should be strings")
        return "ENUM({})".format(','.join("'{}'".format(col) 
                                          for col, _ in self.choices))

class IceCreamFlavor(EnumField, models.CharField):
    def __init__(self, *args, **kwargs):
        flavors = [('chocolate', 'Chocolate'),
                   ('vanilla', 'Vanilla'),
                  ]
        super(IceCreamFlavor, self).__init__(*args, choices=flavors, **kwargs)

class IceCream(models.Model):
    price = models.DecimalField(max_digits=4, decimal_places=2)
    flavor = IceCreamFlavor(max_length=20)

Exécuter syncdb et inspectez votre tableau pour voir si le ENUM a été créé correctement.

mysql> SHOW COLUMNS IN icecream;
+--------+-----------------------------+------+-----+---------+----------------+
| Field  | Type                        | Null | Key | Default | Extra          |
+--------+-----------------------------+------+-----+---------+----------------+
| id     | int(11)                     | NO   | PRI | NULL    | auto_increment |
| price  | decimal(4,2)                | NO   |     | NULL    |                |
| flavor | enum('chocolate','vanilla') | NO   |     | NULL    |                |
+--------+-----------------------------+------+-----+---------+----------------+

10voto

keithxm23 Points 663

http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/

class Entry(models.Model):
    LIVE_STATUS = 1
    DRAFT_STATUS = 2
    HIDDEN_STATUS = 3
    STATUS_CHOICES = (
        (LIVE_STATUS, 'Live'),
        (DRAFT_STATUS, 'Draft'),
        (HIDDEN_STATUS, 'Hidden'),
    )
    # ...some other fields here...
    status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS)

live_entries = Entry.objects.filter(status=Entry.LIVE_STATUS)
draft_entries = Entry.objects.filter(status=Entry.DRAFT_STATUS)

if entry_object.status == Entry.LIVE_STATUS:

Il s'agit d'une autre façon simple et agréable d'implémenter les enums, bien qu'elle ne permette pas vraiment de sauvegarder les enums dans la base de données.

Toutefois, elle vous permet de faire référence à l'"étiquette" lorsque vous effectuez une requête ou que vous spécifiez des valeurs par défaut, contrairement à la réponse la mieux notée où vous devez utiliser la "valeur" (qui peut être un nombre).

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