You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django新手求助:如何在同一表单创建父、子模型对象?

在Django中同一表单创建父-子模型对象的解决方案

嘿,我明白你想要在一个表单里同时创建Goodsreceivednote(父模型)和关联的Goodsreceiveitem(子模型)的需求,而且不想用那种依赖日期找最新父对象的不太靠谱的方法。其实InlineFormSet完全可以直接实现这个需求,关键是在视图里调整保存逻辑,不用先创建父对象再关联子项。下面一步步来教你怎么做:

1. 定义表单与InlineFormSet

首先,我们需要为父模型创建一个普通的ModelForm,然后用inlineformset_factory生成子模型的表单集:

# forms.py
from django import forms
from .models import Goodsreceivednote, Goodsreceiveitem

class GoodsReceivedNoteForm(forms.ModelForm):
    class Meta:
        model = Goodsreceivednote
        fields = ['rec_note_id', 'note_date', 'type']

# 生成子模型的InlineFormSet
GoodsReceiveItemFormSet = forms.inlineformset_factory(
    Goodsreceivednote,  # 父模型
    Goodsreceiveitem,   # 子模型
    fields=['received_item_id', 'item_id', 'item_qty'],
    extra=3,  # 默认显示3条子项输入框,可根据需求调整
    can_delete=False  # 如果允许删除子项,设为True
)

2. 视图中的处理逻辑

核心思路是:先验证父表单,不立即保存,拿到父对象实例后,再用这个实例初始化子表单集并验证,最后一起保存父对象和子项。

# views.py
from django.shortcuts import render, redirect
from .forms import GoodsReceivedNoteForm, GoodsReceiveItemFormSet

def create_grn(request):
    if request.method == 'POST':
        grn_form = GoodsReceivedNoteForm(request.POST)
        # 注意:这里先不传instance,因为父对象还没创建
        item_formset = GoodsReceiveItemFormSet(request.POST)
        
        if grn_form.is_valid() and item_formset.is_valid():
            # 先保存父对象,但commit=False不写入数据库
            grn = grn_form.save(commit=False)
            # 如果你需要自动生成rec_note_id(比如不用用户输入),可以在这里处理
            # 比如 grn.rec_note_id = f"GRN-{datetime.now().strftime('%Y%m%d%H%M%S')}"
            grn.save()
            
            # 现在把父对象关联到子表单集,然后保存子项
            items = item_formset.save(commit=False)
            for item in items:
                item.rec_note_id = grn  # 关联父对象
                item.save()
            
            return redirect('grn_list')  # 替换成你的成功跳转页面
    else:
        # GET请求时,初始化空表单和表单集
        grn_form = GoodsReceivedNoteForm()
        item_formset = GoodsReceiveItemFormSet()
    
    return render(request, 'create_grn.html', {
        'grn_form': grn_form,
        'item_formset': item_formset
    })

3. 模板渲染表单

在模板里,你需要同时渲染父表单和子表单集,别忘了渲染表单集的management_form(Django用来管理表单集的隐藏字段,必须包含):

<!-- templates/create_grn.html -->
<form method="post">
    {% csrf_token %}
    
    <h3>收货单信息</h3>
    {{ grn_form.as_p }}
    
    <h3>收货明细</h3>
    {{ item_formset.management_form }}
    {% for form in item_formset %}
        {{ form.as_table }}
        <hr>
    {% endfor %}
    
    <button type="submit">保存</button>
</form>

注意事项

  • 你的父模型用rec_note_id作为CharField主键,要确保这个字段的值唯一:要么让用户输入唯一值,要么在视图里自动生成(比如结合日期时间戳),避免保存时出现主键重复错误。
  • 如果需要动态添加子项表单(比如用户点击按钮新增一行),可以用JavaScript实现,Django官方文档里有相关的示例思路。
  • 子模型的received_item_id也是CharField主键,同样要保证唯一性,或者考虑改成自增主键(AutoField)会更省心。

这样就能在同一个表单里完成父对象和子对象的创建,完全不需要先创建父对象再关联,也不用依赖日期查询的方法~

内容的提问来源于stack exchange,提问作者Clueless

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:38:16