Chỉ định ENUM mySQL trong mô hình Django


Câu trả lời:


108

Từ tài liệu Django :

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

Và bạn xác định một trường biểu đồ trong mô hình của mình:

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

Bạn có thể làm tương tự với các trường số nguyên nếu bạn không muốn có các chữ cái trong db của mình.

Trong trường hợp đó, hãy viết lại các lựa chọn của bạn:

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

8
Điều này không ngăn các giá trị "sai" được lưu nếu không được làm sạch trước đó, phải không?
Strayer

@Strayer vâng, tôi đoán đây là chỉ có ích cho việc sử dụng các hình thức mô hình
cấp

Lưu ý rằng kiểu Django được đề xuất ngụ ý rằng các ký tự phải là hằng số: docs.djangoproject.com/en/dev/internals/contributing/…
Michael Scheper

11
Như @Carl Meyer đã nói trong câu trả lời của mình, điều này KHÔNG tạo cột ENUM trong cơ sở dữ liệu. Nó tạo ra một cột VARCHAR hoặc INTEGER, vì vậy nó không thực sự trả lời câu hỏi.
Ariel

Tôi có thể thêm tính năng lựa chọn với trường số nguyên không? @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
Kể từ django 1.2, bạn sẽ cần thêm tham số thứ hai, kết nối, vào db_type def.
Hans Lawrenz

2
Điều gì đã xảy ra với codecatelog sau đó? Lokos như nó có thể là một ý kiến ​​hay .... Bây giờ tôi nhận được 404 - ngay cả đối với trang gốc.
Danny Staple

33

Việc sử dụng choicestham số sẽ không sử dụng kiểu ENUM db; nó sẽ chỉ tạo một VARCHAR hoặc INTEGER, tùy thuộc vào việc bạn sử dụng choicesvới CharField hay IntegerField. Nói chung, điều này là tốt. Nếu điều quan trọng đối với bạn là loại ENUM được sử dụng ở cấp cơ sở dữ liệu, bạn có ba tùy chọn:

  1. Sử dụng "./manage.py sql appname" để xem SQL Django tạo, sửa đổi nó theo cách thủ công để sử dụng loại ENUM và tự chạy nó. Nếu bạn tạo bảng theo cách thủ công trước, "./manage.py syncdb" sẽ không gây rối với nó.
  2. Nếu bạn không muốn thực hiện việc này theo cách thủ công mỗi khi tạo DB của mình, hãy đặt một số SQL tùy chỉnh vào appname / sql / modelname.sql để thực hiện lệnh ALTER TABLE thích hợp.
  3. Tạo một loại trường tùy chỉnh và xác định phương thức db_type một cách thích hợp.

Với bất kỳ tùy chọn nào trong số này, bạn có trách nhiệm phải giải quyết các tác động đối với khả năng di chuyển cơ sở dữ liệu chéo. Trong tùy chọn 2, bạn có thể sử dụng SQL tùy chỉnh dành riêng cho cơ sở dữ liệu phụ trợ phần mềm để đảm bảo ALTER TABLE của bạn chỉ chạy trên MySQL. Trong tùy chọn 3, phương thức db_type của bạn sẽ cần phải kiểm tra công cụ cơ sở dữ liệu và đặt kiểu cột db thành kiểu thực sự tồn tại trong cơ sở dữ liệu đó.

CẬP NHẬT : Vì khung di chuyển đã được thêm vào Django 1.7, các tùy chọn 1 và 2 ở trên đã hoàn toàn lỗi thời. Lựa chọn 3 luôn là lựa chọn tốt nhất. Phiên bản mới của tùy chọn 1/2 sẽ liên quan đến việc di chuyển tùy chỉnh phức tạp bằng cách sử dụng SeparateDatabaseAndState- nhưng bạn thực sự muốn tùy chọn 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:

Đây là một cách khác hay và dễ thực hiện mặc dù nó không thực sự lưu các enum trong cơ sở dữ liệu.

Tuy nhiên, nó cho phép bạn tham chiếu đến 'nhãn' bất cứ khi nào truy vấn hoặc chỉ định giá trị mặc định trái ngược với câu trả lời được xếp hạng cao nhất, nơi bạn phải sử dụng 'giá trị' (có thể là một số).


9

Thiết lập choicestrên trường sẽ cho phép một số xác thực trên đầu Django, nhưng nó sẽ không xác định bất kỳ dạng nào của kiểu liệt kê trên đầu cơ sở dữ liệu.

Như những người khác đã đề cập, giải pháp là chỉ định db_typetrên một trường tùy chỉnh.

Nếu bạn đang sử dụng chương trình phụ trợ SQL (ví dụ: MySQL), bạn có thể làm như sau:

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)

Chạy syncdbvà kiểm tra bảng của bạn để xem bảng ENUMđã được tạo đúng cách chưa.

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

Câu trả lời rất hữu ích! Nhưng điều này sẽ không hoạt động đối với PostgreSQL. Lý do là PostgreSQL ENUM không hỗ trợ mặc định. Trong PostgreSQL, đầu tiên chúng ta phải tạo TẠO MIỀN hoặc TẠO LOẠI. Reff 8.7. Các kiểu liệt kê Tôi đã thử thủ thuật của @ David và Nó hoạt động tốt với MySQL nhưng trong PostgrSQL hoạt động cuối cùng với lỗi 'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'.
Grijesh Chauhan


3

Hiện tại có hai dự án github dựa trên việc thêm những thứ này, mặc dù tôi chưa xem xét chính xác cách chúng được triển khai:

  1. Django-EnumField :
    Cung cấp trường mô hình Django kiểu liệt kê (sử dụng IntegerField) với các enum có thể sử dụng lại và xác nhận chuyển tiếp.
  2. Django-EnumFields :
    Gói này cho phép bạn sử dụng các enums Python (kiểu PEP435) thực với Django.

Tôi không nghĩ sẽ sử dụng các kiểu enum DB, nhưng chúng đang hoạt động cho kiểu đầu tiên.


1

Django 3.0 có hỗ trợ tích hợp cho Enums

Từ tài liệu :

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

Bây giờ, hãy lưu ý rằng nó không thực thi các lựa chọn ở cấp cơ sở dữ liệu, đây là cấu trúc chỉ của Python. Nếu bạn cũng muốn thực thi các giá trị đó tại cơ sở dữ liệu, bạn có thể kết hợp giá trị đó với các ràng buộc cơ sở dữ liệu:

class Student(models.Model):
    ...

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

-2

Ở đầu tệp models.py của bạn, thêm dòng này sau khi bạn nhập:

    enum = lambda *l: [(s,_(s)) for s in l]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.