Django表单仅在第二次请求后才有效

时间:2011-10-18 07:47:01

标签: django forms formsets

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

模板始终显示原始模型的表单和第二个模型的一个表单。

我现在想要提交两个表格而不填写第二个表格中的任何内容。 在第一次提交时,seond表单不会验证并重新显示页面,但在第二次提交时,第二个表单是有效的!即使如此,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)
    )

2 个答案:

答案 0 :(得分:1)

首先,只有在为现有对象(即DB中已有对象)创建表单/表单集时,才应设置实例参数。例如,如果software_id = None并且它是GET请求,则应该form = SoftwareForm()

此外,在执行software = form.save(commit=False)后,您应该software.save()而不是software = form.save()。 [我认为这不是一个真正的问题,只是你正在重做一次保存]。请注意,如果您在软件模型中有ManyToManyField,那么您需要在form.save_m2m()之后software = form.save()进行操作。

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

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)
    )

答案 1 :(得分:0)

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

我有以下模型字段: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会自动将截断值设置为initial-file-date,然后不会更改任何内容,并且保存按预期工作。

所以现在我只需要弄清楚要使用什么而不是datetime.now。我想出来之后我会更新这篇文章。