python - choices - django model choice
Especificando un mySQL ENUM en un modelo de Django (8)
A la parte superior de su archivo models.py, agregue esta línea después de realizar sus importaciones:
enum = lambda *l: [(s,_(s)) for s in l]
¿Cómo hago para especificar y usar un ENUM en un modelo de Django?
Actualmente hay dos proyectos Github basados en agregar estos, aunque no he investigado exactamente cómo se implementan:
- Django-EnumField :
Proporciona un campo de enumeración del modelo de Django (utilizando IntegerField) con enumeraciones reutilizables y validación de transición. - github.com/hzdg/django-enumfields :
Este paquete te permite usar enums reales de Python (estilo PEP435) con Django.
No creo que tampoco use los tipos de enum DB, pero están en proceso para el primero.
De la documentación de Django :
MAYBECHOICE = (
(''y'', ''Yes''),
(''n'', ''No''),
(''u'', ''Unknown''),
)
Y define un charfield en su modelo:
married = models.CharField(max_length=1, choices=MAYBECHOICE)
Puede hacer lo mismo con los campos enteros si no desea tener letras en su db.
En ese caso, vuelva a escribir sus elecciones:
MAYBECHOICE = (
(0, ''Yes''),
(1, ''No''),
(2, ''Unknown''),
)
El uso del parámetro de choices
no usará el tipo de dúo ENUM; solo creará un VARCHAR o INTEGER, dependiendo de si usa choices
con un CharField o IntegerField. En general, esto está bien. Si es importante para usted que el tipo de ENUM se utilice a nivel de base de datos, tiene tres opciones:
- Use "./manage.py sql appname" para ver el SQL generado por Django, modifíquelo manualmente para usar el tipo ENUM y ejecútelo usted mismo. Si primero crea la tabla manualmente, "./manage.py syncdb" no se meterá con ella.
- Si no desea hacer esto manualmente cada vez que genera su base de datos, coloque algunos SQL personalizados en el nombre de la aplicación / sql / modelname.sql para realizar el comando ALTER TABLE apropiado.
- Cree un tipo de campo personalizado y defina el método db_type de forma adecuada.
Con cualquiera de estas opciones, sería su responsabilidad lidiar con las implicaciones para la portabilidad entre bases de datos. En la opción 2, puede usar el SQL personalizado específico del back-end de la base de datos para asegurarse de que ALTER TABLE solo se ejecute en MySQL. En la opción 3, su método db_type debería verificar el motor de base de datos y establecer el tipo de columna db en un tipo que realmente exista en esa base de datos.
ACTUALIZACIÓN : desde que se agregó el marco de migraciones en Django 1.7, las opciones 1 y 2 anteriores están completamente obsoletas. La opción 3 siempre fue la mejor opción de todos modos. La nueva versión de las opciones 1/2 implicaría una migración personalizada compleja utilizando SeparateDatabaseAndState
, pero realmente desea la opción 3.
Establecer choices
en el campo permitirá cierta validación en el extremo de Django, pero no definirá ninguna forma de tipo enumerado en el extremo de la base de datos.
Como otros han mencionado, la solución es especificar db_type
en un campo personalizado.
Si está utilizando un backend SQL (por ejemplo, MySQL), puede hacerlo de la siguiente manera:
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)
Ejecute syncdb
e inspeccione su tabla para ver que ENUM
se haya creado correctamente.
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 | |
+--------+-----------------------------+------+-----+---------+----------------+
Si realmente quiere usar su tipo de bases de datos ENUM:
- Use Django 1.x
- Reconozca que su aplicación solo funcionará en algunas bases de datos.
- Rompecabezas a través de esta página de documentación: http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields
¡Buena suerte!
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:
Esta es otra forma sencilla y agradable de implementar enumeraciones, aunque en realidad no guarda enumeraciones en la base de datos.
Sin embargo, sí le permite hacer referencia a la ''etiqueta'' siempre que consulte o especifique los valores predeterminados en lugar de la respuesta mejor calificada donde debe usar el ''valor'' (que puede ser un número).
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) )