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

Django多对多关联中间表表单创建及视图实现求助

问题描述

我有一个带额外字段的多对多关联关系,想要创建一个表单,支持输入产品信息,同时能为一个或多个市场设置对应的关联值(模型中对应字段为link)。目前无法实现该表单的视图编写及模板渲染,请求协助。

表单预期样式

表单样式示意图

关联关系示意图

关联关系示意图

现有模型代码

class Market(models.Model):
    name = models.CharField(max_length=100)
        
class Product(models.Model):
    name = models.CharField(max_length=250)
    market = models.ManyToManyField(Market, through='Link')

class Link(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    market = models.ForeignKey(Market, on_delete=models.CASCADE)
    link = models.CharField(max_length=250)

    class Meta:
        unique_together = [['producto', 'supermercado']]

现有表单代码

class MarketForm(ModelForm):

    class Meta:
        fields = ['name']
        model = Market

class ProductForm(ModelForm):

    class Meta:
        fields = ['name']
        model = Product


class LinkForm(ModelForm):

    class Meta:
        fields = ['market','product','link']
        model = Link

解决方案

1. 修正模型错误

首先Link模型的unique_together字段名与实际字段不匹配,需要修正:

class Link(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    market = models.ForeignKey(Market, on_delete=models.CASCADE)
    link = models.CharField(max_length=250)

    class Meta:
        unique_together = [['product', 'market']]  # 匹配实际字段名

2. 重构表单

使用inlineformset_factory实现产品表单与多个市场关联表单的组合:

from django import forms
from django.forms import inlineformset_factory
from .models import Product, Link

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name']

# 创建关联表单集,支持动态添加/删除市场关联
LinkFormSet = inlineformset_factory(
    Product,
    Link,
    fields=['market', 'link'],
    extra=1,  # 默认显示1个空关联表单
    can_delete=True  # 允许删除已添加的关联项
)

3. 编写视图

处理表单和表单集的提交、验证逻辑:

from django.shortcuts import render, redirect
from .forms import ProductForm, LinkFormSet

def product_create(request):
    if request.method == 'POST':
        product_form = ProductForm(request.POST)
        link_formset = LinkFormSet(request.POST, instance=product_form.instance)
        
        if product_form.is_valid() and link_formset.is_valid():
            product = product_form.save()
            link_formset.save()
            return redirect('product_list')  # 替换为你的产品列表页面URL
    
    else:
        product_form = ProductForm()
        link_formset = LinkFormSet()
    
    return render(request, 'product_create.html', {
        'product_form': product_form,
        'link_formset': link_formset
    })

4. 模板渲染(product_create.html)

按照预期样式渲染表单,可添加JS实现动态新增关联项:

<form method="post">
    {% csrf_token %}
    
    <h3>产品信息</h3>
    {{ product_form.as_p }}

    <h3>市场关联链接</h3>
    {{ link_formset.management_form }}
    {% for form in link_formset %}
        <div class="link-item">
            {{ form.id }}
            <div>
                <label>选择市场:</label>
                {{ form.market }}
                {{ form.market.errors }}
            </div>
            <div>
                <label>关联链接:</label>
                {{ form.link }}
                {{ form.link.errors }}
            </div>
            {% if link_formset.can_delete %}
                <div>
                    <label>删除该关联:</label>
                    {{ form.DELETE }}
                </div>
            {% endif %}
            <hr>
        </div>
    {% endfor %}

    <button type="button" id="add-link">添加市场关联</button>
    <button type="submit">保存产品</button>
</form>

<!-- 动态新增关联项的JS -->
<script>
document.getElementById('add-link').addEventListener('click', function() {
    const totalForms = document.getElementById('id_link_set-TOTAL_FORMS');
    const formCount = parseInt(totalForms.value);
    const newForm = document.querySelector('.link-item').cloneNode(true);

    // 替换表单字段的ID和name属性
    newForm.querySelectorAll('input, select').forEach(field => {
        field.name = field.name.replace(/-\d+-/, `-${formCount}-`);
        field.id = field.id.replace(/-\d+-/, `-${formCount}-`);
        if (field.type !== 'checkbox') field.value = '';
        else field.checked = false;
    });

    document.querySelector('form').insertBefore(newForm, document.getElementById('add-link'));
    totalForms.value = formCount + 1;
});
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 20:33:35