【问题标题】:Django restrict data that can be given to model fieldDjango 限制可以提供给模型字段的数据
【发布时间】:2017-12-31 04:32:28
【问题描述】:

我在 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')}")

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

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    我觉得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).
    

    【讨论】:

      【解决方案2】:

      在模型的 save 方法中执行:

      def save(self, *args, **kwargs):
          mylist = ['up', 'down', 'strange', 'charm',....]
          if self.kind in mylist:
              super(Foo, self).save(*args, **kwargs)
          else:
             raise Exception, "kind take only one of the following values: up, down, strange, charm,...." 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-23
        • 2011-03-20
        • 2021-05-21
        • 2013-09-11
        • 2020-02-20
        • 2016-08-11
        • 2010-10-25
        相关资源
        最近更新 更多