Django中含多对多中间表的CreateView实现问题求助
实现带自定义中间表的Django多对多CreateView
首先得先补全你的Unit模型——因为多对多的中间表一般要关联两边的模型,还得存额外信息(比如配料的用量),我先把它补全成常见的样子:
class Unit(models.Model): recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE) ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE) quantity = models.DecimalField(max_digits=5, decimal_places=2) # 比如配料的用量,克/毫升之类的 class Meta: unique_together = ('recipe', 'ingredient') # 避免同一个菜谱重复加同一个配料
接下来,因为Django默认的ModelForm处理不了带through参数的多对多关系(没法直接处理中间表的额外字段),所以我们需要用inline formset来处理中间表的表单,配合CreateView一起实现。
步骤1:定义表单和Inline Formset
先在forms.py里写对应的表单和表单集:
from django import forms from django.forms import inlineformset_factory from .models import Recipe, Unit, Ingredient class RecipeForm(forms.ModelForm): class Meta: model = Recipe fields = ['recipe_name'] # 只处理菜谱名称,配料部分交给formset # 创建Unit的inline表单集,关联到Recipe模型 UnitFormSet = inlineformset_factory( Recipe, Unit, fields=['ingredient', 'quantity'], # 中间表要填写的字段 extra=3, # 默认显示3个配料输入框,可根据需求调整 can_delete=False # 暂时不需要删除功能,后期可以打开 )
步骤2:重写CreateView的核心方法
在views.py里自定义CreateView,主要是要把formset传入上下文,并且在表单验证通过后先保存菜谱,再保存关联的配料数据:
from django.views.generic.edit import CreateView from .models import Recipe from .forms import RecipeForm, UnitFormSet class RecipeCreateView(CreateView): model = Recipe form_class = RecipeForm success_url = '/recipes/' # 替换成你实际的成功跳转路径 def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.POST: # 如果是POST请求,用POST数据初始化formset context['unit_formset'] = UnitFormSet(self.request.POST, instance=self.object) else: # GET请求,初始化空的formset context['unit_formset'] = UnitFormSet(instance=self.object) return context def form_valid(self, form): # 先保存菜谱实例,但不提交到数据库(commit=False) self.object = form.save(commit=False) self.object.save() # 获取formset并验证 unit_formset = UnitFormSet(self.request.POST, instance=self.object) if unit_formset.is_valid(): # 保存所有关联的配料数据 unit_formset.save() return super().form_valid(form)
步骤3:编写模板(templates/your_app/recipe_form.html)
模板里要同时显示菜谱表单和配料的formset,注意要渲染formset的管理表单(Django需要它来识别formset):
<form method="post"> {% csrf_token %} {{ form.as_p }} <h3>添加配料</h3> {{ unit_formset.management_form }} {% for form in unit_formset %} {{ form.as_table }} {% endfor %} <button type="submit">保存菜谱</button> </form>
最后配置URL
在urls.py里添加对应的路由:
from django.urls import path from .views import RecipeCreateView urlpatterns = [ path('recipe/new/', RecipeCreateView.as_view(), name='recipe-create'), # 其他路由... ]
这样一来,用户在创建菜谱的时候,就能同时添加多个带用量的配料了。如果需要支持动态添加更多配料框,可以用JS来实现,但基础功能这样就完全够用啦。
内容的提问来源于stack exchange,提问作者DavidMM
相关产品推荐
相关产品推荐

