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

如何用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字段关联到同一条患者主记录,需要调整模型结构:

  1. 修改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}"
  1. 调整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': '姓氏'})
    }
)
  1. 更新视图:在视图中先保存主Patient记录,再将克隆的字段组关联到该记录保存。

额外说明

你的JS克隆逻辑是正确的,它能正确更新表单集的索引和计数,确保Django能识别每个表单数据。测试时注意添加表单验证规则,避免空记录存入数据库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 10:55:25