首页 > 解决方案 > Django 多对多字段和表单

问题描述

我正在编写我的第一个 Django 应用程序,我的models.py文件中有两个类,成分和膳食。一种食物有很多成分,您应该能够设置膳食中每种成分的数量,所以这就是我的models.py

class Ingredient(models.Model):
    name = models.CharField(max_length=200)

class Meal(models.Model):
    name = models.CharField(max_length=200)
    ingredients = models.ManyToManyField(Ingredient, through='Recipe_Ingredient')

class Recipe_Ingredient(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
    ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
    quantity = models.FloatField()
    UNITY_CHOICES = (
        ('g', 'Gram(s)'),
        ('kg', 'Kilogram(s)'),
        ('l', 'Liter(s)'),
        ('cl', 'Centiliter(s)'),
    )

    quantityUnit = models.CharField(
        max_length=2,
        choices=UNITY_CHOICES,
        default='g',
)

Recipe_Ingrent 类是用于存储数量的 ManyToMany 关联表。

这里的问题是我不知道在这种情况下如何使用表单。我有一个模板meal_edit.html,它在form.py.

要添加成分,我的表格很简单,如下所示:

from .models import Ingredient

class IngridientForm(forms.ModelForm):
    class Meta:
        model = Ingredient
        fields = ('name',)

那么,我应该如何制作一个表格来创建一个包含多种成分和每种成分数量的食谱?

标签: pythondjangoforms

解决方案


Django 表单集:

从本质上讲,每个成分都是您较大表单中的一个表单(尽管<form您的模板中只有一个标签。例如:

模型.py:

class Ingredient(models.Model):
    name = models.CharField(max_length=200)

class Meal(models.Model):
    name = models.CharField(max_length=200)

class Recipe_Ingredient(models.Model):
    meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE) # Not sure what this is, but I'll leave that to you
    ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
    quantity = models.FloatField()
    UNITY_CHOICES = (
        ('g', 'Gram(s)'),
        ('kg', 'Kilogram(s)'),
        ('l', 'Liter(s)'),
        ('cl', 'Centiliter(s)'),
    )

    quantityUnit = models.CharField(
        max_length=2,
        choices=UNITY_CHOICES,
        default='g',
    )

表格.py:

from django import forms
from your_app import models

class IngredientForm(forms.ModelForm):

    class Meta:
        model = models.Recipe_Ingredient
        fields = ['ingredient', 'quantity', 'quantityUnit']


IngredientFormset = forms.inlineformset_factory(models.Meal, models.Recipe_Ingredient, form=IngredientForm, extra=2)

视图.py:

from your_app import forms, models

class YourMealModelView():

    model = models.Meal

    def get_context_data(self, *args, **kwargs):
        context_data = super().get_context_data(**kwargs)

        if self.request.method == 'POST':
            context_data['ingredients_formset'] = forms.IngredientFormset(
                self.request.POST,
                instance=self.object,   # as in your Meal object
                prefix='ingredients'
            )
        else:
            context_data['ingredients_formset'] = forms.IngredientFormset(
                instance=self.object, prefix='ingredients'
            )

    def form_valid(self, form):
        # You can manipulate the formsets here as well:
        context_data = self.get_context_data()
        ingredients_formset = context_data['ingredients_formset']

        meal = form.save()

        for i_form in ingredients_formset:
            delete = i_form.cleaned_data.get('DELETE')
            if delete:
                i_form.instance.delete()
                continue

            # do stuff and save
            ingredient_recipe = i_form.save(commit=False)
            ingredient_recipe.meal = meal
            ingredient_recipe.save()

模板:

<form id="meal_form">
    {% for field in form.fields %}
        {{ field.label }}
        {{ field }}
    {% endfor %}

    <table>
        <thead>
            {{ ingredients_formset.management_form }}
            <tr>
                <th>Ingredient</th>
                <th>Quantity</th>
                <th>Units</th>
                <th>Delete?</th>
            </tr>
        </thead>
        <tbody>
            {% for i_form in ingredients_formset.forms %}
                <tr id="{{ i_form.prefix }}">
                    {{ i_form.id }}
                    <td>{{ i_form.ingredient }}</td>
                    <td>{{ i_form.quantity }}</td>
                    <td>{{ i_form.quantityUnit }}</td>
                    <td>{% if i_form.instance.pk %}{{i_form.DELETE %}{% endif %}</td>
                </tr>
            {% endfor %}
         </tbody>
    </table>
</form>

当然,我遗漏了很多东西,比如使用 javascript 向表单集添加新项目,如何处理新餐点与编辑现有餐点,以及使用什么 Django View 等,但这给了你一个想法。


推荐阅读