Steve, jeśli miałeś na myśli użycie typu MySQL ENUM, to nie masz szczęścia, o ile wiem, Django nie zapewnia tego wsparcia (ta funkcja nie jest dostępna we wszystkich bazach danych obsługiwanych przez Django). Odpowiedź udzielona przez Paula działa, ale nie definiuje typu w bazie danych.
Jak powiedział @Carl Meyer w swojej odpowiedzi, NIE tworzy to kolumny ENUM w bazie danych. Tworzy kolumnę VARCHAR lub INTEGER, więc tak naprawdę nie odpowiada na pytanie.
Ariel
Czy mogę dodać funkcję wyborów z polem liczby całkowitej? @fulmicoton
Ilyas karim
36
from django.db import models
classEnumField(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)
defdb_type(self):return"enum({0})".format( ','.join("'%s'" % v for v in self.values) )
Od wersji django 1.2 będziesz musiał dodać drugi parametr, connection, do parametru db_type def.
Hans Lawrenz,
2
Co się wtedy stało z kodekatelogiem? Takie lokosy mogłyby być dobrym pomysłem… Dostaję teraz 404 - nawet dla strony głównej.
Danny Staple
33
Użycie choicesparametru nie spowoduje użycia typu ENUM db; po prostu utworzy VARCHAR lub INTEGER, w zależności od tego, czy używasz choicesz CharField czy IntegerField. Ogólnie jest to w porządku. Jeśli jest dla Ciebie ważne, aby typ ENUM był używany na poziomie bazy danych, masz trzy opcje:
Użyj "./manage.py sql appname", aby zobaczyć, jak SQL Django generuje, ręcznie zmodyfikuj go tak, aby używał typu ENUM i uruchom go samodzielnie. Jeśli najpierw utworzysz tabelę ręcznie, „./manage.py syncdb” nie zadziała z nią.
Jeśli nie chcesz robić tego ręcznie za każdym razem, gdy generujesz swoją bazę danych, umieść niestandardowy kod SQL w pliku nazwa_aplikacji / sql / modelname.sql, aby wykonać odpowiednie polecenie ALTER TABLE.
W przypadku każdej z tych opcji do Twojej odpowiedzialności należałoby zajęcie się implikacjami dla przenoszenia między bazami danych. W opcji 2 możesz użyć niestandardowego SQL specyficznego dla zaplecza bazy danych, aby upewnić się, że ALTER TABLE działa tylko w MySQL. W opcji 3 metoda db_type musiałaby sprawdzić silnik bazy danych i ustawić typ kolumny db na typ, który faktycznie istnieje w tej bazie danych.
AKTUALIZACJA : Ponieważ ramy migracji zostały dodane w Django 1.7, powyższe opcje 1 i 2 są całkowicie przestarzałe. Opcja 3 i tak była zawsze najlepsza. Nowa wersja opcji 1/2 wymagałaby złożonej migracji niestandardowej przy użyciu SeparateDatabaseAndState- ale naprawdę potrzebujesz opcji 3.
Jest to kolejny przyjemny i łatwy sposób implementacji wyliczeń, chociaż tak naprawdę nie zapisuje wyliczeń w bazie danych.
Pozwala jednak na odwoływanie się do „etykiety” przy każdym zapytaniu lub określaniu wartości domyślnych, w przeciwieństwie do najwyżej ocenianej odpowiedzi, w przypadku której należy użyć „wartości” (która może być liczbą).
Ustawienie choicesw tym polu pozwoli na pewną walidację po stronie Django, ale nie będzie definiować żadnej formy wyliczonego typu na końcu bazy danych.
Jak wspominali inni, rozwiązaniem jest określenie db_typew niestandardowym polu.
Jeśli używasz zaplecza SQL (np. MySQL), możesz to zrobić w następujący sposób:
from django.db import models
classEnumField(models.Field):def__init__(self, *args, **kwargs):
super(EnumField, self).__init__(*args, **kwargs)
assert self.choices, "Need choices for enumeration"defdb_type(self, connection):ifnot 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))
classIceCreamFlavor(EnumField, models.CharField):def__init__(self, *args, **kwargs):
flavors = [('chocolate', 'Chocolate'),
('vanilla', 'Vanilla'),
]
super(IceCreamFlavor, self).__init__(*args, choices=flavors, **kwargs)
classIceCream(models.Model):
price = models.DecimalField(max_digits=4, decimal_places=2)
flavor = IceCreamFlavor(max_length=20)
Uruchom syncdbi sprawdź tabelę, aby zobaczyć, czy ENUMplik został utworzony poprawnie.
mysql> SHOWCOLUMNSIN 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 | |
+--------+-----------------------------+------+-----+---------+----------------+
Bardzo pomocna odpowiedź! Ale to nie zadziała w przypadku PostgreSQL. Powodem jest to, że PostgreSQL ENUM nie obsługuje wartości domyślnych. W PostgreSQL najpierw musimy stworzyć CREATE DOMAIN lub CREATE TYPE. Por . 8.7. Wyliczone typy Wypróbowałem sztuczkę @ Davida i działa dobrze z MySQL, ale w PostgrSQL praca kończy się błędem 'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'.
Grijesh Chauhan
6
Jeśli naprawdę chcesz używać swoich baz danych typu ENUM:
Użyj Django 1.x
Rozpoznaj, że Twoja aplikacja będzie działać tylko na niektórych bazach danych.
Teraz pamiętaj, że nie wymusza wyborów na poziomie bazy danych, to jest konstrukcja tylko Pythona. Jeśli chcesz wymusić również te wartości w bazie danych, możesz to połączyć z ograniczeniami bazy danych:
Odpowiedzi:
Z dokumentacji Django :
I definiujesz charfielda w swoim modelu:
married = models.CharField(max_length=1, choices=MAYBECHOICE)
Możesz zrobić to samo z polami liczb całkowitych, jeśli nie chcesz, aby w Twojej bazie danych znajdowały się litery.
W takim przypadku przepisz swoje wybory:
MAYBECHOICE = ( (0, 'Yes'), (1, 'No'), (2, 'Unknown'), )
źródło
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) )
źródło
Użycie
choices
parametru nie spowoduje użycia typu ENUM db; po prostu utworzy VARCHAR lub INTEGER, w zależności od tego, czy używaszchoices
z CharField czy IntegerField. Ogólnie jest to w porządku. Jeśli jest dla Ciebie ważne, aby typ ENUM był używany na poziomie bazy danych, masz trzy opcje:W przypadku każdej z tych opcji do Twojej odpowiedzialności należałoby zajęcie się implikacjami dla przenoszenia między bazami danych. W opcji 2 możesz użyć niestandardowego SQL specyficznego dla zaplecza bazy danych, aby upewnić się, że ALTER TABLE działa tylko w MySQL. W opcji 3 metoda db_type musiałaby sprawdzić silnik bazy danych i ustawić typ kolumny db na typ, który faktycznie istnieje w tej bazie danych.
AKTUALIZACJA : Ponieważ ramy migracji zostały dodane w Django 1.7, powyższe opcje 1 i 2 są całkowicie przestarzałe. Opcja 3 i tak była zawsze najlepsza. Nowa wersja opcji 1/2 wymagałaby złożonej migracji niestandardowej przy użyciu
SeparateDatabaseAndState
- ale naprawdę potrzebujesz opcji 3.źródło
http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/
Jest to kolejny przyjemny i łatwy sposób implementacji wyliczeń, chociaż tak naprawdę nie zapisuje wyliczeń w bazie danych.
Pozwala jednak na odwoływanie się do „etykiety” przy każdym zapytaniu lub określaniu wartości domyślnych, w przeciwieństwie do najwyżej ocenianej odpowiedzi, w przypadku której należy użyć „wartości” (która może być liczbą).
źródło
Ustawienie
choices
w tym polu pozwoli na pewną walidację po stronie Django, ale nie będzie definiować żadnej formy wyliczonego typu na końcu bazy danych.Jak wspominali inni, rozwiązaniem jest określenie
db_type
w niestandardowym polu.Jeśli używasz zaplecza SQL (np. MySQL), możesz to zrobić w następujący sposób:
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)
Uruchom
syncdb
i sprawdź tabelę, aby zobaczyć, czyENUM
plik został utworzony poprawnie.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 | | +--------+-----------------------------+------+-----+---------+----------------+
źródło
'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'
.Jeśli naprawdę chcesz używać swoich baz danych typu ENUM:
Powodzenia!
źródło
Obecnie istnieją dwa projekty na githubie oparte na dodaniu tych, chociaż nie przyjrzałem się dokładnie, jak są one zaimplementowane:
zapewnia pole modelu wyliczenia Django (przy użyciu IntegerField) z wyliczeniami wielokrotnego użytku i walidacją przejścia.
Ten pakiet umożliwia używanie prawdziwych wyliczeń Pythona (w stylu PEP435) z Django.
Nie sądzę, aby używać typów wyliczeń DB, ale są one w przygotowaniu do pierwszego.
źródło
Django 3.0 ma wbudowaną obsługę Enum
Z dokumentacji :
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, )
Teraz pamiętaj, że nie wymusza wyborów na poziomie bazy danych, to jest konstrukcja tylko Pythona. Jeśli chcesz wymusić również te wartości w bazie danych, możesz to połączyć z ograniczeniami bazy danych:
class Student(models.Model): ... class Meta: constraints = [ CheckConstraint( check=Q(year_in_school__in=YearInSchool.values), name="valid_year_in_school") ]
źródło
U góry pliku models.py dodaj ten wiersz po przeprowadzeniu importu:
enum = lambda *l: [(s,_(s)) for s in l]
źródło