Django dynamic formset only saving last entry

Viewed 134

I have created a page where users can enter details to forms relating to a parent model (Patient) and two child models (CurrentMed and PastMed) linked to that parent.

Both the child forms use dynamic formsets where the user can add or delete rows to the form. My problem is only the last row of the currentmed and pastmed formsets is saving to my database when the user submits the form?

models.py

class Patient(TimeStampedModel):
    # get a unique id for each patient - could perhaps use this as slug if needed
    patient_id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False)
    
    name = models.CharField("Patient Name", max_length=255)

    creator = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        on_delete=models.SET_NULL)


class Med(TimeStampedModel):


    med_name = models.CharField(“med name“, max_length=20)
    
    dose = models.IntegerField("Dose (mg)", default=0)

    timepoint = models.CharField(
        "timepoint", max_length=20,
        choices=[('current','current'), ('past', 'past')], default='unspecified')
    
    patient = models.ForeignKey(Patient, on_delete=models.CASCADE)

    class Meta:
        abstract = True


class CurrentMed(Med):
    timepoint = models.CharField(
        "", max_length=20,
        choices=[('current','current'), ('past', 'past')], default='current')


class PastMed(Med):
    timepoint = models.CharField(
        "", max_length=20,
        choices=[('current','current'), ('past', 'past')], default='past')

forms.py

from .models import CurrentMed, PastMed, Patient

CurrentmedFormSet = inlineformset_factory(
    Patient, CurrentMed, fields=("med_name", "dose",), extra=2)

PastmedFormSet = inlineformset_factory(
    Patient, PastMed, fields=("med_name", "dose",), extra=2)

class PatientForm(ModelForm):
    class Meta:
        model = Patient
        fields = ['name', 'sex', 'age', 'diagnosis']

views.py

class PatientAddView(LoginRequiredMixin,TemplateView):
    model = Patient
    template_name = "../templates/patient/add.html"

    def get(self, *args, **kwargs):
        patient_form = PatientForm
        currentmed_formset = CurrentmedFormSet(queryset=CurrentMed.objects.none())
        pastmed_formset = PastmedFormSet(queryset=PastMed.objects.none())
        return self.render_to_response({'currentmed_formset': currentmed_formset,
                                        'pastmed_formset': pastmed_formset,
                                        'patient_form': patient_form})

    def post(self, *args, **kwargs):
        form = PatientForm(data=self.request.POST)
        currentmed_formset = CurrentmedFormSet(data=self.request.POST)
        pastmed_formset = PastmedFormSet(data=self.request.POST)
        if form.is_valid():
            patient_instance = form.save()
            if currentmed_formset.is_valid():
                med_name = currentmed_formset.save(commit=False)
                for med in med_name:
                    med.patient = patient_instance
                    med.save()
            if pastmed_formset.is_valid():
                med_name = pastmed_formset.save(commit=False)
                for med in med_name:
                    med.patient = patient_instance
                    med.save()

        return redirect(reverse(
                        'patient:treatment_detail',
                        kwargs={"patient_id": patient_instance.patient_id}))

html

{% extends "base.html" %}
{% load static  %}
{% load crispy_forms_tags %}

{% block javascript %}
<script type="text/javascript" src="{% static 'js/jquery/dist/jquery-1.3.2.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.formset.js' %}"></script>

<script type="text/javascript">
    $(function() {
        $('#currentmeds_table tbody tr').formset({
            prefix: 'current_meds'
        })
    });

    $(function() {
        $('#pastmeds_table tbody tr').formset({
            prefix: 'past_meds'
        })
    });
</script>
<style type="text/css">
    .delete-row {
        margin-left:5px;
    }
</style>

{% endblock %}

{% block content %}
<div>
<div class="entry">
<form id="form"   method="POST">
    <h1>Patient Details</h1>
    {% csrf_token %}

    <h3>Demographics</h3>
    {{patient_form}}

    <h3>Current Medication</h3>
    <table id="currentmeds_table" border="0" cellpadding="0" cellspacing="5">
        <thead>
            <tr>
                <th scope="col">Medication</th>
                <th scope="col">Dose</th>
            </tr>
        </thead>
        <tbody>
        {% for form in currentmed_formset %}
            <tr id="{{ form.prefix }}-row"></tr>
                <td>
                    {% for fld in form.hidden_fields %}{{ fld }}{% endfor %}
                    {% if form.instance.pk %}{{ form.DELETE }}{% endif %}
                    {{ form.med_name}}
                </td>
                <td>{{ form.dose }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    {{currentmed_formset.management_form}}

    <h3>Past Medication</h3>
    <table id="pastmeds_table" border="0" cellpadding="0" cellspacing="5">
        <thead>
            <tr>
                <th scope="col">Medication</th>
                <th scope="col">Dose</th>
            </tr>
        </thead>
        <tbody>
        {% for form in pastmed_formset %}
            <tr id="{{ form.prefix }}-row"></tr>
                <td>
                    {% for fld in form.hidden_fields %}{{ fld }}{% endfor %}
                    {% if form.instance.pk %}{{ form.DELETE }}{% endif %}
                    {{ form.med_name}}
                </td>
                <td>{{ form.dose }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>

    {{pastmed_formset.management_form}}
    
    <button type="submit">Create Patient</button>

</form>
</div>
</div>
{% endblock %}
1 Answers

The prefixes in the javascript were incorrect, found the correct ones by looking at the html source, instead of

    $('#currentmeds_table tbody tr').formset({
        prefix: 'current_meds'
    })

Should have been

    $('#currentmeds_table tbody tr').formset({
        prefix: 'currentmed_set'
    })
Related