Django modelinde bir mySQL ENUM belirtme


93

Bir Django modelinde bir ENUM belirleme ve kullanma konusuna nasıl gidebilirim?


4
Steve, MySQL ENUM türünü kullanmayı kastettiyseniz, Django'nun bunun için destek sağlamadığını bildiğim kadarıyla şansınız kalmadı (bu özellik Django tarafından desteklenen tüm DB'lerde mevcut değildir). Paul tarafından sağlanan yanıt çalışır, ancak olmayacak DB türünü tanımlar.
dguaraglia

Yanıtlar:


109

Gönderen Django belgeleri :

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

Ve modelinizde bir karakter alanı tanımlarsınız:

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

Eğer db'nizde harflerin olmasını istemiyorsanız tamsayı alanları için aynısını yapabilirsiniz.

Bu durumda seçimlerinizi yeniden yazın:

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

8
Bu, daha önce temizlenmemişse "yanlış" değerlerin kaydedilmesini engellemez, değil mi?
Strayer

@Strayer evet, sanırım bu yalnızca model formları kullanmak için yararlıdır
Acute

Önerilen Django stilinin, karakterlerin sabit olması gerektiğini ima ettiğini unutmayın: docs.djangoproject.com/en/dev/internals/contributing/…
Michael Scheper

13
@Carl Meyer'in cevabında söylediği gibi, bu, veritabanında bir ENUM sütunu OLUŞTURMAZ. Bir VARCHAR veya INTEGER sütunu oluşturur, bu yüzden soruyu gerçekten cevaplamaz.
Ariel

Tamsayı alanıyla seçenekler özelliği ekleyebilir miyim? @fulmicoton
Ilyas karim

36
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) )

2
Django 1.2'den itibaren, db_type tanımına ikinci bir parametre, bağlantı eklemeniz gerekecektir.
Hans Lawrenz

2
Codecatelog'a ne oldu peki? Lokolar bunun gibi iyi bir fikir olabilirdi .... Şimdi bir 404 alıyorum - kök sayfa için bile.
Danny Staple

33

Kullanımı choicesENUM db türünü kullanmaz parametreyi; choicesCharField veya IntegerField ile kullanmanıza bağlı olarak sadece bir VARCHAR veya INTEGER yaratacaktır. Genelde bu gayet iyi. ENUM türünün veritabanı düzeyinde kullanılması sizin için önemliyse, üç seçeneğiniz vardır:

  1. Django'nun oluşturduğu SQL'i görmek için "./manage.py sql appname" kullanın, ENUM türünü kullanmak için manuel olarak değiştirin ve kendiniz çalıştırın. Tabloyu önce manuel olarak oluşturursanız, "./manage.py syncdb" tabloyu karıştırmaz.
  2. DB'nizi her oluşturduğunuzda bunu manuel olarak yapmak istemiyorsanız, uygun ALTER TABLE komutunu gerçekleştirmek için appname / sql / modelname.sql içine bazı özel SQL koyun.
  3. Bir oluşturma özel alan tipi ve uygun db_type yöntemi tanımlar.

Bu seçeneklerden herhangi biriyle, veritabanları arası taşınabilirliğin çıkarımlarıyla başa çıkmak sizin sorumluluğunuz olacaktır. 2. seçenekte, ALTER TABLE'ınızın yalnızca MySQL üzerinde çalıştığından emin olmak için veritabanına özel özel SQL kullanabilirsiniz. 3. seçenekte, db_type yönteminizin veritabanı motorunu kontrol etmesi ve db sütun türünü o veritabanında gerçekte var olan bir türe ayarlaması gerekir.

GÜNCELLEME : Taşıma çerçevesi Django 1.7'de eklendiğinden, yukarıdaki 1 ve 2 numaralı seçenekler tamamen geçersizdir. 3. Seçenek zaten her zaman en iyi seçenekti. Seçenek 1 / 2'nin yeni sürümü, karmaşık bir özel geçişi içerecektir SeparateDatabaseAndState- ancak gerçekten 3. seçeneği istiyorsunuz.


10

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:

Bu, numaralandırmaları gerçekten veritabanına kaydetmese de, numaralandırmanın başka bir güzel ve kolay yoludur.

Bununla birlikte, 'değeri' (bir sayı olabilir) kullanmanız gereken en yüksek puanlı yanıtın aksine, varsayılanları sorguladığınızda veya belirtirken 'etikete' başvurmanıza izin verir.


9

Ayar choicessahada Django ucunda bazı doğrulama sağlayacak, ancak olmayacak veritabanı ucunda bir sabit tür herhangi bir biçimde tanımlar.

Diğerlerinin de belirttiği gibi çözüm, db_typeözel bir alan üzerinde belirtmektir .

Bir SQL arka ucu kullanıyorsanız (örneğin MySQL), bunu şu şekilde yapabilirsiniz:

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)

Çalıştırın syncdbve tablonuzun ENUMdoğru şekilde oluşturulup oluşturulmadığını kontrol edin .

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    |                |
+--------+-----------------------------+------+-----+---------+----------------+

Çok Yararlı Cevap! Ancak bu PostgreSQL için çalışmayacaktır. Nedeni PostgreSQL ENUM'un varsayılanı desteklememesidir. PostgreSQL'de önce CREATE DOMAIN veya CREATE TYPE oluşturmalıyız. Reff 8.7. Numaralandırılmış Tipler @ David'in numarasını denedim ve MySQL ile iyi çalışıyor, ancak PostgrSQL'de bir hata ile sonuçlanıyor 'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'.
Grijesh Chauhan


3

Şu anda bunları eklemeye dayanan iki github projesi var, ancak tam olarak nasıl uygulandıklarına bakmadım:

  1. Django-EnumField :
    Yeniden kullanılabilir numaralandırmalar ve geçiş doğrulaması ile bir numaralandırma Django model alanı (IntegerField kullanarak) sağlar.
  2. Django-EnumFields :
    Bu paket, Django ile gerçek Python (PEP435 tarzı) numaralandırmaları kullanmanızı sağlar.

Ben de DB numaralandırma türlerini kullanmayı düşünmüyorum, ancak ilk biri için çalışıyorlar .


1

Django 3.0, Enums için yerleşik desteğe sahiptir

Gönderen belgeler :

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

Şimdi, bir veritabanı düzeyinde seçimleri zorlamadığını unutmayın, bu yalnızca Python yapısıdır. Veritabanında bu değeri de uygulamak istiyorsanız, bunu veritabanı kısıtlamalarıyla birleştirebilirsiniz:

class Student(models.Model):
    ...

    class Meta:
        constraints = [
            CheckConstraint(
                check=Q(year_in_school__in=YearInSchool.values),
                name="valid_year_in_school")
        ]

-2

Models.py dosyanızın en üstüne, içe aktarmalarınızı yaptıktan sonra bu satırı ekleyin:

    enum = lambda *l: [(s,_(s)) for s in l]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.