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

Django Admin中如何将Model的JSONField以Inline形式编辑(含嵌套列表结构)

Django Admin中如何将Model的JSONField以Inline形式编辑(含嵌套列表结构)

我完全懂你这种困扰——把复杂的嵌套JSON塞在一个JSONField里,编辑起来简直是噩梦,想把里面的列表部分做成像Admin里Inline那样能灵活增删改的界面,但又不想为了这部分数据单独建个关联模型,毕竟只是JSON里的嵌套结构而已。咱们一步步来实现这个需求,不用额外的模型,靠自定义表单和表单集就能搞定。

第一步:定义嵌套结构的表单与表单集

首先,我们需要为JSON里key3对应的每个字典元素做一个表单,再用表单集(FormSet)来管理多个这样的表单:

from django import forms

# 对应key3里单个字典的表单
class InnerKeyForm(forms.Form):
    inner_key_1 = forms.CharField(max_length=255, label="内部字段1")
    inner_key_2 = forms.CharField(max_length=255, label="内部字段2")
    # 这里可以根据你的实际JSON结构添加更多字段,比如inner_key_3等等

# 生成可以批量处理InnerKeyForm的表单集,extra=1表示默认显示一个空表单
InnerKeyFormSet = forms.formset_factory(InnerKeyForm, extra=1)

第二步:整合主模型表单与嵌套表单集

接下来,我们要为MyModel写一个自定义表单,把key1、key2和刚才的表单集整合起来,同时处理数据的读取和保存:

from django import forms
from .models import MyModel

class MyModelForm(forms.ModelForm):
    # 把JSON里的key1、key2拆成单独的表单字段
    key1 = forms.CharField(max_length=255, label="Key1")
    key2 = forms.CharField(max_length=255, label="Key2")

    class Meta:
        model = MyModel
        fields = []  # 我们手动处理所有字段,不直接使用模型的默认字段

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 如果是编辑已有数据,从final_result里读取初始值
        if self.instance.pk:
            json_data = self.instance.final_result
            self.fields['key1'].initial = json_data.get('key1', '')
            self.fields['key2'].initial = json_data.get('key2', '')
            # 把key3的列表数据填充到表单集里
            self.inner_formset = InnerKeyFormSet(initial=json_data.get('key3', []))
        else:
            # 新增数据时初始化空的表单集
            self.inner_formset = InnerKeyFormSet()

    def clean(self):
        cleaned_data = super().clean()
        # 收集表单集里的有效数据(跳过空表单)
        inner_entries = []
        for form in self.inner_formset:
            if form.cleaned_data:  # 确保表单有有效输入才加入
                inner_entries.append(form.cleaned_data)
        # 把所有字段数据组装成原始的JSON结构
        cleaned_data['final_result'] = {
            'key1': cleaned_data.get('key1', ''),
            'key2': cleaned_data.get('key2', ''),
            'key3': inner_entries
        }
        return cleaned_data

    def save(self, commit=True):
        instance = super().save(commit=False)
        # 把组装好的JSON数据存回final_result字段
        instance.final_result = self.cleaned_data['final_result']
        if commit:
            instance.save()
        return instance

第三步:自定义Admin类与模板

现在要在Admin后台里把这个表单整合进去,还要修改模板来显示表单集,同时添加增删按钮的功能:

from django.contrib import admin
from .models import MyModel
from .forms import MyModelForm

class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm

    # 把表单集传到模板上下文里
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}
        # 获取当前表单实例,取出里面的inner_formset
        form_instance = self.get_form(request)(instance=self.get_object(request, object_id))
        extra_context['inner_formset'] = form_instance.inner_formset
        return super().changeform_view(request, object_id, form_url, extra_context)

    # 指定自定义的修改页面模板
    change_form_template = 'admin/your_app_name/mymodel_change_form.html'

    # 加载Django自带的表单集JS,实现增删按钮功能
    class Media:
        js = (
            'admin/js/jquery.min.js',
            'admin/js/jquery.formset.js',
        )

admin.site.register(MyModel, MyModelAdmin)

然后创建自定义模板文件templates/admin/your_app_name/mymodel_change_form.html(记得把your_app_name换成你实际的App名称):

{% extends "admin/change_form.html" %}

{% block field_sets %}
    {{ block.super }}
    <!-- 显示key3的表单集 -->
    <div class="inline-group" id="inner-key-formset">
        <h2>Key3 条目</h2>
        {{ inner_formset.management_form }}
        {% for form in inner_formset %}
            <div class="inline-related">
                {{ form.as_table }}
            </div>
        {% endfor %}
    </div>
{% endblock %}

{% block extrajs %}
    {{ block.super }}
    <script type="text/javascript">
        (function($) {
            $(document).ready(function() {
                // 初始化表单集的增删功能
                $('#inner-key-formset .inline-related').formset({
                    prefix: '{{ inner_formset.prefix }}',
                    addText: '添加新条目',
                    deleteText: '删除',
                    addCssClass: 'btn btn-primary',
                    deleteCssClass: 'btn btn-danger'
                });
            });
        })(django.jQuery);
    </script>
{% endblock %}

一些注意事项

  • 如果你的JSON字段有其他类型(比如整数、布尔值、日期),记得把InnerKeyForm里的字段换成对应的类型,比如forms.IntegerField、forms.BooleanField。
  • 如果key3的列表可能为空,代码里的json_data.get('key3', [])已经处理了这种情况,会初始化一个空的表单集。
  • 模板路径一定要对应你的App结构,否则Django找不到模板文件。
  • 测试的时候记得先清空浏览器缓存,确保JS文件能正常加载。

这样一来,你在Admin后台编辑MyModel的时候,就能看到key1、key2的单独输入框,还有key3对应的可增删的条目列表,和普通的Inline体验几乎一样,所有数据最终都会存在final_result这个JSONField里。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 06:58:02