我觉得choices应该怎么做?
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")
)
)
虽然在很多情况下我都看到它用作 SmallInteger 来节省数据库中的空间:在数据库中存储一个数字,在管理区域中,您会看到一个带有“人性化”选项的下拉列表.
kind = models.PositiveSmallIntegerField(
choices=(
(1, "Up"),
(2, "Down"),
(3, "Strange"),
(4, "Charm"),
(5, "Top"),
(6, "Bottom")
)
)
见:
这未在数据库级别强制执行(请参阅this ticket 和此SO question),这意味着您仍然可以这样做:
>>> c = Cast.objects.first()
>>> c.kind = 70
>>> c.save()
但它在管理员中强制执行。如果您需要在较低级别强制执行,我建议您使用Noah Lc's answer。
据我了解,这(仍然)不是 100% 强制执行的:您仍然可以进行不通过模型的 .save() 方法的批量更新;含义:执行Cast.objects.all().update(kind=70) 仍会在kind 字段中设置无效值(70),但他的解决方案确实比管理员选择“低”了一步。您将无法通过实例的 .save() 方法进行模型更新。意思是,你不能这样做:
>>> c=Cast.objects.first()
>>> c.kind=70
>>> c.save()
如果您确实需要真正的数据库实施,则需要实际检查数据库的可能性并在 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)
]
然后是的...您将获得 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).