Django - Adding up values in an object's many to many field? - django

I have 2 classes
class Service(models.Model):
service_name = models.CharField(max_length=15, blank=False)
service_time = models.IntegerField(blank=False)
class Appointment(models.Model):
name = models.CharField(max_length=15)
service_selected = models.ManytoManyField(Service, blank=True)
total_time = models.IntegerField(null=True, blank=True)
What Im trying to do is that after a user has selected the services and created an appointment, each of the services' service_time will be added up to equal total_time
I know I have to use something along the lines of
#in class Appointment
def save(self, *args, **kwargs):
self.total_time += self.service_selected.service_time #it needs to add up all of the service_time from each service selected but that's where Im lost
super(Appointment, self).save(*args, **kwargs)
But I don't know how to get the service_time value from each of the service_name that were selected, or how to query for every service_selected chosen in that appointment's object so that I can add up those values.
edit:
Had to change
total_time = models.IntegerField(blank=False)
to
total_time = models.IntegerField(blank=False, null=False, default=0)
for the answer to work

You can do it as follows:
#in class Appointment
def save(self, *args, **kwargs):
self.total_time += Service.objects.all().aggregate(total_time=Sum('service_time'))['total_time']
# You will have to run a query to get the data and aggregate according to that. You may change the query according to your needs.
super(Appointment, self).save(*args, **kwargs)

Related

Django aggregate sum of manytomany is adding up everything in its field instead of the ones selected

2 Classes involved in question class Appointment and class Service
appointmentApp.models class Service
class Service(models.Model):
service_name = models.CharField(max_length=15, blank=False)
service_time = models.IntegerField(blank=False)
def __str__(self):
return self.service_name
class Meta:
verbose_name_plural = "Services"
appointmentApp/models.py class Appointment
class Appointment(models.Model):
service_chosen = models.ManyToManyField(Service, blank=False)
total_time = models.IntegerField(blank=False, null=False, default=0)
#will add up the amount of time needed for each service
def save(self, *args, **kwargs):
self.total_time += Service.objects.all().aggregate(total_time=Sum('service_time'))['total_time']
super(Appointment, self).save(*args, **kwargs)
def __str__(self):
return self.client_dog_name
Services are chosen through a multiplechoice field and on save the service_chosen's service_time are added up
but what my save function is doing instead is adding up all the existing service.service_time instead of the ones selected, why is this happening?
ManyToManyFields are saved after the containing instance is saved, you need to create a signal handler to perform this update on m2m_changed
from django.db.models.signals import m2m_changed
class Appointment(models.Model):
...
def service_chosen_changed(sender, instance=None, action=None, **kwargs):
if action == 'post_add':
instance.total_time = instance.service_chosen.aggregate(total_time=Sum('service_time'))['total_time']
instance.save()
m2m_changed.connect(service_chosen_changed, sender=Appointment.service_chosen.through)

Django Models: update field value on save

I have a model that is accessed via an endpoint that uses the slug:
path('test/<str:slug>', TestView.as_view(), name='test_view'),
I want to create a dynamic link which requires knowledge of the slug so it can't be assigned on creation since the slug hasn't been generated yet. How can I update the dynamic_link field and update it on creation?
class TestModel(models.Model):
name = models.CharField(max_length=1000)
dynamic_link= models.CharField(max_length=1000, blank=True, null=True)
slug = AutoSlugField(_('slug'), max_length=150, unique=True, populate_from=('name',))
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
Try this:
def update_dynamic_link(instance, created, **kwargs):
if created:
instance.dynamic_link = self.slug #Put whatever you want to assign
instance.save(update_fields=['dynamic_link'])
model.signals.post_save(update_dynamic_link, sender=TestModel)

automatically create a model object when an object of another table is created or updated in django models

I have two models in Django and I want to automatically create an object of History when an object of Food is created or updated and set the food_price attribute of History to the price attribute of the created Food object. My purpose is to have a history of food change price. How can I achieve that?
My models.py is:
class Food(models.Model):
food_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30, null=False, blank=False)
desc = models.TextField(max_length=200)
price = models.IntegerField(null=False, blank=False)
f_thumbnail = models.ImageField(upload_to=get_food_t_image_name)
DDD_data = models.ImageField(upload_to=get_food_d_image_name)
availability = models.BooleanField(default=True)
discount = models.IntegerField(default=0)
def __str__(self):
return '%s %s %s' % (self.name, self.category_id, self.price)
class History(models.Model):
id = models.AutoField(primary_key=True)
food_id = models.IntegerField()
food_price = models.IntegerField()
history_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s %s %s' % (self.id, self.food_id.name, self.food_id.price)
Thanks in advance.
I think you have two ways.
First, use __save__() method in model.
In django model, (models.Model), there's __save__ method. When model object saves, you can do an additional feature that inherits this method.
For more information, please check official docs save()
Second, use signals
Django supports many signals including django.db.models.signals. It includes pre_save, post_save, and so on. So before(after) saving model object, you can do sth in signals.
Please check official docs signals
I think __save__() method is more fit to your purpose. So your code will be...
class Food(models.Model):
food_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30, null=False, blank=False)
desc = models.TextField(max_length=200)
price = models.IntegerField(null=False, blank=False)
f_thumbnail = models.ImageField(upload_to=get_food_t_image_name)
DDD_data = models.ImageField(upload_to=get_food_d_image_name)
availability = models.BooleanField(default=True)
discount = models.IntegerField(default=0)
def __str__(self):
return '%s %s %s' % (self.name, self.category_id, self.price)
def save(self, force_insert=False, force_update=False, *args, **kwargs):
# you can add this for only existing model object
if self.pk:
# You can check if only 'price' field changed
_original_food = Food.objects.get(id=self.pk)
if _original_food.price != self.price:
# do somthing here like saving history...
History.objects.create(food_id=self.id, food_price=self.price)
super(Food, self).save(force_insert, force_update, *args, **kwargs)
super(Food, self).save(force_insert, force_update, *args, **kwargs)
This is just example. You can add & modify your code. I hope it helps you.

Django: object disappearing from model

I have a simple django model with an integer field status:
from django.db import models
class MyObject(models.Model):
dateDownloaded = models.DateField(null=True)
dateShared = models.DateTimeField(null=False)
sharedBy = models.CharField(max_length=100, null=False)
sharedTo = models.CharField(max_length=100, null=False)
token = models.CharField(max_length=200, null=False)
status = models.IntegerField(null=False, default=0)
def save(self, *args, **kwargs):
if not self.pk: # First time saved
self.status = 0
self.token = get_random_string(length=32)
super(MyObject, self).save(*args, **kwargs)
I can add objects to my model and I have a simple helper that counts the number of created objects. Now I also have a call that does the following:
def resetStatus(request):
myObjects = MyObject.objects.all()
for myObject in myObjects:
myObject.status = 0
myObject.save()
return HttpResponse("reset done")
Issue is, after calling this, from time to time all the objects from my database have disappeared. Maybe I have done something wrong with my objects in between but I have no idea what it could be. How can I go about debugging this ?

Trigger "auto_now" on related model

I would like the field "updated" to be triggered in the Tag model whenever a M2M relationship is added to a "Tag" in Movie.
I have made an attempt, but the error tells me: Movie matching query does not exist
class Tag(models.Model):
name = models.CharField("Name", max_length=5000, blank=True)
updated = models.DateTimeField(auto_now=True)
class Movie(models.Model):
title = models.CharField("Title", max_length=10000, blank=True)
tag = models.ManyToManyField('Tag', blank=True)
updated = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
orig = Movie.objects.get(pk=self.pk)
for t in self.tag.exclude(pk__in=orig.tag.values_list('pk', flat=True)):
t.save()
super(Movie, self).save(*args, **kwargs)
I also tried using m2m_chagned
def movie_changed(sender, **kwargs):
# Do something
pass
m2m_changed.connect(movie_changed, sender=Movie.tags.through)
However that also gave me "Movie" not defined.