简体   繁体   English

django 嵌套表单

[英]Nested forms with django

I have functionality where i need to implement nested django forms with the below models我有需要使用以下模型实现嵌套 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()

forms.py表格.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'}

So with the above models and forms, i need to create the forms dynamically on the same screen like in the UI screen below因此,对于上述模型和表单,我需要像下面的 UI 屏幕一样在同一屏幕上动态创建表单

在此处输入图片说明

So from the above screen, we can observe all the three model forms should displayon the same page.因此,从上面的屏幕中,我们可以观察到所有三个模型表单都应该显示在同一页面上。

1. The publisher may have many authors
2. Each author may have many books

Also you can observe from the design, we have two button for您也可以从设计中观察到,我们有两个按钮用于

1.Add Another Author -  Adding Multiple Authors
2.Add Book - Adding multiple books for Author

2. Add Book 2. 添加书籍

When we click on Add Book, a new Book form should be created as in the screenshot当我们点击 Add Book 时,应该创建一个新的 Book 表单,如屏幕截图所示

1. Add another Author 1. 添加另一个作者

When we click on Add another author button a new Author record should be displayed and he can able to add multiple Books for this author same as above by clicking on Add Book当我们点击Add another author按钮时,应该显示一个新的 Author 记录,他可以通过点击Add Book为这个作者添加多本书,与上面相同

If we have only two models A and B, and if B has ForeignKey to A, then we could be able to achive this functionlaity by usign django formsets or inline_formsets or model_formsets , but here in the above we should be able to如果我们只有两个模型 A 和 B,并且如果 B 有ForeignKey到 A,那么我们可以通过使用 django formsets or inline_formsets or model_formsets来实现此功能,但在上面我们应该能够

  1. Add nested(multiple) Book forms for AuthorAuthor添加嵌套(多个) Book表单
  2. Add nested(multiple) Author forms for Publisher为 Publisher 添加嵌套(多个) Author表单

So how to achieve the above functionality ?, i have searched a lot, but could n't able to figure out the above stuff那么如何实现上述功能?,我搜索了很多,但无法弄清楚上面的东西

This can be done by playing with inline formsets , in the view of create a publisher, returns the authors and books formsets (using differente prefix parameters for each forms), then use javascript to add new forms empty forms for books and authors.这可以通过使用内联表单集来完成,在创建出版商的视图中,返回作者和书籍表单集(对每个表单使用不同的前缀参数),然后使用 javascript 为书籍和作者添加新表单空表单。

Bellow is a basic sample I coded for you. Bellow 是我为您编写的基本示例。

The trick is to use javascript to generate book formsets in templates with dynamic form prefixes related to the parent author ( books_formset_0 , books_formset_1 , ...), then on sumbit the form, iterate for each author to find the related book_formset.诀窍是使用 javascript 在模板中生成与父作者相关的动态表单前缀( books_formset_0books_formset_1 ,...),然后在 sumbit 表单中,为每个作者迭代以找到相关的 book_formset。

A complete django project to run and test this code can be downloaded here .可以在此处下载运行和测试此代码的完整 django 项目。

IMPORTANT: The following code hasn't been optimized and not use some standards tools like js templates, ajax, etc, but it works and shows how to solve the problem.重要提示:以下代码未经优化,未使用一些标准工具,如 js 模板、ajax 等,但它有效并显示了如何解决问题。

template.py:模板.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>

forms.py:表格.py:

AuthorInlineFormSet = inlineformset_factory(Publisher, Author, extra=1, can_delete=False)
BookInlineFormSet = inlineformset_factory(Author, Book, extra=1, can_delete=False)

views.py:视图.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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM