Django. Override Save for Model

Django. Override save for model

Some thoughts:

class Model(model.Model):
_image=models.ImageField(upload_to='folder')
thumb=models.ImageField(upload_to='folder')
description=models.CharField()

def set_image(self, val):
self._image = val
self._image_changed = True

# Or put whole logic in here
small = rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)

def get_image(self):
return self._image

image = property(get_image, set_image)

# this is not needed if small_image is created at set_image
def save(self, *args, **kwargs):
if getattr(self, '_image_changed', True):
small=rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
super(Model, self).save(*args, **kwargs)

Not sure if it would play nice with all pseudo-auto django tools (Example: ModelForm, contrib.admin etc).

Django override save method: to return super().save() or not to return?

You should not use return super().save(*args, **kwargs). Because save method does not return anything in Django's models.Model class. Here is the reference to GitHub repository. So you should always use:

def save(self, *args, **kwargs):
#some code
super().save(*args, **kwargs)
# some more code

how to override django save method to update some field dynamically?

Solution One:

I think instead of changing in Ledger model, you should change in Expense model, like this:

class Expense(models.Model):
...
def save(self, *args, **kwargs):
self.payment_option.amount_to_pay = self.payment_option.amount_to_pay + self.amount_to_pay
self.payment_option.save()
super(Expense, self).save(*args, **kwargs)

Solution Two:

But to be honest, Solution One does not seem good to me. Reason is that you are saving same data in 2 places(in both expense and ledger). Instead, it should be once, then the amount_to_pay value in Ledger should be calculated dynamically. Like this:

from django.db.models import Sum

class Ledger(...):

@property
def amount_to_pay(self):
# I am using a property method to show the amount_to_pay value.
# FYI: in this way, you need to remove amount_to_pay field from Ledger model
return self.opening_balance - self.expense_set.all().aggregate(a_sum=Sum('amount_to_pay')).get('a_sum', 0)

In that way, with each ledger, the value amount_to_pay will be dynamically calculated at runtime. For example:

 for l in Ledger.objects.all():
l.amount_to_pay

Solution Three:

If you are wary of making DB hits with each l.amount_to_pay(as it calculates amount_to_pay from DB dynamically) from previous solution, then you can always annotate the value. Like this:

For this solution, you need to change your Expense model and add a related_name:

class Expense(models.Model):
pay_from = models.CharField(max_length=200)
payment_option = models.ForeignKey('Ledger', on_delete=models.CASCADE, related_name='expenses')

Then use that related_name in query like this(FYI: You can't keep def amount_to_pay(...) method in Ledger model for the following usage example):

from django.db.models import Sum, F, ExpressionWrapper, IntegerField

ledgers = Ledger.objects.all().annotate(expense_sum=Sum('expenses__amount_to_pay')).annotate(amount_to_pay=ExpressionWrapper(F('opening_balance') - F('expense_sum'), output_field=IntegerField()))

# usage one
for l in ledgers:
l.amount_to_pay

# usage two
ledgers.values('amount_to_pay')

Overriding Django ModelForm save method and deleting old values(files)

Try using this, it works just fine in terms of auto deleting old files and pictures.
https://pypi.org/project/django-cleanup/



Related Topics



Leave a reply



Submit