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
相关产品推荐
相关产品推荐

