Django nested formsets - 3 levels

26 views Asked by At

I'm working on an app for construction company, and I want to create form for our daily schedule. For this I have 4 models: Date, DailySchedule, WorkersSchedule and MachinerySchedule. Each day we are working on several construction sites and we have 3-4 machines per CS and a few engaged workers on those sites.

I'm trying to use inlineformset_factory with 3 levels of nesting, but without success:

form (pick a date)
  formset0 (choose construction site)
    formset1 (choose employees)
    formset2 (choose machinery)

date
  construction_site_1
    employee_1
    employee_2
    machine_1
    machine_2
    machine_3
  construction_site_2
    employee_1
    employee_2
    machine_1
    machine_2
    machine_3
  construction_site_3
    employee_1
    employee_2
    employee_3
    machine_1
    machine_2
    ...

models.py

class Date(models.Model):
  datum = models.DateField(blank=True, null=True)
    
  def __str__(self):
    return str(self.date)

class DailySchedule(models.Model):
  date = models.ForeignKey(Date, on_delete=models.CASCADE, blank=True, null=True)
  construction_site = models.ForeignKey(ConstructionSite, on_delete=models.CASCADE, blank=True, null=True)
  
  def __str__(self):
    return str(self.date) + ' --- ' + str(self.construction_site)
  
class WorkersSchedule(models.Model):
  daily_schedule = models.ForeignKey(DailySchedule, on_delete=models.CASCADE, blank=True, null=True)
  employees = models.ForeignKey(Employees, on_delete=models.CASCADE, blank=True, null=True)
  
  def __str__(self):
    return str(self.daily_schedule) + ' --- ' + str(self.employees)
  
class MachinerySchedule(models.Model):
  daily_schedule = models.ForeignKey(DailySchedule, on_delete=models.CASCADE, blank=True, null=True)
  machinery = models.ForeignKey(Machinery, on_delete=models.CASCADE, blank=True, null=True)
    
  def __str__(self):
    return str(self.daily_schedule) + ' --- ' + str(self.machinery)

forms.py

class DateForm(ModelForm):
    class Meta:
        model = Date
        fields = "__all__"

views.py

def daily_schedule_form(request):
    date = Date()
    ds = DailySchedule()
    form = DateForm()
        
    DailyScheduleFormSet = inlineformset_factory(Date, DailySchedule, fields=('__all__'), can_delete=False, extra=2)
    WorkersScheduleFormSet= inlineformset_factory(DailySchedule, WorkersSchedule, fields=('__all__'), can_delete=False, extra=2)
    MachineryScheduleFormSet= inlineformset_factory(DailySchedule, MachinerySchedule, fields=('__all__'), can_delete=False, extra=2)

    if request.method == 'POST':
        form = DateForm(request.POST)
        formset0 = DailyScheduleFormSet(request.POST, instance=date, prefix="formset0")
        formset1 = WorkersScheduleFormSet(request.POST, instance=ds, prefix="formset1")
        formset2 = MachineryScheduleFormSet(request.POST, instance=ds, prefix='formset2')
        
        if form.is_valid():
            form_data= form.cleaned_data
            data = form.save()
            formset0 = DailyScheduleFormSet(request.POST, instance=data, prefix="formset0")

            if formset0.is_valid():
                for f in formset0:
                    date = f.cleaned_data["date"]
                    construction_site = f.cleaned_data["construction_site"]
                    date.save()
                    construction_site.save()
                    ds_new = DailySchedule(date = date, construction_site = construction_site)
                    ds_new.save()

                    formset1 = WorkersScheduleFormSet(request.POST, instance=ds_new, prefix="formset1")
                    formset2 = MachineryScheduleFormSet(request.POST, instance=ds_new, prefix='formset2')

                if formset1.is_valid() and formset2.is_valid():
                    ....
      

            return HttpResponseRedirect('/machinery/daily_schedule/')
    else:
        formset0 = DailyScheduleFormSet(prefix="formset0")
        formset1 = WorkersScheduleFormSet(prefix="formset1")
        formset2 = MachineryScheduleFormSet(prefix='formset2')
    return render(request,"machinery/daily_schedule_form.html",{'form':form,'formset0':formset0,'formset1':formset1, 'formset2': formset2})

template

<form method="POST">  
{% csrf_token %}  
{{ form.as_p }}
{{ formset0.management_form }}
{% for x in formset0 %}
    {{x}}
    {{ formset1.as_p }} 
    {{ formset2.as_p }} 
{% endfor %}

<button type="submit">Save</button>  
</form>

form in template I'm stuck with the 3rd level of nesting - probably something with prefixes, but my level of knowledge is limited. Is there a better way to achieve this? Maybe using DRF combined with React/JS or something else? Any help is welcomed.

0

There are 0 answers