Django限制可提供给模型字段的数据

dot*_*dot 2 python django django-models

我在Django中有以下模型:

class Cast(TimeStampedModel):
    user = models.ForeignKey(User, unique=True)
    count = models.PositiveIntegerField(default=1)
    kind = models.CharField(max_length = 7)

    def __str__(self):
        return(f"{self.kind} || {self.count} || {self.modified.strftime('%x')}")
Run Code Online (Sandbox Code Playgroud)

但我希望“种类”字段仅采用以下值之一:上,下,奇怪,魅力,顶部或底部。如何在数据库中强制执行此操作,或者仅在导入数据时才能在视图中强制执行此操作?

Bor*_*jaX 5

我认为选择应该做什么?

class Cast(TimeStampedModel):
    user = models.ForeignKey(User, unique=True)
    count = models.PositiveIntegerField(default=1)
    kind = models.CharField(
        max_length=7,
        choices=(
            ("up", "Up"),
            ("down", "Down"),
            ("strange", "Strange"),
            ("charm", "Charm"),
            ("top", "Top"),
            ("bottom", "Bottom")
        )
    )
Run Code Online (Sandbox Code Playgroud)

尽管在很多情况下我已经看到它用作SmallInteger来节省数据库空间:在DB中,您存储一个数字,在Admin区域中,您会看到一个下拉菜单,其中包含“人类友好”选项。

kind = models.PositiveSmallIntegerField(
    choices=(
        (1, "Up"),
        (2, "Down"),
        (3, "Strange"),
        (4, "Charm"),
        (5, "Top"),
        (6, "Bottom")
    )
)
Run Code Online (Sandbox Code Playgroud)

看到:

行动中的选择

不是在数据库级别强制执行的(请参阅此故障单和此SO问题),这意味着您仍然可以执行以下操作:

>>> c = Cast.objects.first()
>>> c.kind = 70
>>> c.save()
Run Code Online (Sandbox Code Playgroud)

但它是在管理员中强制执行的。如果您需要在较低级别实施它,建议您使用Noah Lc的答案。据我了解,这不是100%强制执行的:您仍然可以执行不通过.save()模型方法进行的批量更新,这意味着这样做Cast.objects.all().update(kind=70)仍然会70kind字段中设置无效值(),但是他的解决方案是,的确比选择“低”了一步。您将无法进行通过.save()实例方法进行的模型更新。意思是,您将不允许这样做:

>>> c=Cast.objects.first()
>>> c.kind=70
>>> c.save()
Run Code Online (Sandbox Code Playgroud)

如果确实需要实施REAL数据库,则需要实际检查数据库的可能性并在cast.kind列上添加约束。

例如,对于Postgres(可能还有其他大多数SQL风格),您可以创建一个执行此操作的新迁移:

from django.db import migrations


def add_kind_constraint(apps, schema_editor):
    table = apps.get_model('stackoverflow', 'Cast')._meta.db_table
    schema_editor.execute("ALTER TABLE %s ADD CONSTRAINT check_cast_kind"
                          " CHECK (kind IN (1, 2, 3, 4, 5, 6) )" % table)


def remove_kind_constraint(apps, schema_editor):
    table = apps.get_model('stackoverflow', 'Cast')._meta.db_table
    schema_editor.execute("ALTER TABLE %s DROP CONSTRAINT check_cast_kind" % table)


class Migration(migrations.Migration):

    dependencies = [
        ('stackoverflow', '0003_auto_20171231_0526'),
    ]

    operations = [
        migrations.RunPython(add_kind_constraint, reverse_code=remove_kind_constraint)
    ]
Run Code Online (Sandbox Code Playgroud)

然后,是的...您将获得100%的安全保护(检查不取决于Django:现在已由您的数据库引擎掌握):

>>> c = Cast.objects.all().update(kind=70)
django.db.utils.IntegrityError: new row for relation "stackoverflow_cast" violates check constraint "check_cast_kind"
DETAIL:  Failing row contains (2, 1, 70, 1).
Run Code Online (Sandbox Code Playgroud)