Django: несколько моделей в одном шаблоне с использованием форм

Я создаю приложение для отслеживания заявок и у меня есть несколько моделей, которые я хотел бы создать на одной странице. Билеты принадлежат Заказчику через ForeignKey. Примечания принадлежат Билетам также через ForeignKey. Я хотел бы иметь возможность выбрать клиента (это целый отдельный проект) ИЛИ создать нового клиента, затем создать билет и, наконец, создать примечание, назначенное новому билету.

Поскольку я довольно новичок в Django, я склонен работать итеративно, каждый раз пробуя новые функции. Я играл с ModelForms, но я хочу скрыть некоторые поля и провести некоторую сложную проверку. Кажется, что уровень контроля, который я ищу, требует либо наборов форм, либо выполнения всего вручную, в комплекте с утомительной, вручную закодированной страницей шаблона, которой я стараюсь избегать.

Есть ли какая-то прекрасная особенность, которую я пропускаю? У кого-нибудь есть хорошая ссылка или пример для использования форм-наборов? Я потратил целые выходные на документацию по API для них, и до сих пор ничего не понимаю. Это проблема дизайна, если я сломаюсь и все вручную пишу?

7 ответов

Это действительно не так сложно реализовать с помощью ModelForms. Допустим, у вас есть формы A, B и C. Вы распечатываете каждую из форм и страницу, и теперь вам нужно обработать POST.

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

Вот документы для пользовательской проверки.

Я просто был примерно в той же ситуации день назад, и вот мои 2 цента:

1) Я нашел, пожалуй, самую короткую и краткую демонстрацию записи нескольких моделей в одной форме здесь: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/,

В двух словах: создайте форму для каждой модели, отправьте оба шаблона в один <form>, с помощью prefix keyarg и иметь проверку дескриптора представления. Если есть зависимость, просто убедитесь, что вы сохранили "родительскую" модель перед зависимой, и использовали идентификатор родителя для внешнего ключа, прежде чем сохранять "дочернюю" модель. По ссылке есть демоверсия.

2) Возможно, формальные наборы можно превзойти в этом, но, насколько я понял, наборы форм в основном предназначены для ввода кратных значений одной и той же модели, которые могут быть привязаны к другой модели / моделям с помощью внешних ключей. Тем не менее, похоже, что по умолчанию нет возможности ввода более чем одной модели данных, и это не то, для чего, похоже, предназначен набор форм.

У меня совсем недавно была какая-то проблема, и я просто понял, как это сделать. Предполагая, что у вас есть три класса, Primary, B, C и что B, C имеют внешний ключ к первичному

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

Этот метод должен позволить вам выполнить любую необходимую вам проверку, а также создать все три объекта на одной странице. Я также использовал javascript и скрытые поля для создания нескольких объектов B, C на одной странице.

MultiModelForm от django-betterforms удобная оболочка для выполнения того, что описано в ответе Гнудифа. Оборачивается регулярно ModelForm s в одном классе, который прозрачно (по крайней мере, для базового использования) используется как единая форма. Я скопировал пример из их документов ниже.

# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs

В настоящее время у меня есть обходной функционал (он проходит мои юнит-тесты). Это хорошее решение, на мой взгляд, когда вы хотите добавить ограниченное количество полей из других моделей.

Я что-то здесь упускаю?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile

"Я хочу скрыть некоторые поля и выполнить некоторую сложную проверку".

Я начинаю со встроенного интерфейса администратора.

  1. Создайте ModelForm, чтобы показать нужные поля.

  2. Расширьте форму с помощью правил проверки в форме. Обычно это clean метод.

    Убедитесь, что эта часть работает достаточно хорошо.

Как только это будет сделано, вы можете отойти от встроенного интерфейса администратора.

Тогда вы можете дурачиться с несколькими, частично связанными формами на одной веб-странице. Это набор шаблонов для представления всех форм на одной странице.

Затем вы должны написать функцию просмотра, чтобы читать и проверять различные формы и делать различные сохранения объектов ().

"Это проблема дизайна, если я сломаюсь и все вручную напишу?" Нет, это просто много времени для небольшой выгоды.

Согласно документации Django, встроенные наборы форм предназначены для этой цели: "Встроенные наборы форм - это небольшой уровень абстракции поверх наборов форм модели. Они упрощают случай работы со связанными объектами через внешний ключ".

См. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/

Другие вопросы по тегам