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

Django中使用commit=False后如何保存多对多字段数据

Django多对多字段保存问题解决

问题背景

在Django项目中,使用ModelForm创建Vendor实例时,通过save(commit=False)暂存实例并设置created_by为当前登录用户,同时需要关联Session中传递的Branch(多对多字段),但现有代码无法正确保存多对多关系,需修正实现方式。


现有代码

models.py

class Branch(models.Model): # Branch Master
    status_type = (
        ("a",'Active'),
        ("d",'Deactive'),
    )
    name = models.CharField(max_length=100, unique=True)
    suffix = models.CharField(max_length=8, unique=True)
    Remark = models.CharField(max_length=200, null=True, blank=True)
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    create_at = models.DateTimeField(auto_now_add=True)
    update_at = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=1, choices = status_type, default = 'a')
    def __str__(self):
        return self.name


class Vendor(models.Model):
    status_type = (
        ("a",'Active'),
        ("d",'Deactive'),
    )
    branch = models.ManyToManyField(Branch)
    company = models.CharField(max_length=200)
    name = models.CharField(max_length=200)
    phone = models.CharField(max_length=11, unique = True)
    email  =  models.EmailField(max_length=254, unique = True)
    gst = models.CharField(max_length=15, unique = True)
    pan_no = models.CharField(max_length=10, unique = True)
    add_1 = models.CharField(max_length=50, null=True, blank = True)
    add_2 = models.CharField(max_length=50, null=True, blank = True)
    add_3 = models.CharField(max_length=50, null=True, blank = True)
    Remark = models.CharField(max_length=200, null=True, blank=True)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)
    create_at = models.DateTimeField(auto_now_add=True)
    update_at = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=1, choices = status_type, default = 'a')
    
    def __str__(self):
        return self.company

forms.py

class VendorForm(ModelForm):
    class Meta:
        model  = Vendor
        fields = '__all__'
        exclude = ['created_by', 'branch']
        widgets = {
            'company':forms.TextInput(attrs={'class':'form-control'}),
            'name':forms.TextInput(attrs={'class':'form-control'}),
            'phone':forms.TextInput(attrs={'class':'form-control'}),
            'email':forms.EmailInput(attrs={'class':'form-control'}),
            'gst':forms.TextInput(attrs={'class':'form-control'}),
            'pan_no':forms.TextInput(attrs={'class':'form-control'}),
            'add_1':forms.TextInput(attrs={'class':'form-control'}),
            'add_2':forms.TextInput(attrs={'class':'form-control'}),
            'add_3':forms.TextInput(attrs={'class':'form-control'}),
            'Remark':forms.Textarea(attrs={'class':'form-control','rows':'2'}),
            'status':forms.Select(attrs={'class':'form-control'}),
        }

views.py

def Add_Vendor(request): # for vendor add
    msg = ""
    msg_type = ""
    branch_id = request.session['branch_id']
    branch_data = Branch.objects.get(id = branch_id)
    form = ""
    if request.method == "POST":
        try:
            form = VendorForm(request.POST)
            if form.is_valid:
                vendor_add = form.save(commit=False)
                vendor_add.created_by = request.user
                vendor_add.instance.branch = branch_data.id
                vendor_add.save()
                form.save_m2m() # for m to m field save                
                msg_type = "success"
                msg = "Vendor Added."
                form = VendorForm(initial={'branch':branch_id})
        except:
            msg_type = "error"
            msg = str(form.errors)
            print(msg)
    else:
        form = VendorForm(initial={'branch':branch_id})
    context = {
        'form':form,
        'branch_data':branch_data,
        'msg_type':msg_type,
        'msg':msg,
        'btn_type':'fa fa-regular fa-plus',
        'form_title':'Vendor Form',
        'tree_main_title':'Vendor',
        'v_url':'vendor_page',
        'tree_title':'Add Form',
    }
    return render(request, 'base/vendor_master/form_vendor.html',context)

问题分析与修复

现有代码的核心问题:

  1. 多对多字段赋值方式错误,不能直接给branch字段赋值ID
  2. form.is_valid未加括号,导致表单验证逻辑未执行
  3. 无效的form.save_m2m()调用(branch字段不在表单中,无需此操作)

修复后的views.py代码:

def Add_Vendor(request): # for vendor add
    msg = ""
    msg_type = ""
    branch_id = request.session['branch_id']
    branch_data = Branch.objects.get(id=branch_id)
    form = ""
    if request.method == "POST":
        form = VendorForm(request.POST)
        if form.is_valid():  # 修复:调用表单验证方法
            vendor_add = form.save(commit=False)
            vendor_add.created_by = request.user
            vendor_add.save()  # 先保存实例,获取主键后再关联多对多字段
            vendor_add.branch.add(branch_data)  # 修复:用add()方法关联多对多对象
            msg_type = "success"
            msg = "Vendor Added."
            form = VendorForm()  # 重置空表单
        else:
            msg_type = "error"
            msg = str(form.errors)
            print(msg)
    else:
        form = VendorForm()  # 表单已排除branch字段,无需设置initial
    context = {
        'form':form,
        'branch_data':branch_data,
        'msg_type':msg_type,
        'msg':msg,
        'btn_type':'fa fa-regular fa-plus',
        'form_title':'Vendor Form',
        'tree_main_title':'Vendor',
        'v_url':'vendor_page',
        'tree_title':'Add Form',
    }
    return render(request, 'base/vendor_master/form_vendor.html',context)

关键修复说明

  • 表单验证:必须调用form.is_valid()方法,否则表单不会执行验证逻辑
  • 多对多关联:多对多字段需要在实例保存后(获取主键),使用add()方法添加关联对象;若需关联多个分支,可传递多个对象或ID,如vendor_add.branch.add(1, 2, 3)
  • 无效代码清理:移除form.save_m2m()和无效的initial设置,因为branch字段已从表单中排除,这些操作无意义

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 16:20:23