【问题标题】:django ignore changed field in formsetdjango 忽略表单集中更改的字段
【发布时间】:2013-10-29 03:03:30
【问题描述】:

我有以下型号:

class Recipe(models.Model):
    fields...

class Ingredient(models.Model):
    fields...

class UsesIngredient(models.Model):
    recipe = models.ForeignKey(Recipe)
    ingredient = models.ForeignKey(Ingredient)
    amount = models.FloatField()
    group = models.CharField()

我有一个视图,它允许用户通过动态表单集为某个配方添加任意数量的“UsesIngredient”模型。组属性自动填充为对用户隐藏。

问题在于,当用户在表单集中添加新表单但未填写任何字段时,我不希望保存该表单。但是,django 仍然尝试保存表单,因为“组”属性已经“更改”(因为在创建额外表单时它已自动填写)。

有没有办法解决这个问题?

谢谢!

【问题讨论】:

    标签: python django django-forms inline-formset


    【解决方案1】:

    嗯,我仍然对 Tim Edgar 的解决方案不太满意,所以我一直在寻找。我想我找到了我要找的东西。 'Form' 类有两个在这种情况下有用的未记录方法:'has_changed()' 和 '_get_changed_data'。

    在 ModelFormSet 验证期间,每个表单都会检查“has_changed()”。如果表单未更改,则跳过验证并假定表单正确。 同样,在 ModelFormSet 保存期间,save_new_objects 检查每个表单以查看它是否已更改。如果未更改,则不会保存表单。

    所以我的解决方案是重写 has_changed() 方法以在仅“组”属性发生更改且所有其他字段为空的情况下返回 False。这是我的实现:

    class UsesIngredientForm(forms.ModelForm):    
        class Meta:
            model = UsesIngredient
    
        def has_changed(self, *args, **kwargs):
            self._get_changed_data(*args, **kwargs)
            # If group is in changed_data, but no other fields are filled in, remove group so
            # the form will not be validated or saved
            if 'group' in self._changed_data and len(self._changed_data) == 1:
                contains_data = False
                for name in ['ingredient', 'amount', 'unit']:
                    field = self.fields[name]
                    prefixed_name = self.add_prefix(name)
                    data_value = field.widget.value_from_datadict(self.data, self.files, prefixed_name)
                    if data_value:
                        contains_data = True
                        break
                if not contains_data:
                    self._changed_data.remove('group')
            return bool(self._changed_data)
    

    希望这对将来的任何人都有帮助!

    编辑: 我编辑了这个答案以反映 Tim Edgars 的评论。 我意识到这个实现仍然使用“私有”方法,但我还没有找到只使用公开记录的方法的更干净的实现。但也许这只是我自己的无能:)。

    【讨论】:

    • 覆盖私有/未记录的方法不是一个好习惯。通常在像 Python 这样没有适当类可见性的语言中,使用下划线 _private_method 表示私有方法。 Django 可以随时更改未记录方法的功能,因为它们会尝试维护公共方法的功能。
    • 好吧,也可以覆盖公开的 'has_changed()' 方法。这样做可以达到相同的结果,我同意这是更好的做法,所以我将编辑我的答案。
    【解决方案2】:

    您可以尝试通过设置blank=False 使您的所有字段都需要一个值。查看更多here。它应该要求验证您关心的值没有留空。

    如果这不起作用,您可以尝试创建自己的自定义 save 方法来执行您关心的验证。

    def save(self, *args, **kwargs):
       # Do your checks on the properties such as self.group, self.amount, etc
       # If it is fine then call
       super(UsesIngredient, self).save(*args, **kwargs)
    

    【讨论】:

    • 问题是,我想让用户看到他可以看到的所有字段都是空白的(如果他这样做了,我只想让 django 忽略该表单)...
    • 然后如回答中所述覆盖表单上的保存方法。如果属性不是您想要的,请不要调用 super().save。
    • 好的,我的问题并不清楚,但表单在调用 save() 方法之前得到验证。并且由于未填写必填字段,因此此验证失败。我只是觉得为这个功能同时覆盖 clean() 和 save() 方法是不对的......
    • 你希望它做一些不同于默认行为的事情。除了重组你如何做你的表单集之外,你会想要覆盖默认行为以匹配你想要的。创建与您所需功能相匹配的自定义保存或清除功能确实没有任何问题。
    • 好的,那我接受你的回答。我只是觉得 django 有一些内置选项可以将表单字段定义为可忽略的东西......无论如何,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 2013-04-06
    • 2022-12-17
    • 2019-12-31
    • 2018-11-07
    • 1970-01-01
    相关资源
    最近更新 更多