Wie spezifiziere und verwende ich eine ENUM in einem Django-Modell?
Wie spezifiziere und verwende ich eine ENUM in einem Django-Modell?
Antworten:
Aus der Django-Dokumentation :
MAYBECHOICE = (
('y', 'Yes'),
('n', 'No'),
('u', 'Unknown'),
)
Und Sie definieren ein Zeichenfeld in Ihrem Modell:
married = models.CharField(max_length=1, choices=MAYBECHOICE)
Sie können dasselbe mit Ganzzahlfeldern tun, wenn Sie keine Buchstaben in Ihrer Datenbank haben möchten.
Schreiben Sie in diesem Fall Ihre Auswahl neu:
MAYBECHOICE = (
(0, 'Yes'),
(1, 'No'),
(2, 'Unknown'),
)
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) )
Bei Verwendung des choices
Parameters wird der ENUM-DB-Typ nicht verwendet. Es wird nur ein VARCHAR oder INTEGER erstellt, je nachdem, ob Sie choices
mit einem CharField oder IntegerField verwenden. Im Allgemeinen ist dies in Ordnung. Wenn es Ihnen wichtig ist, dass der ENUM-Typ auf Datenbankebene verwendet wird, haben Sie drei Möglichkeiten:
Bei jeder dieser Optionen liegt es in Ihrer Verantwortung, sich mit den Auswirkungen auf die datenbankübergreifende Portabilität zu befassen. In Option 2 können Sie datenbank-backendspezifisches benutzerdefiniertes SQL verwenden, um sicherzustellen, dass ALTER TABLE nur unter MySQL ausgeführt wird. In Option 3 müsste Ihre Methode db_type das Datenbankmodul überprüfen und den Spaltentyp db auf einen Typ setzen, der tatsächlich in dieser Datenbank vorhanden ist.
UPDATE : Da das Migrationsframework in Django 1.7 hinzugefügt wurde, sind die obigen Optionen 1 und 2 völlig veraltet. Option 3 war sowieso immer die beste Option. Die neue Version von Optionen 1/2 würde eine komplexe benutzerdefinierte Migration mit beinhalten SeparateDatabaseAndState
- aber Sie möchten wirklich Option 3.
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:
Dies ist eine weitere nette und einfache Möglichkeit, Enums zu implementieren, obwohl Enums nicht wirklich in der Datenbank gespeichert werden.
Sie können jedoch bei jeder Abfrage oder Angabe von Standardeinstellungen auf das 'Label' verweisen, im Gegensatz zu der am besten bewerteten Antwort, bei der Sie den 'Wert' (der eine Zahl sein kann) verwenden müssen.
Die Einstellung choices
auf dem Feld ermöglicht eine gewisse Validierung am Django-Ende, wird dies jedoch nicht tun jede Form eines Aufzählungstypen auf der Datenbank Ende definieren.
Wie andere bereits erwähnt haben, besteht die Lösung darin, anzugeben db_type
in einem benutzerdefinierten Feld .
Wenn Sie ein SQL-Backend (z. B. MySQL) verwenden, können Sie dies folgendermaßen tun:
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)
Führen syncdb
Sie die Tabelle aus und überprüfen Sie sie, um festzustellen, ob die Tabelle ENUM
ordnungsgemäß erstellt wurde.
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 | |
+--------+-----------------------------+------+-----+---------+----------------+
'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'
.
Wenn Sie Ihre Datenbanken wirklich verwenden möchten, geben Sie ENUM ein:
Viel Glück!
Derzeit gibt es zwei Github-Projekte, die auf dem Hinzufügen dieser Projekte basieren, obwohl ich nicht genau untersucht habe, wie sie implementiert sind:
Ich glaube auch nicht, dass DB-Aufzählungstypen verwendet werden, aber sie sind für den ersten in Arbeit .
Aus der Dokumentation :
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,
)
Beachten Sie nun, dass die Auswahl auf Datenbankebene nicht erzwungen wird. Dies ist nur ein Python-Konstrukt. Wenn Sie diesen Wert auch in der Datenbank erzwingen möchten, können Sie dies mit Datenbankeinschränkungen kombinieren:
class Student(models.Model):
...
class Meta:
constraints = [
CheckConstraint(
check=Q(year_in_school__in=YearInSchool.values),
name="valid_year_in_school")
]