Форма Django действительна только после второго запроса

У меня очень странная проблема с формами django, я отображаю форму, которая включает в себя дополнительный набор форм, чтобы пользователь также мог одновременно отправлять данные для связи с внешним ключом.

Шаблон всегда отображает форму для исходной модели и одну форму для второй модели.

Теперь я хочу отправить две формы, не заполняя ничего во второй форме. При первом представлении форма 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 ответа

Решение

Хорошо, я наконец нашел свою проблему!

У меня есть следующее поле модели: 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 автоматически установит усеченное значение как initial-file-date, после чего ничего не изменится, и сохранение будет работать, как и ожидалось.

Так что теперь мне нужно только выяснить, что использовать вместо datetime.now. Я буду обновлять этот пост, когда я понял это.

Прежде всего, вы должны устанавливать аргумент экземпляра только при создании формы / набора форм для существующего объекта, то есть уже в БД. Так, например, если 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)
    )
Другие вопросы по тегам