Angi en mySQL ENUM i en Django modell

stemmer
82

Hvordan går jeg om å spesifisere og bruke en ENUM i en Django-modell?

Publisert på 21/08/2008 klokken 23:48
kilden bruker
På andre språk...                            


9 svar

stemmer
97

Fra Django dokumentasjon :

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

Og du definerer en charfield i modellen:

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

Du kan gjøre det samme med heltallsfeltene hvis du ikke liker å ha bokstaver i db.

I så fall skrive om dine valg:

MAYBECHOICE = (
    (0, 'Yes'),
    (1, 'No'),
    (2, 'Unknown'),
)
Svarte 21/08/2008 kl. 23:54
kilden bruker

stemmer
35
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) )
Svarte 07/10/2009 kl. 10:47
kilden bruker

stemmer
30

Bruk av choicesparameteren ikke bruke ENUM db type; det vil bare skape en VARCHAR eller INTEGER, avhengig av om du bruker choicesen charfield eller IntegerField. Vanligvis er dette helt fint. Hvis det er viktig for deg at ENUM typen brukes på databasenivå, har du tre alternativer:

  1. Bruk "./manage.py sql appname" for å se SQL Django genererer, manuelt endre den til å bruke ENUM type, og kjøre den selv. Hvis du oppretter tabellen manuelt først, "./manage.py SyncDB" vil ikke rote med det.
  2. Hvis du ikke ønsker å gjøre dette manuelt hver gang du genererer DB, sette noen tilpassede SQL i appname / sql / modelname.sql å utføre den aktuelle ALTER TABLE kommandoen.
  3. Lag et egendefinert felt type og definerer db_type metoden på riktig måte.

Med noen av disse alternativene, vil det være ditt ansvar å håndtere konsekvensene for kryss-database portabilitet. I alternativ 2, kan du bruke database-backend-spesifikke tilpassede SQL for å sikre din ALTER TABLE er kun kjøres på MySQL. I alternativ 3, vil din db_type metoden må sjekke databasen motoren og sett på db kolonnetypen til en type som faktisk eksisterer i databasen.

OPPDATERING : Siden vandringer rammene ble lagt i Django 1,7, alternativene 1 og 2 ovenfor er helt foreldet. Alternativ 3 var alltid det beste alternativet uansett. Den nye versjonen av alternativene 1/2 ville innebære en kompleks tilpasset migrering ved hjelp av SeparateDatabaseAndState- men egentlig du vil alternativ 3.

Svarte 29/08/2008 kl. 03:57
kilden bruker

stemmer
9

Stille choicespå feltet vil tillate noen validering på Django slutten, men det vil ikke definere noen form for en nummerert type på databasen slutten.

Som andre har nevnt, er løsningen å spesifisere db_typepå et egendefinert felt.

Hvis du bruker en SQL backend (f.eks MySQL), kan du gjøre dette slik:

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)

Kjør syncdb, og inspisere tabellen for å se at det ENUMble opprettet på riktig måte.

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    |                |
+--------+-----------------------------+------+-----+---------+----------------+
Svarte 26/09/2013 kl. 23:38
kilden bruker

stemmer
8

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:

Dette er en annen fin og enkel måte å implementere enums selv om det ikke egentlig spare enums i databasen.

Men det betyr at du kan referere til 'etiketten' når spørring eller angi standardinnstillinger i motsetning til topp-karakter svar der du må bruke 'verdi' (som kan være et tall).

Svarte 26/10/2012 kl. 15:03
kilden bruker

stemmer
6

Hvis du virkelig ønsker å bruke databaser ENUM Type:

  1. Bruk Django 1.x
  2. Gjenkjenne din søknad vil bare fungere på noen databaser.
  3. Puzzle gjennom denne dokumentasjonen siden: http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

Lykke til!

Svarte 02/12/2008 kl. 18:21
kilden bruker

stemmer
3

Det er nå to GitHub prosjekter basert på å legge disse, selv om jeg ikke har sett på nøyaktig hvordan de er implementert:

  1. Django-EnumField :
    Gir en oppregning Django modell felt (ved hjelp IntegerField) med gjenbrukbare enums og overgang validering.
  2. Django-EnumFields :
    Denne pakken lar deg bruke ekte Python (PEP435-stil) enums med Django.

Jeg tror ikke heller bruke DB enum-typer, men de er i arbeid for første.

Svarte 09/02/2015 kl. 11:23
kilden bruker

stemmer
0

Django 3.0 har innebygd støtte for enums

Fra dokumentasjon :

from django.utils.translation import gettext_lazy as _

class Student(models.Model):

    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.FRESHMAN,
    )

Nå, vær oppmerksom på at det ikke håndheve valg på et databasenivå dette er Python bare konstruere. Hvis du ønsker å også håndheve de verdi på databasen du kan kombinere det med databasen begrensninger:

class Student(models.Model):
    ...

    class Meta:
        constraints = [
            CheckConstraint(
                check=Q(year_in_school__in=YearInSchool.values),
                name="valid_year_in_school")
        ]
Svarte 22/09/2019 kl. 21:08
kilden bruker

stemmer
-2

En toppen av models.py fil, legge til denne linjen når du gjør dine import:

    enum = lambda *l: [(s,_(s)) for s in l]
Svarte 03/03/2014 kl. 19:18
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more