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

98

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

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

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

Теги:
django-forms
design

7 ответов

68

Это не так сложно реализовать с помощью 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 экземпляра эталонной модели.
  • 0
    обратите внимание, что при том, как вы это делаете, вызов .is_valid () не замыкается накоротко. Если вы хотите замкнуть его, вам придется отложить вызов функции .is_valid () до «и».
63

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

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

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

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

24

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

  • 2
    В этом примере, как вы устанавливаете внешние ключи для моделей B и C, чтобы они указывали на основную модель?
  • 0
    У меня есть только две модели, которые я хочу показать в одной форме. Но я не получаю выражение exclude = ('primary',). Что первично? Если есть 2 модели CustomerConfig и Contract. Контракт имеет внешний ключ для CustomerConfig. Например, customer_config = models.ForeignKey ('CustomerPartnerConfiguration') Что такое «основной»?
8

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

# 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
4

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

Я что-то пропустил?

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
2

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

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

  • Постройте ModelForm, чтобы отобразить нужные поля.

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

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

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

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню