使用django的嵌套表单

Shi*_*dla 4 javascript django multiple-instances django-forms

我有功能,我需要使用以下模型实现嵌套的django表单

class Publisher(models.Model):
    name = models.CharField(max_length=256)
    address1 = models.CharField(max_length=256)
    address2 = models.CharField(max_length=256)
    city = models.CharField(max_length=256)

class Author(models.Model):
    publisher = models.ForeignKey(Publisher) 
    name = models.CharField(max_length=256)
    address = models.CharField(max_length=256)

class Book(models.Model):
    author = models.ForeignKey(Author)
    name = models.CharField(max_length=256)
    price = models.FloatField()
Run Code Online (Sandbox Code Playgroud)

forms.py

class PublisherForm(ModelForm):
    class Meta:
        model = Publisher

    def __init__(self, *args, **kwargs):

        super(PublisherForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Publisher Name', 'autofocus':'autofocus'}
        self.fields['address'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Publisher Address '}


class AuthorForm(ModelForm):
    class Meta:
        model = Author
        exclude = ('publisher',)    

    def __init__(self, *args, **kwargs):

        super(AuthorForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Author Name'}
        self.fields['address'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Author Address'}

class BookForm(ModelForm):
    class Meta:
        model = Book
        exclude = ('author',)    

    def __init__(self, *args, **kwargs):

        super(BookForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Book Name'}
        self.fields['price'].widget.attrs = {'id':'inputIcon', 'class':'input-block', 'placeholder':'Book Price'}
Run Code Online (Sandbox Code Playgroud)

因此,使用上述模型和表单,我需要在同一屏幕上动态创建表单,如下面的UI屏幕

在此输入图像描述

因此,从上面的屏幕中,我们可以观察到所有三个模型表单应该显示在同一页面上.

1. The publisher may have many authors
2. Each author may have many books
Run Code Online (Sandbox Code Playgroud)

你也可以从设计上观察,我们有两个按钮

1.Add Another Author -  Adding Multiple Authors
2.Add Book - Adding multiple books for Author
Run Code Online (Sandbox Code Playgroud)

2.添加书籍

当我们点击Add Book时,应该在屏幕截图中创建一个新的Book表单

1.添加另一位作者

当我们点击Add another author按钮时,应该显示一个新的作者记录,并且他可以通过单击为该作者添加多个书籍,如上所述Add Book

如果我们只有两个模型A和B,并且如果B有ForeignKeyA,那么我们可以通过使用django来实现这个功能formsets or inline_formsets or model_formsets,但是在上面我们应该能够

  1. 添加嵌套(多个)Book表单Author
  2. Author为Publisher 添加嵌套(多个)表单

那么如何实现上述功能?,我已经搜索了很多,但无法弄清楚上面的内容

jul*_*sar 6

这可以通过使用inlineformsets来完成,在创建发布者的视图中,返回作者和书籍表单集(使用每个表单的differente前缀参数),然后使用javascript为书籍和作者添加新表单空表单.

贝娄是我为你编码的基本样本.

诀窍是使用JavaScript与相关父作者(动态表单前缀模板生成书表单集books_formset_0,books_formset_1...),然后透过形式,遍历每个作者找到相关book_formset.

可以在此处下载运行和测试此代码的完整django项目.

重要说明:以下代码尚未优化,不使用某些标准工具,如js模板,ajax等,但它可以工作并显示如何解决问题.

template.py:

<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.js"></script>
<script type="text/javascript">
    $(function () {
        $('form').delegate('.btn_add_book', 'click', function () {
            var $this = $(this)
            var author_ptr = $this.attr('id').split('-')[1]
            var $total_author_books = $(':input[name=books_formset_' + author_ptr + '-TOTAL_FORMS]');
            var author_book_form_count = parseInt($total_author_books.val())
            $total_author_books.val(author_book_form_count + 1)

            var $new_book_form = $('<fieldset class="author_book_form">' +
                '<legend>Book</legend>' +
                '<p>' +
                '<label for="id_books_formset_' + author_ptr + '-' + author_book_form_count + '-name">Name:</label>' +
                '<input id="id_books_formset_' + author_ptr + '-' + author_book_form_count + '-name" maxlength="256" name="books_formset_' + author_ptr + '-' + author_book_form_count + '-name" type="text" />' +
                '<input id="id_books_formset_' + author_ptr + '-' + author_book_form_count + '-author" name="books_formset_' + author_ptr + '-' + author_book_form_count + '-author" type="hidden" />' +
                '<input id="id_books_formset_' + author_ptr + '-' + author_book_form_count + '-id" name="books_formset_' + author_ptr + '-' + author_book_form_count + '-id" type="hidden" />' +
                '</p>' +
                '</fieldset>'
            )

            $this.parents('.author_form').find('.author_books').prepend($new_book_form)
        })

        $('form').delegate('#btn_add_author', 'click', function () {
            var $total_authors = $(':input[name=authors_formset-TOTAL_FORMS]');
            author_form_count = parseInt($total_authors.val())
            $total_authors.val(author_form_count + 1)

            book_form = '<fieldset class="author_book_form">' +
                '<legend>Book</legend>' +
                '<p>' +
                '<label for="id_books_formset_' + author_form_count + '-0-name">Name:</label>' +
                '<input id="id_books_formset_' + author_form_count + '-0-name" maxlength="256" name="books_formset_' + author_form_count + '-0-name" type="text" />' +
                '<input id="id_books_formset_' + author_form_count + '-0-author" name="books_formset_' + author_form_count + '-0-author" type="hidden" />' +
                '<input id="id_books_formset_' + author_form_count + '-0-id" name="books_formset_' + author_form_count + '-0-id" type="hidden" />' +
                '</p>' +
                '</fieldset>';

            $new_author_form = $(
                '<fieldset class="author_form">' +
                '<legend>Author</legend>' +
                '<p>' +
                '<label for="id_authors_formset-' + author_form_count + '-name">Name:</label>' +
                '<input id="id_authors_formset-' + author_form_count + '-name" maxlength="256" name="authors_formset-' + author_form_count + '-name" type="text" />' +
                '<input id="id_authors_formset-' + author_form_count + '-publisher" name="authors_formset-' + author_form_count + '-publisher" type="hidden" />' +
                '<input id="id_authors_formset-' + author_form_count + '-id" name="authors_formset-' + author_form_count + '-id" type="hidden" />' +
                '</p>' +
                '<p><input type="button" value="Add Book" class="btn_add_book" id="author-' + author_form_count + '"/></p>' +
                '<div class="author_books">' +
                '<input id="id_books_formset_' + author_form_count + '-TOTAL_FORMS" name="books_formset_' + author_form_count + '-TOTAL_FORMS" type="hidden" value="1" />' +
                '<input id="id_books_formset_' + author_form_count + '-INITIAL_FORMS" name="books_formset_' + author_form_count + '-INITIAL_FORMS" type="hidden" value="0" />' +
                '<input id="id_books_formset_' + author_form_count + '-MAX_NUM_FORMS" name="books_formset_' + author_form_count + '-MAX_NUM_FORMS" type="hidden" value="1000" />' +
                book_form +
                '</div >' +
                '</fieldset >'
            )

            $('#authors').prepend($new_author_form)
        })
    })
</script>
<h1>Add Publisher</h1>
<form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <p><input type="button" id="btn_add_author" value="Add another author"/></p>

    <div id="authors">
        {{ authors_formset.management_form }}
        {% for form in authors_formset %}
            <fieldset class="author_form">
                <legend>Author</legend>
                {{ form.as_p }}
                <p><input type="button" value="Add Book" class="btn_add_book" id="author-{{ forloop.counter0 }}"/></p>

                <div class="author_books">
                    {{ books_formset.management_form }}
                    {% for form in books_formset %}
                        <fieldset class="author_book_form">
                            <legend>Book</legend>
                            {{ form.as_p }}
                        </fieldset>
                    {% endfor %}
                </div>
            </fieldset>
        {% endfor %}
    </div>
    <p><input type="submit" value="Save"></p>
</form>
Run Code Online (Sandbox Code Playgroud)

forms.py:

AuthorInlineFormSet = inlineformset_factory(Publisher, Author, extra=1, can_delete=False)
BookInlineFormSet = inlineformset_factory(Author, Book, extra=1, can_delete=False)
Run Code Online (Sandbox Code Playgroud)

views.py:

class PublisherCreateView(CreateView):
    model = Publisher

    def form_valid(self, form):
        result = super(PublisherCreateView, self).form_valid(form)

        authors_formset = AuthorInlineFormSet(form.data, instance=self.object, prefix='authors_formset')
        if authors_formset.is_valid():
            authors = authors_formset.save()

        authors_count = 0
        for author in authors:
            books_formset = BookInlineFormSet(form.data, instance=author, prefix='books_formset_%s' % authors_count)
            if books_formset.is_valid():
                books_formset.save()
            authors_count += 1

        return result

    def get_context_data(self, **kwargs):
        context = super(PublisherCreateView, self).get_context_data(**kwargs)
        context['authors_formset'] = AuthorInlineFormSet(prefix='authors_formset')
        context['books_formset'] = BookInlineFormSet(prefix='books_formset_0')
        return context
Run Code Online (Sandbox Code Playgroud)