【问题标题】:Django form is only valid after second requestDjango 表单仅在第二次请求后有效
【发布时间】:2011-12-09 21:07:29
【问题描述】:

我对 django 表单有一个非常奇怪的问题,我显示了一个包含附加表单集的表单,以便用户也可以同时提交外键关系的数据。

模板始终为原始模型显示一个表单,为第二个模型显示一个表单。

我现在想提交这两个表单,而不在第二个表单中填写任何内容。 在第一次提交时,第二个表单不验证并重新显示页面,但在第二次提交时,第二个表单是有效的!即使如此,POST 数据也是相同的。 这怎么可能?

或者我做的完全错了,你怎么知道用户是没有在表单集中填写任何东西,或者他填写的东西是无效的?

这里是模型:

class Software(models.Model):
    creation_date = models.DateTimeField(default=datetime.now)
    creator = models.ForeignKey(User)
    version = models.CharField(max_length=300, unique=True, editable=False)
    major_version = models.IntegerField()
    minor_version = models.IntegerField()
    [...]

    def save(self, **kwargs):
        """
        This updates the version string to the combined representation.
        """
        self.version = Software.combine_version_string (self.major_version, self.minor_version)
        super(Software, self).save(**kwargs)

class SoftwarePatch(models.Model):
    file  = models.FileField(upload_to='software_patches')
    file_name = models.CharField(max_length=255, editable=False)
    file_date = models.DateTimeField(default=datetime.now)
    upload_date = models.DateTimeField(default=datetime.now)
    software = models.ForeignKey('Software', related_name='patches')
    firmware_patch = models.BooleanField(default=True)
    target_path = models.CharField(max_length=255, blank=True)

    class Meta:
        unique_together = ('software', 'file_name')
        verbose_name_plural = "software patches"

    def __unicode__(self):        
        return self.file_name

    def clean(self):
          if self.file and not self.file_name:
              self.file_name = self.file.file.name 

这是我的表格:

SoftwarePatchFormSet = inlineformset_factory(Software, 
    SoftwarePatch, 
    extra=1)


class SoftwareForm(forms.ModelForm):
    """
    A simple form for creating a new software.
    """
    class Meta:
        model = Software

最后是我的视图函数:

def software_add(request, software_id=None):
    if software_id == None:
        software = Software()
    else:
        software = Software.objects.get(id=software_id)

    if request.POST:        
        form = SoftwareForm(request.POST, instance=software)        

        if form.is_valid():
            software = form.save(commit=False)
            softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)

            if softwarepatch_formset.is_valid():
                software = form.save()
                softwarepatch_formset.save()

                # Redirect, in case of a popup close it
                if request.POST.has_key("_popup"):
                    pk_value = software._get_pk_val()
                    return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
                        # escape() calls force_unicode.
                        (escape(pk_value), escape(software)))
                if 'next' in request.POST:
                    return HttpResponseRedirect(request.POST['next'])
                else:
                    return HttpResponseRedirect(reverse('index'))
    else:
        form = SoftwareForm(instance=software)
        softwarepatch_formset = SoftwarePatchFormSet(instance=software)

    is_popup = request.GET.has_key("_popup") or request.POST.has_key("_popup")

    return render_to_response(
        'main/software_edit.html',
        {'form': form,
         'softwarepatch_formset': softwarepatch_formset,
         'add': True,
         'is_popup': is_popup,
        },
        context_instance = RequestContext(request)
    )

【问题讨论】:

    标签: django forms formsets


    【解决方案1】:

    首先,您应该仅在为现有对象(即数据库中已存在的对象)创建表单/表单集时设置实例参数。例如,如果software_id = None 是一个 GET 请求,你应该只做form = SoftwareForm()

    另外,在执行software = form.save(commit=False) 之后,您应该执行software.save() 而不是software = form.save()。 [我不认为这真的是一个问题,只是你正在重做一个保存]。请记住,如果您在软件模型中有一个ManyToManyField,您还需要在software = form.save() 之后添加form.save_m2m()

    这是我认为你应该拥有的:

    def software_add(request, software_id=None):
        if request.POST: 
            if software_id:
                software = Software.objects.get(id=software_id)
                form = SoftwareForm(request.POST, instance=software)
            else:
                form = SoftwareForm(request.POST)
    
            if form.is_valid():
                software = form.save(commit=False)
                softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)
    
                if softwarepatch_formset.is_valid():
                    software.save()
                    softwarepatch_formset.save()
    
                    # Redirect, in case of a popup close it
                    if request.POST.has_key("_popup"):
                        pk_value = software._get_pk_val()
                        return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
                            # escape() calls force_unicode.
                            (escape(pk_value), escape(software)))
                    if 'next' in request.POST:
                        return HttpResponseRedirect(request.POST['next'])
                    else:
                        return HttpResponseRedirect(reverse('index'))
            else:
                softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES)     
        else:
            if software_id:
                software = Software.objects.get(id=software_id)
                form = SoftwareForm(instance=software)
                softwarepatch_formset = SoftwarePatchFormSet(instance=software)
            else:
                form = SoftwareForm()
                softwarepatch_formset = SoftwarePatchFormSet()
    
    
        is_popup = request.GET.has_key("_popup") or request.POST.has_key("_popup")
    
        return render_to_response(
            'main/software_edit.html',
            {'form': form,
             'softwarepatch_formset': softwarepatch_formset,
             'add': True,
             'is_popup': is_popup,
            },
            context_instance = RequestContext(request)
        )
    

    【讨论】:

    • 您好,感谢您的建议,但不幸的是,这并不能解决我的问题。因为我想保存软件模型,所以没有填写 software_patch。这在管理后端以某种方式工作。在那里你可以显示例如software_patch 表单集的三个条目,如果您不填写任何一个,我仍然可以保存。
    • 顺便说一句。上面的代码有错误。如果软件表单无效,您将尝试传递 'softwarepatch_formset': softwarepatch_formset,最后会崩溃。
    【解决方案2】:

    好的,我终于找到了我的问题!

    我有以下模型字段:file_date = models.DateTimeField(default=datetime.now)

    这会将 innital-file-date 设置为如下值:u'2011-10-18 08:14:30.242000' 通过 html 小部件呈现后,该值将是:u'2011-10-18 08:14:30' 所以 django 会认为表单已更改,因此不会保存。

    在第二次加载时,django 会自动将截断的值设置为初始文件日期,然后什么都没有改变,保存按预期工作。

    所以现在我只需要弄清楚使用什么来代替 datetime.now。等我弄明白后我会更新这篇文章。

    【讨论】:

    • 您可以添加auto_addauto_now_add [docs.djangoproject.com/en/dev/ref/models/fields/#datefield]。请注意,该字段不会显示在管理员或ModelForm 自动生成的表单上。否则删除默认值,如果用户没有在视图中输入request.POST["field_date"] [before softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)]
    • 我现在在这里接受我自己的答案,因为我发现了所描述的问题。我为有问题的 datetime.now 语句添加了一个单独的问题 stackoverflow.com/questions/7836005/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    相关资源
    最近更新 更多