Несколько классов формы в общих представлениях django (класс) - PullRequest
15 голосов
/ 08 июня 2011

Я хотел бы использовать основанные на классах общие представления django 1.3 для форм, но иногда приходится управлять несколькими классами форм в одной форме.Однако, похоже, что существующие представления, основанные на FormMixin, предполагают единый класс формы.

Возможно ли это с общими представлениями и как мне это сделать?

РЕДАКТИРОВАТЬ: уточнить, у меня есть одна форма, но более одного (на основе ModelForm) класса.Например, в примере inline_formset в django docs я хотел бы представить страницу, на которой автор и его книг может быть отредактирован сразу, в единой форме:

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

Ответы [ 3 ]

9 голосов
/ 08 июня 2011

Столкнувшись с подобной проблемой, я пришел к выводу, что это невозможно.

Несмотря на то, что наличие нескольких форм на одной странице само по себе оказалось ошибкой дизайна, возникли всевозможные проблемы. Например, пользователь заполняет две формы, нажимает кнопку «отправить» на одной из них и теряет данные другой. Обходной путь требует сложного контроллера, который должен знать о состоянии всех форм на странице. (См. Также здесь для обсуждения связанных с этим проблем.)

Если наличие нескольких форм на странице не является вашим точным требованием, я бы посоветовал взглянуть на альтернативные решения.

Например, обычно можно показать пользователю только одну редактируемую форму за раз.

В моем случае я переключился на django-formwizard (, а не на django.contrib, который немного староват и в настоящее время находится в процессе доработки, но этот Обновление: Начиная с выпуска 1.4 Django, приложение django-formwizard будет доступно в django.contrib, заменив старого мастера форм. Оно уже в стволе, см. Документы ). Для пользователя я сделал так, чтобы на странице было много форм, но только одна из них редактируемая. И пользователь должен был заполнить формы в заранее установленном порядке. Это значительно облегчило работу с несколькими формами.

В противном случае, если формы действительно должны быть представлены одновременно, возможно, имеет смысл объединить их в одну.


ОБНОВЛЕНИЕ (после вашего уточнения):

Нет, вы также не можете иметь дело с наборами форм, используя универсальный FormView. Хотя ваш пример выглядит довольно простым для реализации: я думаю, что он очень похож на этот пример в Django документах по формам. Он имеет дело с двумя наборами форм, и вам просто нужно заменить один на форму (я думаю, вам все еще нужно указать префикс, чтобы избежать возможных конфликтов атрибутов id элементов).

Короче говоря, в вашем случае я бы создал подкласс django.views.generic.base.View и переопределил get() и post() методы для работы с формой и набором форм, аналогично приведенному выше примеру из Django docs.

В этом случае я думаю, что можно представить форму и набор форм редактируемыми - с помощью одной кнопки, чтобы отправить их обоих.

ДРУГОЕ ОБНОВЛЕНИЕ:

В Django trac есть активная недавняя заявка, #16256 Дополнительные представления на основе классов: производные от форм-наборов общие представления . Если все пойдет хорошо, в Django будут добавлены новые общие представления: FormSetsView, ModelFormSetsView и InlineFormSetsView. В частности, последний «предоставляет способ показать и обработать модель с ее встроенными наборами форм».

2 голосов
/ 12 июля 2017

Представление полей из двух моделей на одной странице просмотра

Вы должны расширить класс django.views.generic.View и переопределить методы get (request) и post (request).

Вот как я это сделал.

Я использую Джанго 1.11 .

Вот так выглядит моя форма (состоящая из двух форм): User registration form

Мой Просмотр класса, который отображает две мои формы:

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

У меня есть модели User и UserProfile. User равно django.contrib.auth.models.User и UserProfile выглядит следующим образом:

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 () (пользователь является экземпляром класса User)) UserProfile также будет сохранен.

Пояснение 2: , если создано: (уточнение из класса View)

  • Если пользователя СОЗДАЮТ, создайте UserProfile где пользователь = пользовательский экземпляр, который был только что отправлен через пользовательскую форму

  • адрес собирается из ProfileForm и добавляется в пользовательский экземпляр ранее вызов user.save ()

У меня есть две формы:

UserForm:

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

ProfileForm:

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

Я надеюсь, что я хорошо понял ваш вопрос и что это поможет вам (и другим). :)

1 голос
/ 11 мая 2013

Один из принципов django заключается в том, что вы можете создать одну большую форму из нескольких меньших форм, используя только одну кнопку отправки. Вот почему теги <form> не генерируются самим django.

Проблема с общими видами, основанными на классе или нет, и множеством таких форм на заднем плане, конечно, в том, что небо - это предел. Формы могут быть связаны каким-либо образом: форма «мать» и необязательные дополнительные данные, которые зависят от данных в матери (скажем, один). Тогда есть модель, которая связана с несколькими другими моделями через внешние ключи и / или промежуточные таблицы, где вы будете использовать form + formsets. Кроме того, существует страница с полным набором форм, как в администраторе, когда вы делаете некоторые поля редактируемыми непосредственно в виде списка. Каждый из них представляет собой различные типы многоформальных представлений, и я не думаю, что было бы полезно создать одно общее представление, которое охватывало бы все случаи.

Если у вас есть «материнская» модель, вы можете использовать стандартные UpdateView или CreateView и добавить методы для дополнительных форм, которые вызываются из get() и post(), после кода, который имеет дело с мать модель. Например, в form_valid(), если материнская форма действительна, вы можете обрабатывать другие формы. У вас будет персональный компьютер матери, который вы затем используете для соединения данных в других формах.

...