【问题标题】:Add data to Django form before it is saved?在保存之前将数据添加到 Django 表单?
【发布时间】:2017-12-26 12:40:03
【问题描述】:

在这个简化的用例中,我有一个包含 MyField_1 和 MyField_2 的 Django 表单。用户填写MyField_1。在将表单保存为模型之前,我希望计算机以编程方式添加MyField_2,这是从另一个数据源中提取的一些数据。我目前的视图如下所示:

class MyCreate(CreateView):
    model = MyModel
    fields = ['MyField_1']

如何在模型保存前将数据添加到MyField_2?

【问题讨论】:

    标签: django django-models django-forms django-views


    【解决方案1】:

    有多种方法可以做到这一点(例如覆盖ModelForm.save() 方法),但是我推荐的一种方法是覆盖ModelFormMixin.form_valid() 方法。所以像这样改变你的观点:

    class MyCreate(CreateView):
      model = MyModel
      fields = ['MyField_1']
    
      def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.MyField_2 = 43
        self.object.save()
        return super(ModelFormMixin, self).form_valid(form)
    

    两个建议:

    【讨论】:

    • 谢谢。看起来非常好。也感谢所有其他信息。 form.save(commit=False) 有什么作用?在这种情况下是否需要它?
    • 是的,同意,ccbv.co.uk 是一个很棒的基于类视图的网站。 @亚历克西斯
    • @serafeim 为什么是form.save(commit=False)呢?没有它会怎样?
    • 正如@PiyushMaurya 解释的那样,form.save(commit=False) 只会返回模型的一个实例而不保存到数据库中。如果您在没有 commit=False 的情况下运行 form.save() 对象将被保存到数据库中,并且如果未通过表单填写必填字段,您将收到异常。如果不运行 form.save(...) 您将无法引用表单应创建的对象。这就是为什么将 form_valid 传递给 form 变量以便您可以从那里获取对象实例的原因。
    • 完美。现在有道理了。谢谢!
    【解决方案2】:

    您可以这样做(参见基于类的视图的第二个示例):

    if request.method == 'POST':
        form = Form(request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.my_field_2 = #Put value here
            instance.save()
            return #Put return here
    else:
        form = Form()
    

    这样您可以初始化任何值以形成用户不输入的形式。

    在基于类的CreateView 中,您可以通过以下方式覆盖form_valid 方法:

    def form_valid(self, form):
        form.instance.field_2 = # Put value here
        return super(YourCreateView, self).form_valid(form)
    

    【讨论】:

    • 您好,请注意,您推荐的是普通旧功能 django 视图的答案,而不是经典视图。
    • @piyush save(commit=False) 是做什么的?需要吗?
    • @Serafeim 我也刚刚更新了基于类的视图的答案。
    • @Alexis save(commit=False) 不会在数据库中保存值,这意味着它当时不会将这些值提交到数据库,因此您可以向数据实例添加更多值,然后保存到数据库中稍后。
    • 您好@PiyushMaurya,您的回答是正确的,这是另一种方法。我只是把我从ModelMixin的form_valid提出的方法复制过来修改了一下。
    【解决方案3】:

    我会覆盖 ModelForm.save() 或模型 save()。您应该将您的逻辑尽可能地推回到模型上。如果您只打算在没有 MyField_2 的情况下创建此模型的实例,那么您应该覆盖模型 save()。这遵循了面向对象编程的封装实践,如果做得正确,这是一件非常漂亮的事情。

    class MyModelForm(forms.ModelForm):
        model = MyModel
        fields = ["field_1"]
        # no changes needed in form because the model itself will handle auto-population of field_2.
    
    class MyModel(models.Model):
        field_1 = models.SomeFieldType(...)
        field_2 = models.SomeFieldType(...)
    
        def save(self, *args, **kwargs):
            if self.id: # If true, this is being created. If false, this is just being edited and it already exists.
                # auto populate field_2
            else:
                # it's being edited and already exists so I'm guessing we just want this to do the default logic.
                super(MyModel, self).save(*args, **kwargs)
    

    如果你真的想要,你甚至可以通过模型保存方法 **kwargs 传递一个变量,它告诉逻辑它是由表单创建的。在这种情况下,您执行自动填充,但如果它不是由表单创建的,它可以选择不自动填充 field_2

    注意:我同意 @sarafeim 的说法,即您应该为字段使用带下划线的名称,而不是驼峰式。

    【讨论】:

    • 你好@Ian Kirkpatrick - 你应该覆盖什么取决于你的情况。我不同意覆盖模型的保存方法,因为这样,每当您保存该模型(从 django 管理员,从不同的视图)时,您将执行视图特定的代码。你并不总是想这样做。表格也是如此。模型表单可以与不同的视图重用,您可能希望每个视图具有不同的行为。当然,有时覆盖模型保存很重要,例如在保存实例时应始终执行操作。
    • 是的,我在回答的开头提到了这一点,但没有谈论为什么。我同意,有时候你可能想要不同的行为,在这种情况下,逻辑应该从模型上移得更高一点
    • 另外,我之前忘记覆盖 form_valid 视图方法的另一个原因是,您通常可以通过您的视图(例如,您可能想为 request.user 设置一个值)并且该信息不容易传递到模型的保存中。将它传递给您的表单相当容易(例如,通过将请求对象从视图传递到您的表单的 __init__ 方法),但它仍然需要一些工作——另一方面,这些参数在视图上只是等待要使用:)
    • 抱歉之前的评论有点太长了。我不得不说我写这一切是因为我被所有这些东西咬了!例如,我在两个不同的 django 视图中重新使用了 ModelForm(但忘记了它)。我更改了一个视图以将请求对象传递给表单并相应地更改表单以接收它。猜猜当我尝试调用另一个视图时发生了什么:) 不幸的是,这是动态语言的一个普遍问题(在 Java 中,如果我更改了构造函数,我会得到编译器异常),所以我试图防止此类错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-10
    • 2022-01-05
    • 1970-01-01
    • 2016-10-15
    • 1970-01-01
    • 2017-03-06
    • 2012-03-19
    相关资源
    最近更新 更多