Django形式具有多个模型和动态数量的模型

时间:2015-06-24 21:12:30

标签: django django-models django-forms django-rest-framework

我正在创建一个创建动态数量模型的表单。 FormSets似乎是工作的工具,但我找不到使用多个模型的示例。

使用this exampleAuthorBook)中的模型,我的表单集只需要一个Author表单,并且至少需要一个Book表单。用户可以向作者添加任意数量的书籍,所有书籍都在同一表格(集)中。

在Django后端实现这一目标的最佳方法是什么?

我最好将JSON发布到REST API以创建这些模型吗?

1 个答案:

答案 0 :(得分:2)

This example正是您要找的。它解释了如何使用内联表单集来填充父母和孩子的模型:

# models.py
from django.db import models

class Recipe(models.Model):
    title = models.CharField(max_length=255)
    description = models.TextField()


class Ingredient(models.Model):
    recipe = models.ForeignKey(Recipe)
    description = models.CharField(max_length=255)


class Instruction(models.Model):
    recipe = models.ForeignKey(Recipe)
    number = models.PositiveSmallIntegerField()
    description = models.TextField()

# forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory

from .models import Recipe, Ingredient, Instruction


class RecipeForm(ModelForm):
    class Meta:
        model = Recipe


IngredientFormSet = inlineformset_factory(Recipe, Ingredient)
InstructionFormSet = inlineformset_factory(Recipe, Instruction)

# views.py
from django.http import HttpResponseRedirect
from django.views.generic import CreateView

from .forms import IngredientFormSet, InstructionFormSet, RecipeForm
from .models import Recipe


class RecipeCreateView(CreateView):
    template_name = 'recipe_add.html'
    model = Recipe
    form_class = RecipeForm
    success_url = 'success/'

    def get(self, request, *args, **kwargs):
        """
        Handles GET requests and instantiates blank versions of the form
        and its inline formsets.
        """
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        ingredient_form = IngredientFormSet()
        instruction_form = InstructionFormSet()
        return self.render_to_response(
            self.get_context_data(form=form,
                                  ingredient_form=ingredient_form,
                                  instruction_form=instruction_form))

    def post(self, request, *args, **kwargs):
        """
        Handles POST requests, instantiating a form instance and its inline
        formsets with the passed POST variables and then checking them for
        validity.
        """
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        ingredient_form = IngredientFormSet(self.request.POST)
        instruction_form = InstructionFormSet(self.request.POST)
        if (form.is_valid() and ingredient_form.is_valid() and
            instruction_form.is_valid()):
            return self.form_valid(form, ingredient_form, instruction_form)
        else:
            return self.form_invalid(form, ingredient_form, instruction_form)

    def form_valid(self, form, ingredient_form, instruction_form):
        """
        Called if all forms are valid. Creates a Recipe instance along with
        associated Ingredients and Instructions and then redirects to a
        success page.
        """
        self.object = form.save()
        ingredient_form.instance = self.object
        ingredient_form.save()
        instruction_form.instance = self.object
        instruction_form.save()
        return HttpResponseRedirect(self.get_success_url())

    def form_invalid(self, form, ingredient_form, instruction_form):
        """
        Called if 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,
                                  ingredient_form=ingredient_form,
                                  instruction_form=instruction_form))

<!-- recipe_add.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Multiformset Demo</title>
    <script src="{{ STATIC_URL }}js/jquery.min.js"></script>
    <script src="{{ STATIC_URL }}js/jquery.formset.js"></script>
    <script type="text/javascript">
        $(function() {
            $(".inline.{{ ingredient_form.prefix }}").formset({
                prefix: "{{ ingredient_form.prefix }}",
            })
            $(".inline.{{ instruction_form.prefix }}").formset({
                prefix: "{{ instruction_form.prefix }}",
            })
        })
    </script>
</head>

<body>
    <div>
        <h1>Add Recipe</h1>
        <form action="." method="post">
            {% csrf_token %}
            <div>
                {{ form.as_p }}
            </div>
            <fieldset>
                <legend>Recipe Ingredient</legend>
                {{ ingredient_form.management_form }}
                {{ ingredient_form.non_form_errors }}
                {% for form in ingredient_form %}
                    {{ form.id }}
                    <div class="inline {{ ingredient_form.prefix }}">
                        {{ form.description.errors }}
                        {{ form.description.label_tag }}
                        {{ form.description }}
                    </div>
                {% endfor %}
            </fieldset>
            <fieldset>
                <legend>Recipe instruction</legend>
                {{ instruction_form.management_form }}
                {{ instruction_form.non_form_errors }}
                {% for form in instruction_form %}
                    {{ form.id }}
                    <div class="inline {{ instruction_form.prefix }}">
                        {{ form.number.errors }}
                        {{ form.number.label_tag }}
                        {{ form.number }}
                        {{ form.description.errors }}
                        {{ form.description.label_tag }}
                        {{ form.description }}
                    </div>
                {% endfor %}
            </fieldset>
            <input type="submit" value="Add recipe" class="submit" />
        </form>
    </div>
</body>
</html>

通过这种方式(假设您使用基于类的视图),它允许您在formset中定义所需的表单。

查看inlineformset_factory signature,了解如何定义每个表单集的表单数量。

要处理创建的动态表单数,您可以指定 extra,min_num和max_num

然后对于显示,在我看来,使用javascript是最方便的选择。 This是您可以找到答案的地方。