首页 文章

inlineformset_factory在创建后创建新对象并编辑对象

提问于
浏览
10

在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创建一个新对象,比如作者,与多个图书相关联,这是什么方法?

使用django docs上面的Author Book模型的示例将很有帮助 . django docs仅提供了如何使用inlineformset_factory编辑已创建对象但未创建新对象的示例

4 回答

  • 6

    我一开始没有正确地阅读你的问题 . 您还需要呈现父模型的表单 . 我没有对此进行过测试,我已经完成了之前我所做过的事情以及之前关联的答案,但它应该可行 .

    UPDATE

    如果您同时使用视图并进行编辑,则应首先检查作者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,
        })
    
  • 2

    我已经使用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>
    

    Notice:

    • 这是一个使用 inlineformset_factory 功能和Django通用基于类的视图的简单可运行示例

    • 我正在安装 django-crispy-forms 已安装,并且已正确配置 .

    • 代码存储库托管在:https://bitbucket.org/slackmart/library_example

    我知道显示解决方案的代码更多,但是开始使用Django基于类的视图非常棒 .

  • 1

    根据Onyeka提供的广泛助手,我发布了我的最终解决方案 .

    下面我使用Django Docs中的Author和Book示例发布使用Django的inlineformset_factory的Add和Edit解决方案 .

    首先,添加作者对象,附加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 ,假设已导入视图,并且已经构造了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>
    

    注意:

    使用action =' . '似乎是一种构建url的廉价方式,因此表单将表单数据发布到同一页面 . 在这个例子中,使用action =' . '对于edit_author.html模板,始终将表单发布到/ edit /而不是/ edit / 1或/ edit / 2

    使用{%url 'edit_author' author_id%}构建网址可确保将表单 always 发布到正确的网址 . 没有使用{%url%}会花费我很多时间和麻烦 .

    非常感谢Onyeka .

  • 8

    我做了你正在尝试的事情:https://github.com/yakoub/django_training/tree/master/article

    您需要使用prefix属性创建单独的表单 . 然后当你保存时,你需要遍历所有书籍并将它们与你刚创建的作者联系起来 .

相关问题