如何用JavaScript克隆表单并将数据存入同一条数据库记录?
问题描述
我用Django开发,需要克隆表单元素。找到的JS代码能实现表单克隆并提交,但数据存入数据库后,克隆的表单字段对应不同的记录ID。我希望把克隆的两个表单字段数据存入同一条数据库记录,现在数据库里能看到同值但不同ID的记录,该怎么解决?
相关代码
models.py
from django.db import models from django.contrib.auth.models import User # Create your models here. class PatientBilgi(models.Model): name = models.CharField(max_length=155) surname = models.CharField(max_length=155) def __str__(self): return self.name class PatientAliskanlik(models.Model): sigara = models.CharField(max_length=155) alkol = models.CharField(max_length=155) def __str__(self): return self.sigara class Patient(models.Model): name = models.CharField(max_length=155) pbilgi = models.ForeignKey(PatientBilgi, on_delete=models.CASCADE, blank=True, null=True) palis = models.ForeignKey(PatientAliskanlik, on_delete=models.CASCADE, blank=True, null=True) def __int__(self): return self.name
forms.py
from django import forms from .models import Patient, PatientBilgi, PatientAliskanlik from django.forms import (formset_factory, modelformset_factory) class PatientForm(forms.ModelForm): class Meta: model = Patient fields = ['name'] class PatientBilgiForm(forms.ModelForm): class Meta: model = PatientBilgi fields = ['name', 'surname'] class PatientAliskanlikForm(forms.ModelForm): class Meta: model = PatientAliskanlik fields = ['sigara', 'alkol'] ###### Formsets class PatientBilgiModelForm(forms.ModelForm): class Meta: model = PatientBilgi fields = ('name', 'surname' ) labels = { 'name': 'Patient Name', 'surname': 'Patient Surname' } widgets = { 'name': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Enter Patient Name and Surname here' } ) } PatientBilgiFormset = formset_factory(PatientBilgiForm) BookModelFormset = modelformset_factory( PatientBilgi, fields=('name', 'surname', ), extra=1, widgets={ 'name': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Enter Patient Name here' } ), 'surname': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Enter Patient Surname here' } ) } ) PatientFormset = modelformset_factory( Patient, fields=('name', ), extra=1, widgets={'name': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Enter Author Name here' }) } )
views.py
from django.shortcuts import render, redirect from django.urls import reverse from .models import Patient, PatientBilgi, PatientAliskanlik from .forms import PatientForm, PatientBilgiForm, PatientAliskanlikForm, PatientBilgiFormset, PatientBilgiModelForm, PatientFormset from formtools.wizard.views import SessionWizardView # Create your views here. def home(request): return render(request, 'home.html') def hasta_listele(request): data = Patient.objects.all() return render(request, 'deneme.html', {'data': data}) def hasta_detay(request, id): kata = Patient.objects.get(id=id) kata1 = PatientBilgi.objects.get(id=id) kata2 = PatientAliskanlik.objects.get(id=id) return render(request, 'hasta_detay.html', context = { 'kata': kata, 'kata1': kata1, 'kata2': kata2} ) # Multi Step Form Submission class multistepformsubmission(SessionWizardView): template_name = 'hasta_kayit.html' form_list = [PatientBilgiForm, PatientAliskanlikForm, PatientForm] def done(self, form_list, **kwargs): form_data = [form.cleaned_data for form in form_list] patientdata = PatientBilgi(name = form_data[0]['name'], surname = form_data[0]['surname']) patientdata.save() patientdata1 = PatientAliskanlik(sigara = form_data[1]['sigara'], alkol = form_data[1]['alkol']) patientdata1.save() patientdata2 = Patient(name = form_data[2]['name'] ) patientdata2.save() data = Patient.objects.all() data1 = PatientBilgi.objects.all() data2 = PatientAliskanlik.objects.all() return render(self.request, 'done.html', context={ 'data': data, 'data1': data1, 'data2': data2 }) ######## Deneme Amaçlı Fonksiyon, gereği yok.... def deneme(request): a = Patient.objects.all() return render(request, "a.html", {'a': a }) ########## Formset Fonksiyonu def create_book_normal(request): template_name = 'create_normal.html' if request.method == 'GET': formset = PatientBilgiFormset(request.GET or None) elif request.method == 'POST': formset = PatientBilgiFormset(request.POST) if formset.is_valid(): for form in formset: name = form.cleaned_data.get('name') surname = form.cleaned_data.get('surname') # save book instance if name: PatientBilgi(name=name).save() PatientBilgi(surname=surname).save() return redirect('done') return render(request, template_name, { 'formset': formset }) def done1(request): return render(request, 'done.html')
create_normal.html
<form class="form-horizontal" method="POST" action=""> {% csrf_token %} {{ formset.management_form }} {% for form in formset %} <div class="row form-row spacer"> <div class="col-2"> <label>{{form.name.label}}</label> </div> <div class="col-4"> <div class="input-group"> {{form.name}} {{form.surname}} <div class="input-group-append"> <button class="btn btn-success add-form-row">+</button> </div> </div> </div> </div> {% endfor %} <div class="row spacer"> <div class="col-4 offset-2"> <button type="submit" class="btn btn-block btn-primary">Create</button> </div> </div> </form> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script type="text/javascript"> function updateElementIndex(el, prefix, ndx) { var id_regex = new RegExp('(' + prefix + '-\\d+)'); var replacement = prefix + '-' + ndx; if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); if (el.id) el.id = el.id.replace(id_regex, replacement); if (el.name) el.name = el.name.replace(id_regex, replacement); } function cloneMore(selector, prefix) { var newElement = $(selector).clone(true); var total = $('#id_' + prefix + '-TOTAL_FORMS').val(); newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() { var name = $(this).attr('name') if(name) { name = name.replace('-' + (total-1) + '-', '-' + total + '-'); var id = 'id_' + name; $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked'); } }); newElement.find('label').each(function() { var forValue = $(this).attr('for'); if (forValue) { forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-'); $(this).attr({'for': forValue}); } }); total++; $('#id_' + prefix + '-TOTAL_FORMS').val(total); $(selector).after(newElement); var conditionRow = $('.form-row:not(:last)'); conditionRow.find('.btn.add-form-row') .removeClass('btn-success').addClass('btn-danger') .removeClass('add-form-row').addClass('remove-form-row') .html('-'); return false; } function deleteForm(prefix, btn) { var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); if (total > 1){ btn.closest('.form-row').remove(); var forms = $('.form-row'); $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); for (var i=0, formCount=forms.length; i<formCount; i++) { $(forms.get(i)).find(':input').each(function() { updateElementIndex(this, prefix, i); }); } } return false; } $(document).on('click', '.add-form-row', function(e){ e.preventDefault(); cloneMore('.form-row:last', 'form'); return false; }); $(document).on('click', '.remove-form-row', function(e){ e.preventDefault(); deleteForm('form', $(this)); return false; }); </script>
解决方案
问题根源
你的核心问题出在create_book_normal视图的保存逻辑:你把单个表单里的name和surname分别存入了两条PatientBilgi记录,而不是合并成一条,这才导致数据库出现同值(单字段有值)但不同ID的记录。
修复方案
场景1:克隆表单用于添加多个患者(每个克隆表单对应一条独立记录)
修改views.py中的create_book_normal函数,将每个表单的name和surname合并为一条记录保存:
def create_book_normal(request): template_name = 'create_normal.html' if request.method == 'GET': formset = PatientBilgiFormset(request.GET or None) elif request.method == 'POST': formset = PatientBilgiFormset(request.POST) if formset.is_valid(): for form in formset: cleaned_data = form.cleaned_data name = cleaned_data.get('name') surname = cleaned_data.get('surname') # 合并name和surname到同一条记录 if name or surname: PatientBilgi(name=name, surname=surname).save() return redirect('done') return render(request, template_name, {'formset': formset})
场景2:克隆字段组存入同一条患者记录(如一个患者有多组姓名信息)
如果你的需求是把多组克隆的name+surname字段关联到同一条患者主记录,需要调整模型结构:
- 修改models.py
class Patient(models.Model): main_name = models.CharField(max_length=155) # 其他患者核心信息 class PatientName(models.Model): patient = models.ForeignKey(Patient, on_delete=models.CASCADE, related_name='names') name = models.CharField(max_length=155) surname = models.CharField(max_length=155) def __str__(self): return f"{self.name} {self.surname}"
- 调整forms.py
from django.forms import inlineformset_factory PatientNameFormset = inlineformset_factory( Patient, PatientName, fields=('name', 'surname'), extra=1, widgets={ 'name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '姓名'}), 'surname': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '姓氏'}) } )
- 更新视图:在视图中先保存主
Patient记录,再将克隆的字段组关联到该记录保存。
额外说明
你的JS克隆逻辑是正确的,它能正确更新表单集的索引和计数,确保Django能识别每个表单数据。测试时注意添加表单验证规则,避免空记录存入数据库。
内容的提问来源于stack exchange,提问作者Prusa
相关产品推荐
相关产品推荐

