Search code examples
djangodjango-modelsmany-to-manymodelform

Autofilling Django model form field with data from associated objects


I have a model form that creates a new job entry, and on submission, I need an invisible field job_time_estimation to be set to a sum of 'service_stats_estimate_duration' values from ServiceItemStats objects associated with the JobEntry by a many-to-many relationship when submitting the form.

For example, if in my NewJobEntryForm I chose two existing ServiceItemStats objects that have service_stats_estimate_duration values 60 and 90, on submission, I want a value 150 to be saved in that JobEntry object's job_time_estimation attribute.

I tried doing this using aggregation by defining a save() method in the model but I am getting an error "name 'serviceItemStats' is not defined".

I am not sure if I am going about this the right way. Any help would be appreciated.

My code:

models.py:

class ServiceItemStats(models.Model):
    service_stats_name = models.CharField(primary_key=True, max_length=20)
    service_stats_estimate_duration = models.IntegerField()
    # Many-to-many relationship with JobEntry.

    def __str__(self):
        return self.service_stats_name

class JobEntry(models.Model):

    # PK: id - automatically assigned by Django.
    jo

b_entry_date_time = models.DateTimeField(default=timezone.now)
    jo

b_date = models.DateField(blank=True, null=True)
    job_checked_in = models.BooleanField()
    job_checked_out = models.BooleanField(default=False)
    job_priority = models.IntegerField()
    job_time_estimation = models.IntegerField(blank=True, null=True)
    job_comments = models.TextField(max_length=200, blank=True, null=True)
    job_parts_instock = models.BooleanField(default=False)
    job_started = models.BooleanField(default=False)
    job_finished = models.BooleanField(default=False)

    job_expand_fault_evidence = models.ImageField(blank=True, null=True)
    job_expand_comments = models.ImageField(blank=True, null=True)
    job_expand_parts_required = models.CharField(max_length=200, blank=True, null=True)

    vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE) #One-to-one relationship
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE) #One-to-one relationship
    serviceBay = models.ForeignKey(ServiceBay, on_delete=models.CASCADE, blank=True, null=True) #One-to-one relationship

    serviceItemStats = models.ManyToManyField(ServiceItemStats, blank=True) #Many-to-many relationship

    def __str__(self):
        return self.id

    def save(self, *args, **kwargs):
        if not self.job_time_estimation:
            self.job_time_estimation = serviceItemStats.objects.all().aggregate('service_stats_estimate_duration')
        return super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse("jobs:job_detail",kwargs={'pk':self.pk})

views.py

class JobCreateView(FormView):
    template_name = "jobs/jobentry_form.html"
    form_class = NewJobEntryForm
    success_url = reverse_lazy("jobs:job_list")
    
    def form_valid(self, form):
        form.save()
        return super(job_list, self).form_valid(form)

forms.py

class NewJobEntryForm(ModelForm):
    class Meta:
        model = JobEntry
        fields = ['vehicle', 'customer', 'job_date', 'job_checked_in', 'job_priority', 'job_comments', 'job_parts_instock', 'serviceItemStats']

        widgets = {
        'job_date' : forms.DateInput(format=('%m/%d/%Y'), attrs={'class':'form-control', 'placeholder':'Select a date', 'type':'date'}),
        'ServiceItemStats' : forms.CheckboxSelectMultiple(),
        'job_priority' : forms.RadioSelect(choices=priorityOptions),
        }

Solution

  • You can try this.

    from django.db.models import Sum
    
    class JobCreateView(FormView):
        template_name = "jobs/jobentry_form.html"
        form_class = NewJobEntryForm
        success_url = reverse_lazy("jobs:job_list")
        
        def form_valid(self, form):
            job=form.save()
            estimation = job.serviceItemStats.all().aggregate(total=Sum('service_stats_estimate_duration'))
            job.job_time_estimation = estimation['total']
            job.save()
            return super(job_list, self).form_valid(form)