17

我想将 django 1.3 的基于类的通用视图用于表单,但有时必须在一个表单中管理多个表单类。但是,看起来基于 FormMixin 的现有视图假设一个表单类。

这对于通用视图是否可行,我该怎么做?

编辑:澄清一下,我有一种形式,但不止一种(基于 ModelForm 的)类。例如,在 django 文档中的 inline_formset 示例中,我想展示一个页面,可以在其中以单一形式一次编辑作者及其书籍

author_form = AuthorForm(request.POST, instance = author)
books_formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
4

3 回答 3

11

面对类似的问题,我得出结论,这是不可能的。

尽管每页有多个表单本身被证明是一个设计错误,但会带来各种麻烦。例如,用户填写了两张表格,点击其中一张的提交并丢失了另一张的数据。解决方法需要复杂的控制器,该控制器需要了解页面上所有表单的状态。(有关相关问题的一些讨论,另请参见此处。)

如果每页有多个表单不是您的确切要求,我建议您查看替代解决方案。

例如,通常一次只能向用户显示一个可编辑的表单。

就我而言,我切换到django-formwizard(不是 django.contrib 一个,它有点旧,目前似乎正在重新设计,但这个 更新:从 Django 1.4 版开始,django-formwizard应用程序将在 中可用django.contrib,替换旧的表单向导. 它已经在后备箱中,请参阅文档)。对于用户,我让它看起来页面上实际上有多个表单,但只有一个是可编辑的。用户必须按预定顺序填写表格。这使得处理多种形式变得更加容易。

否则,如果真的需要一次呈现所有表单,将它们组合成一个可能是有意义的。


更新(在您澄清之后):

不,您也不能使用泛型处理表单集FormView。尽管您的示例似乎很容易实现:我认为它与Django docs on formsets 中的此示例非常相似。它处理两个表单集,您只需用表单替换一个(我认为您仍然需要指定前缀以避免元素id属性可能发生冲突)。

简而言之,在您的情况下,我将继承django.views.generic.base.View和覆盖get()以及post()处理表单和表单集的方法,类似于 Django 文档中的上述示例。

在这种情况下,我认为呈现可编辑的表单和表单集是很好的——只需一个按钮即可同时提交它们。

另一个更新:

在 Django trac 中有一张有效的最新票,#16256更多基于类的视图:表单集派生的通用视图。如果一切顺利,新的通用视图将被添加到 Django FormSetsView:ModelFormSetsViewInlineFormSetsView. 特别是,最后一个“提供了一种使用内联表单集显示和处理模型的方法”。

于 2011-06-08T11:51:20.400 回答
4

在单个视图页面上显示来自两个模型的字段

您必须扩展 django.views.generic.View 类并覆盖 get(request) 和 post(request) 方法。

我就是这样做的。

我正在使用Django 1.11

这就是我的表单(由两个表单组成)的样子: 用户登记表

我的View类呈现我的两种形式:

from django.views.generic import View

class UserRegistrationView(View):
    # Here I say which classes i'm gonna use
    # (It's not mandatory, it's just that I find it easier)
    user_form_class = UserForm
    profile_form_class = ProfileForm
    template_name = 'user/register.html'

    def get(self, request):
        if request.user.is_authenticated():
            return render(request, 'user/already_logged_in.html')
        # Here I make instances of my form classes and pass them None
        # which tells them that there is no additional data to display (errors, for example)
        user_form = self.user_form_class(None)
        profile_form = self.profile_form_class(None)
        # and then just pass them to my template
        return render(request, self.template_name, {'user_form': user_form, 'profile_form': profile_form})

    def post(self, request):
        # Here I also make instances of my form classes but this time I fill
        # them up with data from POST request
        user_form = self.user_form_class(request.POST)
        profile_form = self.profile_form_class(request.POST)

        if user_form.is_valid() and profile_form.is_valid():
            user = user_form.save(commit=False)
            user_profile = profile_form.save(commit=False)

            # form.cleaned_data is a dictionary which contains data validated
            # by fields constraints (Say we have a field which is a number. The cleaning here would 
            # be to just convert a string which came from the browser to an integer.)
            username = user_form.cleaned_data['username']
            password = user_form.cleaned_data['password']

            # This will be clarified later 
            # You can save each object individually if they're not connected, as mines are (see class UserProfile below)
            user.set_password(password)
            user.userprofile = user_profile
            user.save()

            user = authenticate(username=username, password=password)

            if user is not None:
                if user.is_active:
                    login(request, user)
                return redirect('user:private_profile')

        # else: # form not valid - each form will contain errors in form.errors
        return render(request, self.template_name, {
            'user_form': user_form,
            'profile_form': profile_form
        })

我有一个UserUserProfile模型。 Userdjango.contrib.auth.models.UserUserProfile如下:

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    friends = models.ManyToManyField('self', null=True, blank=True)
    address = models.CharField(max_length=100, default='Some address 42')

    def get_absolute_url(self):
        return reverse('user:public_profile', kwargs={'pk': self.pk})

    def __str__(self):
        return 'username: ' + self.user.username + '; address: ' + self.address

    @receiver(post_save, sender=User) # see Clarification 1 below
    def create_user_profile(sender, instance, created, **kwargs):
        if created: # See Clarification 2 below
            UserProfile.objects.create(user=instance, address=instance.userprofile.address)

    @receiver(post_save, sender=User)
    def update_user_profile(sender, instance, **kwargs):
        instance.userprofile.save()

说明1: @receiver(post_save, sender=User)

  • 当用户被保存时(我在某处写了 user.save() (用户是用户类的一个实例)) UserProfile 也将被保存。

说明2:如果已创建:(来自 View 类的说明)

  • 如果正在创建用户,请创建一个用户配置文件,其中用户 = 用户实例,该实例刚刚通过用户表单提交

  • 地址从 ProfileForm 收集并在调用 user.save() 之前添加到用户实例

我有两种形式:

用户窗体:

class UserForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
    password_confirmation = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)

    first_name = forms.CharField(required=True)
    last_name = forms.CharField(required=True)

    class Meta:
        model = User
        fields = ('email', 'username', 'first_name', 'last_name', 'password', 'password_confirmation')

    def clean(self):
        cleaned_data = super(UserForm, self).clean()
        password = cleaned_data.get("password")
        password_confirmation = cleaned_data.get("password_confirmation")

        if password != password_confirmation:
            self.fields['password'].widget = forms.PasswordInput()
            self.fields['password_confirmation'].widget = forms.PasswordInput()

            self.add_error('password', "Must match with Password confirmation")
            self.add_error('password_confirmation', "Must match with Password")
            raise forms.ValidationError(
                "Password and Password confirmation do not match"
            )

个人资料表格:

class ProfileForm(forms.ModelForm):
    class Meta:
        model = UserProfile
        fields = ('address',)

我希望我能很好地理解你的问题,这将有助于你(和其他人)。:)

于 2017-07-12T11:47:08.053 回答
2

django 的原则之一是您可以从几个较小的表单中构建一个大表单,只需一个提交按钮。这就是为什么<form>-tags 不是由 django 本身生成的。

通用视图的问题,无论是否基于类,以及背景中的多种此类形式,当然是天空是极限。这些表格可能以某种方式相关:“母亲”表格和可选的额外数据,这些数据取决于母亲中的数据(例如,onetoone)。然后是通过外键和/或中间表连接到其他几个模型的模型,您可以在其中使用表单+表单集。然后是所有表单集类型的页面,例如在管理员中,当您直接在列表视图中使某些字段可编辑时。每一种都是不同类型的多表单视图,我认为制作一个涵盖所有情况的通用视图不会有成效。

但是,如果您有一个“母”模型,则可以在处理母模型的代码之后为从and调用的额外表单使用标准UpdateVieworCreateView和添加方法。例如,如果母表格有效,您可以处理其他表格。您将拥有母亲的 pk,然后您可以使用它来连接其他形式的数据。get()post()form_valid()

于 2013-05-11T16:24:43.993 回答