Django: кілька моделей в одному шаблоні за допомогою форм [закрито]


114

Я будую додаток для відстеження квитків із підтримкою і маю кілька моделей, які я хотів би створити з однієї сторінки. Квитки належать Клієнту через ForeignKey. Примітки належать також і до квитків через ForeignKey. Я хотів би мати можливість обрати Клієнта (це цілком окремий проект) АБО створити нового Клієнта, потім створити Квиток і нарешті створити Примітку, присвоєну новому квитку.

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

Чи є якась чудова особливість, якої я пропускаю? Хтось має хороший довідник чи приклад для використання наборів форм? Я витратив цілі вихідні на документи API для них, і я все ще не знаю. Це проблема з дизайном, якщо я все зламаю і вручну кодую?


спочатку слід підтвердити форму клієнта, і якщо вона була дійсною, створити копію з request.POST (new_data = request.POST.copy ())., а потім отримати ідентифікатор клієнта (з підтвердженої форми клієнта) та оновивши new_data, зробіть ID клієнта - значення для поля іноземних ключів (можливо, клієнт у вашій моделі). Нарешті, розгляньте нові дані для підтвердження другої форми (Квитки)
Negar37

Відповіді:


87

Це реально не надто важко реалізувати з ModelForms . Тож скажімо, що у вас є форми A, B і C. Ви роздруковуєте кожну з форм і сторінку, і тепер вам потрібно обробляти пошту.

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
btw, я не думаю, що набори форматів є гарним рішенням описаної вами проблеми. Я завжди використовував їх для представлення декількох примірників моделі. Наприклад, у вас є форма заявника, і ви хочете, щоб 3 посилання на вас склали набір форм, який містить 3 екземпляри моделі Довідник.
Джейсон Кріста,

1
зауважте, що, як ви це робите, виклик .is_valid () не має короткого замикання. Якщо ви хочете коротке замикання на нього, вам потрібно буде затримати виклик функції .is_valid () до тих пір, коли «і».
Лежати Райан

66

Я щойно був у тій же ситуації, що був день тому, і ось мої 2 копійки:

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

Коротше кажучи: Створіть форму для кожної моделі, подайте їх обох до шаблону в єдиному <form>, використовуючи prefixkeyarg та отримайте перевірку ручки подання. Якщо існує залежність, просто переконайтеся, що ви зберегли "батьківську" модель перед залежною, і використовуйте ідентифікатор батьків для зовнішнього ключа, перш ніж зайнятися збереженням "дочірньої" моделі. Посилання має демонстраційну версію.

2) Можливо, набори форматів можуть бути обіграні, але, наскільки я заглибився, формати - це в основному для введення кратностей однієї моделі, які можуть бути необов'язково прив’язані до іншої моделі / моделі за допомогою зовнішніх ключів. Однак, як видається, немає типового варіанту для введення більше даних однієї моделі, і це не те, для чого набір форматів, мабуть, призначений.


26

Я зовсім недавно мала певну проблему і просто придумала, як це зробити. Якщо припустити, що у вас є три класи: Первинний, В, С і 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 на одній сторінці.


3
У цьому прикладі, як ви встановлюєте зовнішні клавіші для моделей B і C для вказівки на первинну модель?
Користувач

У мене є лише дві моделі, які я хочу показати в одній формі. Але я не отримую оператор виключення = ('основний',). Що є первинним? Якщо є 2 моделі CustomerConfig та Contract. Контракт має іноземний ключ до CustomerConfig. Такі як customer_config = models.ForeignKey ("CustomerPartnerConfiguration") Що таке "первинне"?
pitchblack408

10

MultiModelForm з django-betterformsзручна обгортка робити те , що описано в відповідь Gnudiff в . Він поєднує звичайні ModelForms в один клас, який прозоро (принаймні для основного використання) використовується як одна форма. Я скопіював приклад із своїх документів нижче.

# 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

Щойно побачив django-betterformsта його клас MultiModelForm, перш ніж натрапити на вашу відповідь. Їх рішення виглядає справді добре, але, здається, воно не оновлювалося протягом певного часу. Ви все ще використовуєте цей @jozxyqk? Якісь проблеми?
enchance

@enchance минуло кілька років. Тоді я знайшов це зручним і одним з кращих варіантів. Якщо ви не надто захоплюєтесь цим, це економить час. Я можу собі уявити, коли ви хочете почати налаштовувати та робити нетривіальні форми, було б простіше прокрутити свої. Легке змішування форм і контекстів у поглядах - це перша особливість, яку я дійсно думаю, що я пропустив у джанго.
jozxyqk

Дякую за відповідь. Я розглядаю можливість просто розвантажити його, і, можливо, оновити кілька речей по дорозі. З того, що я бачив до цього часу, це працює просто чудово. Ви маєте рацію, це величезна економія часу.
enchance

5

Наразі у мене є функціональний спосіб вирішення (він проходить мої тестові одиниці). На мою думку, це гарне рішення, коли ви хочете додати лише обмежену кількість полів з інших моделей.

Я щось тут пропускаю?

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

3

"Я хочу приховати деякі поля та провести складну перевірку."

Я починаю із вбудованого інтерфейсу адміністратора.

  1. Побудуйте ModelForm, щоб показати потрібні поля.

  2. Розкладіть Форму за допомогою правил перевірки у формі. Зазвичай це cleanметод.

    Переконайтеся, що ця частина працює досить добре.

Після цього ви зможете відійти від вбудованого інтерфейсу адміністратора.

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

Тоді вам потрібно написати функцію перегляду, щоб прочитати та перевірити різні речі форми та зробити різні об'єкти збереження ().

"Це проблема дизайну, якщо я все зламаю і вручну кодую?" Ні, це просто багато часу для не великої користі.


Я не знаю як, тому не робіть цього
orokusaki

1
@orokusaki: Що ще ти хотів би? Це, здається, описує рішення. Що ще слід сказати? Питання невизначене, тому важко надати фактичний код. Замість того, щоб скаржитися, будь ласка, надайте пропозицію щодо вдосконалення. Що ти пропонуєш?
S.Lott

1

Згідно з документацією Django, для цієї мети призначені вбудовані набори форматів: "Вбудовані набори форматів - це невеликий абстракційний шар поверх модельних форматів. Вони спрощують випадок роботи із суміжними об'єктами через зовнішній ключ".

Дивіться https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.