Django表单字段旁加Bootstrap5按钮弹窗录入关联数据方法
不用纠结外键的事,外键是你要把承包商拆成独立数据模型做关联的时候才需要改的,和你现在要在输入框旁加按钮的需求完全不冲突,按钮直接在模板层渲染就行,不用动你现在写好的forms.py配置。
1. 渲染带+按钮的输入框
你现在直接用{{ form.contractor }}是输出不了旁边的按钮的,别在widget的attrs里费劲,直接在写页面模板的时候,把contractor字段手动按Bootstrap5的输入组结构拼出来就行:
<div class="mb-3"> <label for="{{ form.contractor.id_for_label }}" class="form-label">Contractor</label> <div class="input-group"> {{ form.contractor }} <button class="btn btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#addContractorModal" >+</button> </div> {% if form.contractor.errors %} <div class="invalid-feedback d-block"> {% for err in form.contractor.errors %}{{ err }}{% endfor %} </div> {% endif %} </div>
这里的data-bs-target值要和你已经写好的模态框容器id完全一致,点按钮就会自动唤起模态框,不需要额外写JS触发。你之前在forms.py里给contractor字段配的form-control类会自动渲染到input标签上,样式不会乱。
2. 模态框表单提交后回填主表单
你已经写完模态框里的小表单了,提交逻辑选下面任意一种就行:
无刷新异步提交(体验最好)
给模态框里的小表单加个id比如contractorCreateForm,加一段简单的JS监听提交,阻止默认页面跳转,提交成功后把拿到的承包商名称填到主表单输入框,再关模态框:
注意:Django自动渲染的模型表单输入框id固定为
id_字段名格式,所以contractor输入框的id一定是id_contractor,JS里直接用这个选择器不会出错。
const contractorForm = document.getElementById('contractorCreateForm') contractorForm.addEventListener('submit', e => { e.preventDefault() const formData = new FormData(contractorForm) fetch(contractorForm.action, { method: 'POST', body: formData, headers: {'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value} }) .then(res => res.json()) .then(data => { // 把返回的承包商名称填进主表单的contractor输入框 document.getElementById('id_contractor').value = data.name // 关闭模态框、重置小表单 bootstrap.Modal.getInstance('#addContractorModal').hide() contractorForm.reset() }) .catch(() => alert('提交失败,请检查输入内容')) })
对应的后端视图只需要接收POST请求,校验小表单通过后保存数据,返回JSON即可:
from django.http import JsonResponse def create_contractor(request): if request.method == 'POST': form = ContractorForm(request.POST) # 替换成你自己写的模态框小表单类 if form.is_valid(): contractor = form.save() # 这里返回你要填充到主表单的字段,比如承包商名称字段是name return JsonResponse({'code': 0, 'name': contractor.name}) return JsonResponse({'code': 1, 'errors': form.errors}, status=400)
同步提交(零JS写法)
如果不想写前端交互逻辑,也可以让小表单正常提交到后端,把新创建的承包商名称存到session里,重定向回主表单页面时,给主表单的contractor字段传初始值就行,缺点是提交小表单时页面会刷新。
外键配置说明
等你后续需要统一维护承包商数据、支持重复选择的时候,再改外键也不迟:
- 新建Contractor模型存储承包商基础信息
- 把Contratos模型里的contractor字段从文本字段改成外键字段,执行
makemigrations和migrate - 这时候主表单的contractor会自动渲染成下拉选择框,之前的+按钮逻辑依然能用,只需要在小表单提交成功后,多一步把新创建的承包商追加到下拉选项里即可,不需要重做按钮渲染。
内容的提问来源于stack exchange,提问作者Mario A

