фабрика inlineformset создает новые объекты и редактирует объекты после создания
в документах django есть пример использования inlineformset_factory для редактирования уже созданные объекты
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-an-inline-formset-in-a-view
Я изменил пример таким образом:
def manage_books(request):
author = Author()
BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',))
if request.method == "POST":
formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
if formset.is_valid():
formset.save()
return HttpResponseRedirect(author.get_absolute_url())
else:
formset = BookInlineFormSet(instance=author)
return render_to_response("manage_books.html", {
"formset": formset,
})
с вышеизложенным он отображает только встроенную модель без родительской модели.
чтобы создать новый объект, скажем Author, с несколькими книгами связанный с использованием inlineformset_factory, каков подход?
пример использования вышеуказанной модели авторской книги из django docs будет полезен. В документах django приведен только пример того, как редактировать уже созданный объект с помощью inlineformset_factory а не создать новый
4 ответов
сначала я не правильно прочитал Ваш вопрос. Необходимо также отобразить форму для родительской модели. Я не тестировал это, я ухожу от того, что я делал раньше, и ранее связанный ответ, но он должен работать.
обновление
Если вы используете представление для обоих и редактирования, вы должны сначала проверить идентификатор автора. Если нет ID, он будет отображать обе формы как новый экземпляр, тогда как с ID он будет заполнять их существующими данными. Затем вы можете проверить, был ли запрос POST.
def manage_books(request, id):
if id:
author = Author.objects.get(pk=author_id) # if this is an edit form, replace the author instance with the existing one
else:
author = Author()
author_form = AuthorModelForm(instance=author) # setup a form for the parent
BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',))
formset = BookInlineFormSet(instance=author)
if request.method == "POST":
author_form = AuthorModelForm(request.POST)
if id:
author_form = AuthorModelForm(request.POST, instance=author)
formset = BookInlineFormSet(request.POST, request.FILES)
if author_form.is_valid():
created_author = author_form.save(commit=False)
formset = BookInlineFormSet(request.POST, request.FILES, instance=created_author)
if formset.is_valid():
created_author.save()
formset.save()
return HttpResponseRedirect(created_author.get_absolute_url())
return render_to_response("manage_books.html", {
"author_form": author_form,
"formset": formset,
})
Я сделал это, используя представления на основе классов Django.
вот мой подход:
models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author)
title = models.CharField(max_length=100)
forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset
from .models import Author, Book
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', )
@property
def helper(self):
helper = FormHelper()
helper.form_tag = False # This is crucial.
helper.layout = Layout(
Fieldset('Create new author', 'name'),
)
return helper
class BookFormHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(BookFormHelper, self).__init__(*args, **kwargs)
self.form_tag = False
self.layout = Layout(
Fieldset("Add author's book", 'title'),
)
BookFormset = inlineformset_factory(
Author,
Book,
fields=('title', ),
extra=2,
can_delete=False,
)
views.py
from django.views.generic import CreateView
from django.http import HttpResponseRedirect
from .forms import AuthorForm, BookFormset, BookFormHelper
from .models import Book, Author
class AuthorCreateView(CreateView):
form_class = AuthorForm
template_name = 'library/manage_books.html'
model = Author
success_url = '/'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
book_form = BookFormset()
book_formhelper = BookFormHelper()
return self.render_to_response(
self.get_context_data(form=form, book_form=book_form)
)
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
book_form = BookFormset(self.request.POST)
if (form.is_valid() and book_form.is_valid()):
return self.form_valid(form, book_form)
return self.form_invalid(form, book_form)
def form_valid(self, form, book_form):
"""
Called if all forms are valid. Creates a Author instance along
with associated books and then redirects to a success page.
"""
self.object = form.save()
book_form.instance = self.object
book_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, book_form):
"""
Called if whether a form is invalid. Re-renders the context
data with the data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form, book_form=book_form)
)
def get_context_data(self, **kwargs):
""" Add formset and formhelper to the context_data. """
ctx = super(AuthorCreateView, self).get_context_data(**kwargs)
book_formhelper = BookFormHelper()
if self.request.POST:
ctx['form'] = AuthorForm(self.request.POST)
ctx['book_form'] = BookFormset(self.request.POST)
ctx['book_formhelper'] = book_formhelper
else:
ctx['form'] = AuthorForm()
ctx['book_form'] = BookFormset()
ctx['book_formhelper'] = book_formhelper
return ctx
urls.py
from django.conf.urls import patterns, url
from django.views.generic import TemplateView
from library.views import AuthorCreateView
urlpatterns = patterns('',
url(r'^author/manage$', AuthorCreateView.as_view(), name='handle-books'),
url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
)
manage_books.HTML-код
{% load crispy_forms_tags %}
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>
<div class='container'>
<form method='post'>
{% crispy form %}
{{ book_form.management_form }}
{{ book_form.non_form_errors }}
{% crispy book_form book_formhelper %}
<input class='btn btn-primary' type='submit' value='Save'>
</form>
<div>
обратите внимание:
- это простой runable пример который использует
inlineformset_factory
feature и Django общие представления на основе классов - я assumming
django-crispy-forms
установлен, и это правильно сконфигурированный. - хранилище кода хранится в: https://bitbucket.org/slackmart/library_example
Я знаю, что это больше кода, что показали решения,но начать использовать представления на основе классов Django отлично.
я публикую свои окончательные решения, согласно обширному помощнику, предоставленному Onyeka.
ниже я публикую решения для добавления и редактирования использования inlineformset_factory Django, используя пример автора и книги, найденный в документах Django.
во-первых, добавление объекта автора, с 3 дополнительными объектами книги, которые будут добавлены.
очевидно, это входит в ваш views.py
def add_author(request):
'''This function creates a brand new Author object with related Book objects using inlineformset_factory'''
author = Author()
author_form = AuthorModelForm(instance=author) # setup a form for the parent
BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',))
if request.method == "POST":
author_form = AuthorModelForm(request.POST)
formset = BookInlineFormSet(request.POST, request.FILES)
if author_form.is_valid():
created_author = author_form.save(commit=False)
formset = BookInlineFormSet(request.POST, request.FILES, instance=created_author)
if formset.is_valid():
created_author.save()
formset.save()
return HttpResponseRedirect(created_author.get_absolute_url())
else:
author_form = AuthorModelForm(instance=author)
formset = BookInlineFormSet()
return render(request, "add_author.html", {
"author_form": author_form,
"formset": formset,
})
def edit_author(request, author_id):
'''This function edits an Author object and its related Book objects using inlineformset_factory'''
if id:
author = Author.objects.get(pk=author_id) # if this is an edit form, replace the author instance with the existing one
else:
author = Author()
author_form = AuthorModelForm(instance=author) # setup a form for the parent
BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',))
formset = BookInlineFormSet(instance=author)
if request.method == "POST":
author_form = AuthorModelForm(request.POST)
if id:
author_form = AuthorModelForm(request.POST, instance=author)
formset = BookInlineFormSet(request.POST, request.FILES)
if author_form.is_valid():
created_author = author_form.save(commit=False)
formset = BookInlineFormSet(request.POST, request.FILES, instance=created_author)
if formset.is_valid():
created_author.save()
formset.save()
return HttpResponseRedirect(created_author.get_absolute_url())
return render(request, "edit_author.html", {
"author_id": author_id, # This author_id is referenced
# in template for constructing the posting url via {% url %} tag
"author_form": author_form,
"formset": formset,
})
эта часть входит в ваш urls.py, предполагая, что представления были импортированы, и urlpatterns уже построены.
...
url(r'^add/book/$', views.add_author, name='add_author'),
url(r'^edit/(?P<author_id>[\d]+)$', views.edit_author, name='edit_author'),
...
теперь к части шаблонов. Редактировать Автор шаблона объекта (edit_author.html) выглядит так (не применяется стиль)
<form action="{% url 'edit_book' author_id %}" method="POST" >
<!-- See above: We're using the author_id that was passed to template via views render of the edit_author(...) function -->
{% csrf_token %} <!-- You're dealing with forms. csrf_token must come -->
{{ author_form.as_p }}
{{ formset.as_p }}
<input type="submit" value="submit">
</form>
чтобы добавить новый объект автора через шаблон (add_author.HTML-код):
<form action="." method="POST" >{% csrf_token %}
{{ author_form.as_p }}
{{ formset.as_p }}
<input type="submit" value="submit">
</form>
Примечание:
действие='."может показаться, что это дешевый способ построения url-адреса, при котором форма отправляет данные формы на ту же страницу. С этим пример использования действия='.- для редактора.html шаблон всегда получил форму, опубликованную в /edit / вместо /edit /1 или/edit / 2
построение url-адреса с помощью {%url 'edit_author' author_id %} обеспечивает форму всегда сообщения на правый url. Неспособность использовать {%url %} стоила мне много часов и проблем.
большое спасибо Onyeka.
Я сделал именно то, что вы пытаетесь : https://github.com/yakoub/django_training/tree/master/article
необходимо создать отдельную форму с помощью атрибута prefix . тогда при сохранении нужно перебрать все книги и связать их с автором вы только что создали .