Django复制Recipe模型时IngredientQuantite内联表单数据无法保存
问题原因分析
当你为内联表单设置instance=original_recipe时,表单集绑定的是原食谱关联的已存在的IngredientQuantite数据库实例。提交表单时,Django会尝试更新这些原有实例,而非创建新实例关联到新生成的Recipe对象。这就导致新食谱无法绑定任何新的IngredientQuantite数据,甚至可能意外修改原食谱的食材用量。
移除instance参数后,表单集处于空白初始状态,提交时会创建全新的IngredientQuantite实例并关联到新食谱,因此数据能正常保存,但无法预填原食谱的食材数据。
修复方案
方案1:通过initial参数预填内联数据(推荐)
这种方式利用表单的initial参数传递原食谱的食材数据,不绑定原实例,提交时直接创建关联新食谱的新实例:
from django.shortcuts import get_object_or_404, render, redirect from .forms import RecipeForm, IngredientQuantiteFormset from .models import Recipe class DuplicateRecipe(View): def get(self, request, pk): original = get_object_or_404(Recipe, pk=pk) # 预填食谱主表单 recipe_form = RecipeForm(instance=original) # 生成内联表单的初始数据 ingredient_initial = [ { 'ingredient': item.ingredient.id, 'quantity': item.quantity, 'unit': item.unit } for item in original.ingredientquantite_set.all() ] # 初始化内联表单集,不设置instance,用initial传预填数据 ingredient_formset = IngredientQuantiteFormset(initial=ingredient_initial) return render(request, 'recipes/duplicate.html', { 'recipe_form': recipe_form, 'ingredient_formset': ingredient_formset }) def post(self, request, pk): original = get_object_or_404(Recipe, pk=pk) # 创建新的Recipe实例,不绑定原实例 recipe_form = RecipeForm(request.POST, instance=Recipe()) if recipe_form.is_valid(): new_recipe = recipe_form.save() # 绑定内联表单集到新食谱,保存数据 ingredient_formset = IngredientQuantiteFormset(request.POST, instance=new_recipe) if ingredient_formset.is_valid(): ingredient_formset.save() return redirect('recipe_detail', pk=new_recipe.pk) # 验证失败时返回表单(可优化错误提示) ingredient_formset = IngredientQuantiteFormset(request.POST) return render(request, 'recipes/duplicate.html', { 'recipe_form': recipe_form, 'ingredient_formset': ingredient_formset })
核心逻辑:
- GET请求时,用
initial给内联表单传原食谱的食材数据,避免绑定原实例 - POST请求时,内联表单集的
instance设置为刚创建的新食谱,确保保存的是关联新食谱的新实例
方案2:手动复制并关联内联实例
如果使用ModelFormSet,可以复制原食谱的IngredientQuantite实例,解除与原食谱的关联后作为表单初始数据,提交时再关联到新食谱:
from django.forms import modelformset_factory from django.shortcuts import get_object_or_404, render, redirect from .models import Recipe, IngredientQuantite # 定义ModelFormSet IngredientQuantiteFormset = modelformset_factory( IngredientQuantite, fields=('ingredient', 'quantity', 'unit'), extra=0 ) class DuplicateRecipe(View): def get(self, request, pk): original = get_object_or_404(Recipe, pk=pk) recipe_form = RecipeForm(instance=original) # 复制原食材实例,解除与原食谱的关联 copied_ingredients = [] for item in original.ingredientquantite_set.all(): copied_item = IngredientQuantite( ingredient=item.ingredient, quantity=item.quantity, unit=item.unit, recipe=None # 关键:解除原关联 ) copied_ingredients.append(copied_item) # 初始化表单集,用空queryset,手动绑定复制的实例 ingredient_formset = IngredientQuantiteFormset(queryset=IngredientQuantite.objects.none()) for form, item in zip(ingredient_formset, copied_ingredients): form.instance = item return render(request, 'recipes/duplicate.html', { 'recipe_form': recipe_form, 'ingredient_formset': ingredient_formset }) def post(self, request, pk): original = get_object_or_404(Recipe, pk=pk) recipe_form = RecipeForm(request.POST) if recipe_form.is_valid(): new_recipe = recipe_form.save() ingredient_formset = IngredientQuantiteFormset(request.POST) if ingredient_formset.is_valid(): # 手动将每个食材实例关联到新食谱并保存 for form in ingredient_formset: if form.cleaned_data: item = form.save(commit=False) item.recipe = new_recipe item.save() return redirect('recipe_detail', pk=new_recipe.pk) ingredient_formset = IngredientQuantiteFormset(request.POST) return render(request, 'recipes/duplicate.html', { 'recipe_form': recipe_form, 'ingredient_formset': ingredient_formset })
这个方案适合需要保留原实例所有字段(包括隐藏字段)的场景,确保预填数据与原实例完全一致。
内容的提问来源于stack exchange,提问作者Readix
相关产品推荐
相关产品推荐

