Django ModelForms - “实例”没有按预期工作

发布于 2024-09-26 08:22:49 字数 1657 浏览 6 评论 0原文

我有一个模型表单,可以创建一个新模型或编辑现有模型 - 这很简单并且应该可以工作,但由于某种原因我每次都会得到一个新实例。

场景是这是电子商务订单的第一步。用户必须填写一些描述订单的信息(存储在模型中)。我创建模型,保存它,然后重定向到下一个视图,以便用户输入其抄送信息。我将模型保留在会话中,这样我就不必在下一个视图中进行数据库查找。模板中有第二个(抄送信息)视图的链接,可让用户返回第一个视图来编辑订单。

# forms.py

class MyForm(forms.ModelForm):
    class Meta:
        fields = ('field1', 'field2')
        model = MyModel

# views.py

def create_or_update(request):
    if request.method == 'POST':
        form = MyForm(request.POST)
        if form.is_valid():
            m = form.save(commit=False)
            # update some other fields that aren't in the form
            m.field3 = 'blah'
            m.field4 = 'blah'
            m.save()
            request.session['m'] = m
            return HttpResponseRedirect(reverse('enter_cc_info'))
        # invalid form, render template
        ...
    else:
        # check to see if we're coming back to edit an existing model
        # this part works, I get an instance as expected
        m = request.session.get('m', None)
        if m:
            instance = get_object_or_None(MyModel, id=m.id)
            if instance:
                form = MyForm(instance=instance)
            else:
                # can't find it in the DB, but it's in the session
                form = MyForm({'field1': m.field1, 'field2': m.field2})
        else:
            form = MyForm()

    # render the form
    ...

如果我在返回视图编辑订单时在调试器中单步执行,则将按照预期将实例设置为先前创建的模型来创建表单。但是,当在后续 POST 中处理表单时,它会在调用 form.save() 时创建模型的新实例。

我相信这是因为我限制了表单中的字段,因此渲染的 HTML 中没有任何地方可以存储现有模型的 id(或其他引用)。但是,我尝试添加“pk”和“id”字段(不同时),但我的表单根本不呈现。

我怀疑我让事情变得比需要的更复杂,但我现在陷入困境,需要一些反馈。提前致谢。

I have a modelform that will either create a new model or edit an existing one - this is simple and should work, but for some reason I'm getting a new instance every time.

The scenario is this is the first step in an ecommerce order. The user must fill out some info describing the order (which is stored in the model). I create the model, save it, then redirect to the next view for the user to enter their cc info. I stick the model in the session so I don't have to do a DB lookup in the next view. There is a link in the template for the second (cc info) view that lets the user go back to the first view to edit their order.

# forms.py

class MyForm(forms.ModelForm):
    class Meta:
        fields = ('field1', 'field2')
        model = MyModel

# views.py

def create_or_update(request):
    if request.method == 'POST':
        form = MyForm(request.POST)
        if form.is_valid():
            m = form.save(commit=False)
            # update some other fields that aren't in the form
            m.field3 = 'blah'
            m.field4 = 'blah'
            m.save()
            request.session['m'] = m
            return HttpResponseRedirect(reverse('enter_cc_info'))
        # invalid form, render template
        ...
    else:
        # check to see if we're coming back to edit an existing model
        # this part works, I get an instance as expected
        m = request.session.get('m', None)
        if m:
            instance = get_object_or_None(MyModel, id=m.id)
            if instance:
                form = MyForm(instance=instance)
            else:
                # can't find it in the DB, but it's in the session
                form = MyForm({'field1': m.field1, 'field2': m.field2})
        else:
            form = MyForm()

    # render the form
    ...

If I step through in the debugger when I go back to the view to edit an order that the form is created with the instance set to the previously created model, as expected. However, when the form is processed in the subsequent POST, it creates a new instance of the model when form.save() is called.

I believe this is because I've restricted the fields in the form, so there is nowhere in the rendered HTML to store the id (or other reference) to the existing model. However, I tried adding both a 'pk' and an 'id' field (not at the same time), but then my form doesn't render at all.

I suspect I'm making this more complicated than it needs to be, but I'm stuck at the moment and could use some feedback. Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅唱ヾ落雨殇 2024-10-03 08:22:49

这很有趣。这是我的尝试。考虑这一行:

form = MyForm(request.POST)

你能检查 request.POST 的内容吗?具体来说,检查是否存在有关正在编辑模型的实例的任何信息。你会发现没有。换句话说,每次在 POST 上保存表单时,都会创建一个新实例。

为什么会发生这种情况?当您创建传递 instance=instance 关键字参数的表单时,您是在告诉 Form 类返回模型实例的实例。但是,当您将表单呈现给模板时,此信息仅用于填写字段。也就是说,有关特定实例的信息丢失了。当然,当您打包后,就有办法连接到旧实例。

如何防止这种情况发生?常见的习惯用法是使用主键作为 URL 的一部分并在 POST 上查找实例。然后创建表格。在你的情况下,这意味着:

def create_or_update(request, instance_id):
#                             ^^^^^ 
#                             URL param
    if request.method == 'POST':
        instance = get_object_or_None(Model, pk = instance_id)
        # ^^^^^
        # Look up the instance

        form = MyForm(request.POST, instance = instance)
        #                           ^^^^^^^
        #                           pass the instance now.
        if form.is_valid():
              ....

This is interesting. Here is my stab at it. Consider this line:

form = MyForm(request.POST)

Can you inspect the contents of request.POST? Specifically, check if there is any information regarding which instance of the model is being edited. You'll find that there is none. In other words, each time you save the form on POST a new instance will be created.

Why does this happen? When you create a form passing the instance=instance keyword argument you are telling the Form class to return an instance for an instance of the model. However when you render the form to the template, this information is used only to fill in the fields. That is, the information about the specific instance is lost. Naturally when you post pack there is way to connect to the old instance.

How can you prevent this? A common idiom is to use the primary key as part of the URL and look up an instance on POST. Then create the form. In your case this would mean:

def create_or_update(request, instance_id):
#                             ^^^^^ 
#                             URL param
    if request.method == 'POST':
        instance = get_object_or_None(Model, pk = instance_id)
        # ^^^^^
        # Look up the instance

        form = MyForm(request.POST, instance = instance)
        #                           ^^^^^^^
        #                           pass the instance now.
        if form.is_valid():
              ....
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文