0

我对 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)
    )
4

2 回答 2

1

首先,您应该仅在为现有对象创建表单/表单集时设置实例参数,即已经在数据库中的对象。因此,例如,如果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)
    )
于 2011-10-18T08:54:32.363 回答
1

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

我有以下模型字段: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。当我弄清楚时,我会更新这篇文章。

于 2011-10-19T05:11:18.113 回答