การระบุ mySQL ENUM ในโมเดล Django


93

ฉันจะระบุและใช้ ENUM ในโมเดล Django ได้อย่างไร


4
สตีฟถ้าคุณหมายถึงการใช้ประเภท MySQL ENUM แสดงว่าคุณโชคไม่ดีเท่าที่ฉันรู้ว่า Django ไม่ได้ให้การสนับสนุนสำหรับสิ่งนั้น (ฟีเจอร์นั้นไม่มีในฐานข้อมูลทั้งหมดที่ Django รองรับ) คำตอบที่ได้รับจาก Paul ใช้งานได้ แต่จะไม่กำหนดประเภทในฐานข้อมูล
dguaraglia

คำตอบ:


109

จากเอกสาร Django :

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

และคุณกำหนดชาร์ฟิลด์ในโมเดลของคุณ:

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

คุณสามารถทำได้เช่นเดียวกันกับฟิลด์จำนวนเต็มหากคุณไม่ต้องการให้มีตัวอักษรในฐานข้อมูลของคุณ

ในกรณีนั้นให้เขียนตัวเลือกของคุณใหม่:

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

8
สิ่งนี้ไม่ได้ป้องกันไม่ให้บันทึกค่า "เท็จ" หากไม่ได้รับการทำความสะอาดก่อนใช่หรือไม่?
Strayer

@Strayer ใช่ฉันเดาว่านี่มีประโยชน์สำหรับการใช้แบบจำลองเท่านั้น
เฉียบพลัน

โปรดทราบว่าสไตล์ Django ที่แนะนำมีนัยว่าอักขระควรเป็นค่าคงที่: docs.djangoproject.com/en/dev/internals/contributing/…
Michael Scheper

14
ดังที่ @Carl Meyer กล่าวในคำตอบของเขาสิ่งนี้ไม่ได้สร้างคอลัมน์ ENUM ในฐานข้อมูล มันสร้างคอลัมน์ VARCHAR หรือ INTEGER ดังนั้นจึงไม่ได้ตอบคำถามจริงๆ
Ariel

ฉันสามารถเพิ่มคุณสมบัติตัวเลือกด้วยฟิลด์จำนวนเต็มได้หรือไม่ @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 คุณจะต้องเพิ่มพารามิเตอร์ที่สองการเชื่อมต่อไปยัง db_type def
Hans Lawrenz

2
เกิดอะไรขึ้นกับ codecatelog แล้ว? Lokos เหมือนจะเป็นความคิดที่ดี .... ตอนนี้ฉันได้รับ 404 - แม้แต่สำหรับหน้ารูท
Danny Staple

33

การใช้choicesพารามิเตอร์จะไม่ใช้ประเภท ENUM db มันจะสร้าง VARCHAR หรือ INTEGER ขึ้นอยู่กับว่าคุณใช้choicesกับ CharField หรือ IntegerField โดยทั่วไปก็ใช้ได้ดี หากสิ่งสำคัญสำหรับคุณที่ต้องใช้ประเภท ENUM ในระดับฐานข้อมูลคุณมีสามตัวเลือก:

  1. ใช้ "./manage.py sql appname" เพื่อดู SQL Django ที่สร้างขึ้นแก้ไขด้วยตนเองเพื่อใช้ประเภท ENUM และเรียกใช้ด้วยตัวเอง หากคุณสร้างตารางด้วยตนเองก่อน "./manage.py syncdb" จะไม่ยุ่งกับตาราง
  2. หากคุณไม่ต้องการทำสิ่งนี้ด้วยตนเองทุกครั้งที่สร้างฐานข้อมูลให้ใส่ SQL ที่กำหนดเองบางส่วนใน appname / sql / modelname.sql เพื่อดำเนินการคำสั่ง ALTER TABLE
  3. สร้างประเภทฟิลด์ที่กำหนดเองและกำหนดวิธีการ db_type อย่างเหมาะสม

ด้วยตัวเลือกใด ๆ เหล่านี้คุณจะต้องรับผิดชอบในการจัดการกับผลกระทบของการพกพาข้ามฐานข้อมูล ในตัวเลือกที่ 2 คุณสามารถใช้SQL ที่กำหนดเองเฉพาะฐานข้อมูลแบ็กเอนด์เพื่อให้แน่ใจว่า ALTER TABLE ของคุณทำงานบน MySQL เท่านั้น ในตัวเลือกที่ 3 เมธอด db_type ของคุณจะต้องตรวจสอบเอ็นจินฐานข้อมูลและตั้งค่าประเภทคอลัมน์ db เป็นประเภทที่มีอยู่จริงในฐานข้อมูลนั้น

อัปเดต : เนื่องจากเฟรมเวิร์กการย้ายข้อมูลถูกเพิ่มใน Django 1.7 ตัวเลือก 1 และ 2 ข้างต้นจึงล้าสมัยทั้งหมด ตัวเลือกที่ 3 เป็นตัวเลือกที่ดีที่สุดเสมอ เวอร์ชันใหม่ของตัวเลือก 1/2 จะเกี่ยวข้องกับการโยกย้ายแบบกำหนดเองที่ซับซ้อนโดยใช้SeparateDatabaseAndState- แต่คุณต้องการตัวเลือกที่ 3 จริงๆ


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:

นี่เป็นอีกวิธีที่ดีและง่ายในการใช้ enums แม้ว่าจะไม่ได้บันทึก enums ในฐานข้อมูลจริงๆ

อย่างไรก็ตามจะอนุญาตให้คุณอ้างอิง 'ป้ายกำกับ' เมื่อใดก็ตามที่สอบถามหรือระบุค่าเริ่มต้นซึ่งตรงข้ามกับคำตอบที่ได้รับคะแนนสูงสุดซึ่งคุณต้องใช้ 'ค่า' (ซึ่งอาจเป็นตัวเลข)


9

การตั้งค่าchoicesบนฟิลด์จะอนุญาตให้มีการตรวจสอบความถูกต้องบางอย่างในส่วนท้ายของ Django แต่จะไม่กำหนดรูปแบบของประเภทการแจกแจงใด ๆ ที่ส่วนท้ายของฐานข้อมูล

ดังที่คนอื่น ๆ กล่าวถึงวิธีแก้ปัญหาคือการระบุdb_typeในฟิลด์ที่กำหนดเอง

หากคุณใช้แบ็กเอนด์ SQL (เช่น MySQL) คุณสามารถทำได้ดังนี้:

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)

เรียกใช้syncdbและตรวจสอบตารางของคุณเพื่อดูว่าENUMสร้างขึ้นอย่างถูกต้อง

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

คำตอบที่เป็นประโยชน์มาก! แต่จะใช้ไม่ได้กับ PostgreSQL เหตุผลคือ PostgreSQL ENUM ไม่รองรับค่าเริ่มต้น ใน PostgreSQL ก่อนอื่นเราต้องสร้าง CREATE DOMAIN หรือ CREATE TYPE Reff 8.7. ระบุชนิดฉันพยายาม @ เคล็ดลับของเดวิดและมันทำงานได้ดีกับ MySQL แต่ใน PostgrSQL 'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'ทำงานสิ้นสุดขึ้นกับข้อผิดพลาด
Grijesh Chauhan

6

หากคุณต้องการใช้ฐานข้อมูลของคุณประเภท ENUM:

  1. ใช้ Django 1.x
  2. การจดจำแอปพลิเคชันของคุณจะใช้ได้กับฐานข้อมูลบางส่วนเท่านั้น
  3. ปริศนาผ่านหน้าเอกสารนี้: http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

โชคดี!


3

ขณะนี้มีโปรเจ็กต์ github สองโปรเจ็กต์จากการเพิ่มสิ่งเหล่านี้แม้ว่าฉันจะไม่ได้ตรวจสอบวิธีการนำไปใช้อย่างชัดเจน

  1. Django-EnumField :
    จัดเตรียมฟิลด์แบบจำลองการแจงนับ Django (โดยใช้ IntegerField) พร้อม enums ที่ใช้ซ้ำได้และการตรวจสอบการเปลี่ยนแปลง
  2. Django-EnumFields :
    แพ็คเกจนี้ให้คุณใช้ enums Python (สไตล์ PEP435) จริงกับ Django

ฉันไม่คิดว่าจะใช้ DB enum types แต่อยู่ในผลงานสำหรับคนแรก


1

Django 3.0 มีการรองรับ Enums ในตัว

จากเอกสารประกอบ :

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

ตอนนี้โปรดทราบว่ามันไม่ได้บังคับใช้ตัวเลือกในระดับฐานข้อมูลนี่คือการสร้าง Python เท่านั้น หากคุณต้องการบังคับใช้ค่าเหล่านั้นกับฐานข้อมูลคุณสามารถรวมเข้ากับข้อ จำกัด ของฐานข้อมูล:

class Student(models.Model):
    ...

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

-2

ด้านบนของไฟล์ models.py ของคุณเพิ่มบรรทัดนี้หลังจากที่คุณนำเข้า:

    enum = lambda *l: [(s,_(s)) for s in l]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.