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

114

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

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

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

neoice
источник
сначала вы должны проверить свою форму клиента и, если она действительна, создать копию из request.POST (new_data = request.POST.copy ()). а затем получить идентификатор клиента (из проверенной формы клиента) и с обновлением new_data сделать идентификатор клиента значение для поля foreignkey (возможно, клиент в вашей модели). И, наконец, рассмотрите new_data для проверки вашей второй формы (билеты)
Negar37

Ответы:

87

Это действительно не так уж сложно реализовать с помощью 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
Кстати, я не думаю, что наборы форм - хорошее решение описанной вами проблемы. Я всегда использовал их для представления нескольких экземпляров модели. Например, у вас есть форма кандидата, и вы хотите, чтобы 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) Возможно, это можно сделать с помощью наборов форм, но, насколько я понял, наборы форм в основном предназначены для ввода кратных одной и той же модели, которые могут быть необязательно привязаны к другой модели / моделям с помощью внешних ключей. Однако, похоже, нет опции по умолчанию для ввода данных более чем одной модели, и это не то, для чего, похоже, предназначен набор форм.

Gnudiff
источник
26

У меня совсем недавно была проблема, и я только понял, как это сделать. Предполагая, что у вас есть три класса: 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 на одной странице.


источник
3
В этом примере, как вы устанавливаете внешние ключи для моделей B и C, чтобы они указывали на первичную модель?
Пользователь
У меня есть только две модели, которые я хочу показать в одной форме. Но я не получаю выражение exclude = ('primary',). Что первично? Если есть 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
jozxyqk
источник
Просто видел django-betterformsи его класс MultiModelForm, прежде чем найти свой ответ. Их решение выглядит неплохо, но кажется, что оно давно не обновлялось. Вы все еще используете этот @jozxyqk? Какие-то проблемы?
enchance
@enchance, прошло несколько лет. Тогда я нашел это удобным и одним из лучших вариантов. Если вы не слишком увлекаетесь этим, это сэкономит время. Я могу представить, что когда вы захотите начать настраивать и создавать нетривиальные формы, было бы проще создать свои собственные. Простое смешивание форм и контекстов в представлениях - первая функция, которую я действительно упустил в django.
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метод.

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

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

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

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

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

С. Лотт
источник
Я не знаю как, поэтому не делайте этого
orokusaki
1
@orokusaki: Чего еще вы хотите? Кажется, это описывает решение. Что еще нужно сказать? Вопрос расплывчатый, поэтому сложно предоставить реальный код. Вместо того, чтобы жаловаться, пожалуйста, предложите улучшения. Что ты предлагаешь?
S.Lott
1

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

См. Https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

пользователь1376892
источник