inlineformset_factory 创建新对象并在创建后编辑对象
inlineformset_factory create new objects and edit objects after created
在 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,
})
通过以上,它只渲染了没有父模型的内联模型。
要使用 inlineformset_factory 创建一个关联多本书的新对象,比如 Author,方法是什么?
使用来自 django 文档的上述 Author Book 模型的示例将会有所帮助。 django 文档仅提供了如何使用 inlineformset_factory 编辑 已创建对象的示例,但没有提供 创建新对象
的示例
起初我没有正确阅读你的问题。您还需要呈现父模型的表单。我还没有测试过这个,我将放弃我之前所做的和之前链接的答案,但它应该有效。
更新
如果您同时使用视图进行编辑,则应首先检查作者 ID。如果没有 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,
})
我完全按照您的要求做了:
https://github.com/yakoub/django_training/tree/master/article
您需要使用前缀属性创建单独的表单。
然后当您保存时,您需要遍历所有书籍并将它们与您刚刚创建的作者相关联。
我正在 post 根据 Onyeka 提供的广泛助手,我正在制定我的最终解决方案。
下面我 post 使用 Django 的 inlineformset_factory 添加和编辑解决方案,使用 Django 文档中的 Author 和 Book 示例。
首先,Author 对象的添加,要追加 3 个 Book 对象。
显然,这会进入您的 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,假设视图已经导入,url模式已经构建。
...
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>
通过模板添加全新的 Author 对象 (add_author.html):
<form action="." method="POST" >{% csrf_token %}
{{ author_form.as_p }}
{{ formset.as_p }}
<input type="submit" value="submit">
</form>
注意:
使用动作='.'可能看起来是构建 url 的廉价方法,其中表单 post 将表单数据发送到同一页面。在这个例子中,使用 action='.'因为 edit_author.html 模板总是得到 posted 到 /edit/ 而不是 /edit/1 或 /edit/2
的形式
使用 {% url 'edit_author' author_id %} 构造 url 确保形式 always posts 向右 url。未能使用 {% url %} 花费了我很多时间和麻烦。
非常感谢 Onyeka。
我已经使用 Django Class-Based Views 完成了这项工作。
这是我的方法:
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>
注意事项:
- 这是一个使用
inlineformset_factory
的简单可运行示例
功能和 Django 通用 Class-基于视图
- 我假设
django-crispy-forms
已经安装,而且是正确的
已配置。
- 代码存储库位于:https://bitbucket.org/slackmart/library_example
我知道显示的解决方案需要更多代码,但开始使用 Django Class-基于视图非常棒。
在 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,
})
通过以上,它只渲染了没有父模型的内联模型。
要使用 inlineformset_factory 创建一个关联多本书的新对象,比如 Author,方法是什么?
使用来自 django 文档的上述 Author Book 模型的示例将会有所帮助。 django 文档仅提供了如何使用 inlineformset_factory 编辑 已创建对象的示例,但没有提供 创建新对象
的示例起初我没有正确阅读你的问题。您还需要呈现父模型的表单。我还没有测试过这个,我将放弃我之前所做的和之前链接的答案,但它应该有效。
更新
如果您同时使用视图进行编辑,则应首先检查作者 ID。如果没有 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,
})
我完全按照您的要求做了: https://github.com/yakoub/django_training/tree/master/article
您需要使用前缀属性创建单独的表单。 然后当您保存时,您需要遍历所有书籍并将它们与您刚刚创建的作者相关联。
我正在 post 根据 Onyeka 提供的广泛助手,我正在制定我的最终解决方案。
下面我 post 使用 Django 的 inlineformset_factory 添加和编辑解决方案,使用 Django 文档中的 Author 和 Book 示例。
首先,Author 对象的添加,要追加 3 个 Book 对象。
显然,这会进入您的 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,假设视图已经导入,url模式已经构建。
...
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>
通过模板添加全新的 Author 对象 (add_author.html):
<form action="." method="POST" >{% csrf_token %}
{{ author_form.as_p }}
{{ formset.as_p }}
<input type="submit" value="submit">
</form>
注意:
使用动作='.'可能看起来是构建 url 的廉价方法,其中表单 post 将表单数据发送到同一页面。在这个例子中,使用 action='.'因为 edit_author.html 模板总是得到 posted 到 /edit/ 而不是 /edit/1 或 /edit/2
的形式使用 {% url 'edit_author' author_id %} 构造 url 确保形式 always posts 向右 url。未能使用 {% url %} 花费了我很多时间和麻烦。
非常感谢 Onyeka。
我已经使用 Django Class-Based Views 完成了这项工作。
这是我的方法:
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>
注意事项:
- 这是一个使用
inlineformset_factory
的简单可运行示例 功能和 Django 通用 Class-基于视图 - 我假设
django-crispy-forms
已经安装,而且是正确的 已配置。 - 代码存储库位于:https://bitbucket.org/slackmart/library_example
我知道显示的解决方案需要更多代码,但开始使用 Django Class-基于视图非常棒。