【问题标题】:Restricting ForeignKey choices on sub-classes限制子类的 ForeignKey 选择
【发布时间】:2015-04-26 18:52:26
【问题描述】:

我有一组模型,关于餐馆和经营它们的厨师*

class Chef(models.Model):
    name = models.TextField()

class Restaurant(models.Model):
    name = models.TextField()
    chef = models.ForeignKey(Chef)

class FrenchChef(Chef):
    angryness = models.PositiveIntegerField()

class FrenchRestaurant(Restaurant):
    region = models.TextField()

不幸的是,当前的模型意味着非FrenchChef 可以运行FrenchRestaurant

我是否可以将子类模型的 ForeignKey 的查询集限制为父类上可用查询集的子集?

* 我的模特实际上不是厨师和餐馆,但这更容易解释。这可能看起来并不明显,但Chefs 和FrenchChefs 确实需要进行不同的建模。

【问题讨论】:

  • 这不是关于如何组织模型的问题,不是特别是在 django 中,而是在任何数据库中。或者您是在询问如何验证数据.. 您是否查看过 save() 函数或清理数据或 django 验证方法??

标签: python django django-models


【解决方案1】:

如果您对此感到担忧,可以尝试定义干净的方法

class FrenchRestaurant(models.Model):
    # ...

    def clean(self):
        if not isinstance(self.chief, FrenchChief):
            raise ValidationError()

【讨论】:

    【解决方案2】:

    通过这样做:

    class FrenchChef(Chef):
        angryness = models.PositiveIntegerField()
    

    除了Chef,您还在数据库中创建一个表。在此处阅读有关模型继承类型的信息:https://docs.djangoproject.com/en/1.7/topics/db/models/#model-inheritance

    我认为你应该为厨师创建一张桌子,为餐馆创建一张桌子,这里不需要继承:

    class Chef(models.Model):
        name = models.TextField()
        # all chefs with not null angryness is frenchchefs...
        # but you can add some field to explicitly save chef type
        angryness = models.PositiveIntegerField(null=True)
    
    class Restaurant(models.Model):
        name = models.TextField()
        chef = models.ForeignKey(Chef)
        region = models.TextField()
        # rtype added here but it is not necessarily
        rtype = models.IntegerField(choices=restaurans_types)
    

    并且选择的限制(过滤)应该是形式:

    class FrenchRestaurantForm(forms.ModelForm):
        def __init__(self, *args,**kwargs):
            super (FrenchRestaurantForm, self ).__init__(*args,**kwargs)
            self.fields['chef'].queryset = Chef.objects.filter(
                angryness__gte=MIN_ANGRYNESS_LVL)
        def save(commit=True):
            model = super(FrenchRestaurantForm, self).save(commit=False)
            model.rtype = SomeFrenchRestTypeConst
            if commit:
                model.save()
            return model
        class Meta:
            model = Restaurant
    

    要检查用户输入,您可以将 clean 方法添加到表单字段 https://docs.djangoproject.com/en/1.7/ref/forms/validation/#cleaning-a-specific-field-attribute

    如果FrenchChef 是故意创建的(它是数据库中的另一个表),那么您应该将它添加到FrenchRestaurant(另一个表-> 另一个fk id):

    class FrenchRestaurant(Restaurant):
        region = models.TextField()
        frenchchef = models.ForeignKey(FrenchChef)
    

    【讨论】:

    • 不幸的是,这不适合我的情况。 FrenchChefs 的处理方式不同,并被显式建模为不同的对象。我在问题中添加了注释。
    【解决方案3】:

    就像我在评论中提到的,您可以查看 django 模型数据验证方法。刚刚在这篇文章中发现了另一个注释。 Adding Custom Django Model Validation

    以下是进行验证的常见模式。代码 sn-p 是从上述帖子中的一个答案中提取的。回复https://stackoverflow.com/users/247542/cerin

    class BaseModel(models.Model):
    
        def clean(self, *args, **kwargs):
            # add custom validation here
            super(BaseModel, self).clean(*args, **kwargs)
    
        def save(self, *args, **kwargs):
            self.full_clean()
            super(BaseModel, self).save(*args, **kwargs)

    您可以继续阅读 django 文档中有关验证的更多信息。

    如果您正在寻找任何可能存在的基于类型/继承的解决方案。我不确定它们是否存在。我仍然想看看是否有人在 django 中提出了这样的规定。

    【讨论】:

      猜你喜欢
      • 2014-06-30
      • 2022-01-20
      • 2018-04-21
      • 2012-10-16
      • 2017-04-19
      • 2012-02-05
      • 1970-01-01
      • 2013-12-27
      • 2021-07-13
      相关资源
      最近更新 更多