Django: несколько моделей в одном шаблоне с использованием форм
Я создаю приложение отслеживания поддержки поддержки и имею несколько моделей, которые я хотел бы создать с одной страницы. Билеты принадлежат Заказчику через ForeignKey. Заметки принадлежат Билетам через ForeignKey. Я хотел бы иметь возможность выбора Клиента (что целый отдельный проект) ИЛИ создания нового Клиента, затем создания Билета и, наконец, создания Ноты, назначенной новому билету.
Поскольку я довольно новичок в Django, я стараюсь работать итеративно, каждый раз тестируя новые функции. Я играл с ModelForms, но я хочу скрыть некоторые из полей и выполнить некоторую сложную проверку. Кажется, что уровень контроля, который я ищу, требует либо набора форм, либо делает все вручную, в комплекте с утомительной, ручной кодировкой страницы шаблона, которую я пытаюсь избежать.
Есть ли какая-то прекрасная особенность, которую я пропускаю? У кого-то есть хорошая ссылка или пример использования форм? Я провел целый уик-энд в документах API для них, и я все еще не знаю. Является ли это проблемой дизайна, если я сломаю и ручного кода все?
Ответы
Ответ 1
Это не так сложно реализовать с помощью 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
Я был примерно в той же ситуации день назад, и вот мои 2 цента:
1) Я нашел, возможно, кратчайшую и краткую демонстрацию множественной записи модели в виде одной формы здесь: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/.
Вкратце: сделайте форму для каждой модели, отправьте их как шаблону в один <form>
, используя prefix
keyarg, так и проверив проверку дескриптора. Если есть зависимость, просто убедитесь, что вы сохранили "родительский"
модель до зависимостей, и используйте родительский идентификатор для внешнего ключа перед тем, как совершить сохранение "дочерней" модели. Ссылка имеет демонстрацию.
2) Возможно, в это могут быть применены формы, но, насколько я понял, formets предназначены в первую очередь для ввода кратных одной и той же модели, которые могут быть необязательно привязаны к другой модели/моделям с помощью внешних ключей. Тем не менее, по-видимому, нет опции по умолчанию для ввода нескольких данных модели, и не для того, что, по-видимому, подразумевается для набора форм.
Ответ 3
У меня совсем недавно была какая-то проблема, и я просто понял, как это сделать.
Предполагая, что у вас есть три класса: 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 на одной странице.
Ответ 4
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
Ответ 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
Ответ 6
"Я хочу скрыть некоторые поля и выполнить некоторую сложную проверку".
Я начинаю со встроенного интерфейса администратора.
-
Постройте ModelForm, чтобы отобразить нужные поля.
-
Расширьте форму с помощью правил проверки в форме. Обычно это метод clean
.
Убедитесь, что эта часть работает достаточно хорошо.
Как только это будет сделано, вы можете отойти от встроенного интерфейса администратора.
Затем вы можете обманывать несколько, частично связанных форм на одной веб-странице. Это куча шаблонов для представления всех форм на одной странице.
Затем вам нужно написать функцию просмотра, чтобы читать и проверять различные вещи формы и выполнять различные сохранения объектов().
"Является ли это проблемой дизайна, если я сломаю и ручного кода все?" Нет, это просто много времени, чтобы не принести большой пользы.
Ответ 7
В соответствии с документацией Django для этой цели используются встроенные формы:
"Inline formsets - это небольшой слой абстракции поверх формных форм моделей, которые упрощают работу с связанными объектами через внешний ключ".
См. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets