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

如何在Django中创建含另一模型所有实例字段的表单?

解决方案

一、修正现有模型结构

你当前的MinimumQuantity模型存在继承错误,应继承自models.Model,修正后代码如下:

# models.py
class Supply(models.Model):
    name = models.CharField(max_length=50)

class StorageLocation(models.Model):
    name = models.CharField(max_length=50)

class MinimumQuantity(models.Model):  # 修正继承逻辑
    storage_location = models.ForeignKey(StorageLocation, on_delete=models.PROTECT)
    supply = models.ForeignKey(Supply, on_delete=models.PROTECT)
    amount = models.IntegerField()

二、基于现有模型的表单集实现

要在创建Supply时同时为所有StorageLocation设置最小数量,InlineFormSet是适配一对多关联场景的最佳工具,以下是具体实现步骤:

1. 定义表单与表单集

在forms.py中创建主表单及关联的表单集:

# forms.py
from django import forms
from django.forms import inlineformset_factory
from .models import Supply, MinimumQuantity, StorageLocation

class SupplyForm(forms.ModelForm):
    class Meta:
        model = Supply
        fields = ['name']

class MinimumQuantityForm(forms.ModelForm):
    class Meta:
        model = MinimumQuantity
        fields = ['amount']

# 创建关联表单集:绑定Supply与MinimumQuantity,禁用自动新增、删除功能
MinimumQuantityFormSet = inlineformset_factory(
    Supply,
    MinimumQuantity,
    form=MinimumQuantityForm,
    extra=0,
    can_delete=False
)

2. 重写视图逻辑

修改SupplyCreateView,实现表单集的初始化与关联保存:

# views.py
from django.views.generic.edit import CreateView
from .models import Supply, StorageLocation
from .forms import SupplyForm, MinimumQuantityFormSet

class SupplyCreateView(CreateView):
    model = Supply
    template_name = "supplies_form.html"
    form_class = SupplyForm

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.request.POST:
            # POST请求时用提交数据初始化表单集
            context['quantity_formset'] = MinimumQuantityFormSet(self.request.POST)
        else:
            # GET请求时为每个存储位置生成对应表单
            formset = MinimumQuantityFormSet()
            for idx, location in enumerate(StorageLocation.objects.all()):
                # 预设置存储位置值并隐藏字段,避免用户修改
                formset.forms[idx].initial['storage_location'] = location
                formset.forms[idx].fields['storage_location'].widget = forms.HiddenInput()
            context['quantity_formset'] = formset
        return context

    def form_valid(self, form):
        # 先保存Supply实例
        self.object = form.save()
        # 验证并保存关联的最小数量表单集
        quantity_formset = self.get_context_data()['quantity_formset']
        if quantity_formset.is_valid():
            quantity_formset.instance = self.object
            quantity_formset.save()
        return super().form_valid(form)

3. 调整模板渲染

修改模板,按需求展示主表单与数量输入字段:

<!-- supplies_form.html -->
<div class="card-body">
    {% csrf_token %}
    {{ form|crispy }}

    <h3>minimum supply quantities</h3>
    {% for form in quantity_formset %}
        <div class="mb-3">
            <label class="form-label">{{ form.instance.storage_location.name }} - amount:</label>
            {{ form.amount|as_crispy_field }}
            {{ form.storage_location }}
        </div>
    {% endfor %}

    <button type="submit" class="btn btn-primary">Save</button>
</div>

三、简化模型的替代方案(无需MinimumQuantity模型)

若无需对单位置最小数量做复杂查询(如按位置统计物资),可通过JSONField简化模型结构:

1. 修改Supply模型

# models.py
from django.db import models

class Supply(models.Model):
    name = models.CharField(max_length=50)
    # 用JSON格式存储:{"location_name": amount}
    min_quantities = models.JSONField(default=dict)

class StorageLocation(models.Model):
    name = models.CharField(max_length=50)

2. 动态生成表单字段

在forms.py中为每个存储位置动态生成数量输入框:

# forms.py
from django import forms
from .models import Supply, StorageLocation

class SupplyForm(forms.ModelForm):
    class Meta:
        model = Supply
        fields = ['name']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为每个存储位置添加IntegerField
        for location in StorageLocation.objects.all():
            field_name = f"min_{location.name}"
            initial_val = self.instance.min_quantities.get(location.name, 0)
            self.fields[field_name] = forms.IntegerField(
                label=f"{location.name} - amount",
                initial=initial_val,
                min_value=0
            )

    def clean(self):
        cleaned_data = super().clean()
        min_quantities = {}
        # 收集所有位置的数量值
        for location in StorageLocation.objects.all():
            field_name = f"min_{location.name}"
            min_quantities[location.name] = cleaned_data.get(field_name, 0)
        cleaned_data['min_quantities'] = min_quantities
        return cleaned_data

    def save(self, commit=True):
        instance = super().save(commit=False)
        instance.min_quantities = self.cleaned_data['min_quantities']
        if commit:
            instance.save()
        return instance

3. 简化视图与模板

视图直接使用默认CreateView,模板只需渲染表单即可:

# views.py
class SupplyCreateView(CreateView):
    model = Supply
    template_name = "supplies_form.html"
    form_class = SupplyForm
<!-- supplies_form.html -->
<div class="card-body">
    {% csrf_token %}
    {{ form|crispy }}
    <button type="submit" class="btn btn-primary">Save</button>
</div>

方案选择建议

  • 若需对单位置最小数量做复杂查询、统计,优先选择原模型+InlineFormSet方案。
  • 若仅需简单存储各位置数量,JSONField简化方案更轻便,减少数据库表数量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 11:50:20