【问题标题】:Django: saving multiple modelforms simultaneously (complex case)Django:同时保存多个模型表单(复杂情况)
【发布时间】:2011-06-29 07:42:05
【问题描述】:

嗯,这实际上可能是一个简单的案例,但我很难弄清楚。

我有两个用户注册渠道(公司和其他所有人)。当企业用户通过我的注册表单创建 User 实例时,我还希望他们输入创建相关实例的辅助表单(基于 Website 和 Organization_Contact 模型)。

我知道如何通过发出额外的同步或异步请求来解决这个问题,但我希望用户填写三个表单并一键提交。

我的问题是其他两种形式依赖于用户外键作为字段。我已经将该字段设置为“null=True,blank=True”,这样我就可以在没有外键的情况下验证和保存表单,但我最终想将该键添加到两个模型实例中。

我认为我可以验证三个表单,保存 UserForm,然后使用模型查询集返回新的 User.id(全部在一个视图中)。然后,我将该 user_id 值添加到其他两个表单字典(WebsiteForm 和 Organization_ContactForm)。

它会这样工作:

def register_company(request):
    if request.method=='POST'
       uform=UserCreationForm(request.POST, prefix='user')
       sform=SiteForm(request.POST, prefix='site')
       oform=OrgForm(request.POST, prefix='site')
       if uform.is_valid() and sform.is_valid() and oform.is_valid():
            uform.save()
            user=User.objects.get(email__iexact=uform.cleaned_data['email'])
            uid=unicode(user.id)
       #now I'd add uid back into the SiteForm and Orgform dictionaries and then save both instances              

问题: 1) 不确定我是否可以保存模型表单,然后将该模型实例作为查询集返回到单个视图中

2)我说我不确定,因为我无法通过尝试将变量传递给查询集的问题。

get manager 方法似乎不接受那里的变量。我假设是因为我传递了一个等效的硬编码字符串并且它起作用了。

好的,所以我正在考虑创建一个新的管理器方法(电子邮件),它接受一个变量参数(清理后的电子邮件字段),然后重试保存一个模型表单、检索模型 ID 数据和保存其他模型表单的过程.

我还认为我可以通过保存后信号来处理这个问题。

这里只是大致的方向会非常有帮助。我需要一个起点。

【问题讨论】:

    标签: django django-queryset modelform


    【解决方案1】:

    这些都是ModelForms吗?

    if request.method=='POST'
       uform=UserCreationForm(request.POST, prefix='user')
       sform=SiteForm(request.POST, prefix='site')
       oform=OrgForm(request.POST, prefix='site')
       if uform.is_valid() and sform.is_valid() and oform.is_valid():
            # save returns the object 
            user = uform.save()
    
            # commit = false doesn't save to DB.
            site = sform.save(commit=False)
            site.user = user
            site.save()
    
            org = oform.save(commit=False)
            org.user = user
            org.save()
    

    关于 cmets 的更新:既然可以在一个地方进行,为什么要将这种表单保存逻辑分散到多个区域和文件(信号、表单保存)中?

    代码更具可读性,您甚至可以节省几行代码。除非它要在全球范围内使用。

    【讨论】:

    • 抱歉,我花了很长时间解释我在代码上略过的一般问题。是的,这些都是 ModelForms,并且有问题的视图显然超出了 org.save()。
    • 对不起,这不能解决您的问题吗?你说#now I'd add uid back into the SiteForm and Orgform dictionaries and then save both instances。这是这样做的更好方法:) 让表单尽其所能,然后直接设置用户,而不是修改您传递给 2 个表单的 POST 字典。
    • 我想我应该只保存所有三个模型表单,然后发送一个带有回调的保存后信号,该回调从新用户实例中获取 user_id 并更新其他两个新模型实例(组织和站点)。这合理吗?或者我可以在同一个视图中保存和查询(这更可取)?
    • 这听起来像是一个简单问题的复杂解决方案!我肯定更喜欢复制和粘贴上面的代码,而不是将这个逻辑传播到视图之外。您已经有一个从保存 UserCreationModelForm 返回的用户实例,所以使用它!如果您必须查询用户表才能再次获取实例,为什么还要使用用户 ID?
    【解决方案2】:

    我会这样做:

    def register_company(request):
        uform=UserCreationForm(request.POST or None, prefix='user')
        sform=SiteForm(request.POST or None, prefix='site')
        oform=OrgForm(request.POST or None, prefix='site')
    
        if request.POST and all((uform.is_valid(), sform.is_valid(), oform.is_valid()):
            user = uform.save()
            sform.save(user)
            oform.save(user)
    
        ruturn ...
    
    class UserCreateionForm(ModelForm):
        Meta:
            model = User
    
    class SiteForm(ModelForm):
        Meta:
            model=Site
            exclude=['user', ]
    
        def save(self, user, commit=True):
            self.instance.user = user
            return super(SiteForm, self).save(commit=commit)
    

    【讨论】:

    • Skir 大人,你简直让我想跳出窗外。我什至从未考虑过 save 可以将实例作为参数。我只见过“commit=False”的说法。哦,男孩,如果是真的,那太容易了。
    • 它不能,但你可以定义你的保存方法来做到这一点。而在 Meta 中,您使用 exclude=['user',],因为没有此行,您的表单将无法验证并会绘制用户选择框。
    • 对,所以我知道 exclude 并且实际上我对您的解决方案感到非常兴奋,以至于我急于尝试它而没有低头查看覆盖的保存方法。我熟悉的那个。我现在要试一试。
    • 顺便说一句,请注意我在 if 语句中使用了 all(...) 而不是 and。因为在您的示例中,如果第一个表单无法验证,其他表单将不会被验证,用户只会看到第一个表单的错误。
    • 设置为 def save(self, user):
    猜你喜欢
    • 2020-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-31
    • 2016-12-20
    • 2017-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多