override the save method in django - django

I want the create field to change when the price field changes, that is, if I change the rest of the fields, the create field will not change and will remain the same.
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.IntegerField()
create = models.DateTimeField(auto_now=True)
discount = models.IntegerField()
information = models.TextField()

You can try a signal like this:
from django.dispatch import receiver
from django.db.models.signals import pre_save
from django.utils import timezone
#receiver(pre_save, sender=Product)
def update_create_field(sender, instance, **kwargs):
try:
_pre_save_instance = Product.objects.get(pk=instance.pk)
if _pre_save_instance.price != instance.price:
instance.create = timezone.now() #here you can specify any value you want
except Exception as e:
print('Error updating create field!')
print(e)

You can save your initial values in __init__, and check them before save:
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.IntegerField()
# Remove auto_now=True, it does not allow to set values by yourself.
create = models.DateTimeField()
discount = models.IntegerField()
information = models.TextField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.create = self.create or datetime.now(UTC)
self._initial_price = self.price
def save(self, *args, **kwargs):
if self.price != self._initial_price:
self.create = datetime.now(UTC)
super().save(*args, **kwargs)

You can overwrite __init__ and save on the model as an easy alternative
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._price_dirty = self.price
def save(self, *args, **kwargs):
if self._price_dirty != self.price:
self.create = timezone.now()
super().save(*args, **kwargs)

Related

Recursively going through a model

I have a model like so:
class CustomUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
slug = models.SlugField(blank=True)
def lastfolder(self):
try:
return self.folder_set.all().order_by('-created_at')[0]
except IndexError:
return None
def lastdrive(self):
try:
return self.drive_set.all().order_by('-created_at')[0]
except IndexError:
return None
def lastfile(self):
try:
return self.drive_set.all().order_by('-created_at')[0]
except IndexError:
return None
def save(self, *args, **kwargs):
self.slug = slugify(self.user.username)
return super().save(*args, **kwargs)
def get_absolute_url(self):
kwargs = {'slug' : self.slug}
return reverse('', kwargs=kwargs)
def __str__(self):
return self.user.username
class Drive(models.Model):
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
cover_picture = models.ImageField(upload_to=f'media/{user_path}', default="media/drive.png")
created_at = models.DateTimeField()
slug = models.SlugField(blank=True)
def get_absolute_url(self):
kwargs = {'slug' : self.slug}
return reverse('', kwargs=kwargs)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
return super().save(*args, **kwargs)
class Meta:
unique_together = [['owner', 'name']]
ordering = ('name',)
class Folder(models.Model):
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
drive = models.ForeignKey('Drive', on_delete=models.CASCADE)
name = models.CharField(max_length=64)
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True)
cover_picture = models.ImageField(upload_to=f'media/{user_path}', default="media/folder-white.png")
created_at = models.DateTimeField()
path = models.CharField(max_length=2048, blank=True)
slug = models.SlugField(blank=True)
def __str__(self):
return self.name
def get_absolute_url(self):
kwargs = {'slug' : self.slug}
return reverse('', kwargs=kwargs)
def get_path(self):
yield self.name
try:
yield from get_path(self.parent)
except:
pass
def complete_get_path(self):
text = []
for i in self.get_path():
text.append(i)
text.reverse()
return "/".join(text)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
self.path = self.complete_get_path()
return super().save(*args, **kwargs)
class Meta:
unique_together = [['parent', 'name']]
ordering = ('name',)
If I create a Folder instance with a name Programming and another Folder instance with a name Python and parent with reference to Programming, it is supposed to populate the path field for Programming with "Programming" and Python with "Programming/Python". Unfortunately, it does not work
My emphasis is on the Folder model and the methods: get_path, complete_get_path, and save.
After saving the model, and checking the path field in the admin, it just shows only the name attribute not return the actual generated path of the folder.
How do I go about it.
The problem is that get_path isn't defined and the blanket try/except is eating your error. Your method should be:
def get_path(self):
yield self.name
yield from self.parent.get_path()
Here's a slight optimization. If you yield the parent's path's first, you don't have to reverse the list of folder names and then can simply join it at the end.
def get_path(self):
yield from self.parent.get_path()
yield self.name
def complete_get_path(self):
return "/".join(self.get_path())

Slug field creation using ID

this the postmodel and trying to create a slug unique field
The Slug field creation error str(self.id) is returning "none" where as the seld.title is working correctly
class postmodel(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(User,default=None)
body = models.TextField()
slug = models.SlugField(unique=True)
subject=models.TextField()
timestamp = models.DateTimeField(auto_now_add=True,auto_now=False)
updated = models.DateTimeField(auto_now=True,auto_now_add=False)
#models.permalink
def get_absolute_url(self):
return ('blog_post_dpetail', (),
{
'slug': self.slug,
})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)+"-"+str(self.id)
super(postmodel, self).save(*args, **kwargs)
You cannot get self.id until the object is saved in your database. So you can modify your save() method to:
def save(self, *args, **kwargs):
super(postmodel, self).save(*args, **kwargs) # Save your model in order to get the id
# Then implement the logic for the slug
if not self.slug:
self.slug = slugify(self.title) + "-" + str(self.id)
self.save() # Call save() again in order to save the slug
self.id is auto increment field in the database, it is exist only after you save object in the database, so you have created a stalemate:
You can save object if slug is none and you can create slug while you not save the object.
hope it help you.
so I came up with a good enough solution based on previous answers.
In models.py
this allows slugfield to be blank when you first submit the form...
slug = models.SlugField(unique=True, allow_unicode=True, blank=True)
override the save function to 1. create the post 2. set the slug of the instance of the post to title + id of the post.
def save(self, *args, **kwargs):
super(Post, self).save(*args, **kwargs)
self.slug = slugify(self.title) + "-" + str(self.id)
return super(Post, self).save(*args, **kwargs)
Hope it helps.

Cannot Edit model fields/Django Signal to update image likes wont work/

I implemented a signal with an m2mchanged receiver to update a PositiveIntegerField in my Image model. This PositiveIntegerField is for total_likes of the image but upon implementing the signal I realized that the total_likes field did not change from zero. I started experimenting and found that this value will not change even if I change it in the shell or the admin page itself.
I dont think this is a problem with the signal/receiver but rather something else happening that wont let the value be change in the database.
Here is My code.
models.py
class Image(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name='images_created')
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200,
blank=True)
url = models.URLField()
image = models.ImageField(upload_to='images/%Y/%m/%d')
description = models.TextField(blank=True)
created = models.DateField(auto_now_add=True, db_index=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Image, self).save(*args, *kwargs)
users_like = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='images_liked',
blank=True)
def get_absolute_url(self):
return reverse('images:detail', args=[self.id, self.slug])
tags = TaggableManager()
total_likes = models.PositiveIntegerField(db_index=True, default=0)
I can post the signals.py files or any other code if necessary.
Thank you!
Solution
My problem was in my overridden save method, I changed it to the following.
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Image, self).save(*args, *kwargs)
elif self.slug != slugify(self.title):
self.slug = slugify(self.title)
super(Image, self).save(*args, *kwargs)
else:
super(Image, self).save(*args, *kwargs)
Correct and simple solution here will be:
def save(self, *args, **kwargs):
if not self.id: # don't allow to change slug after object created
self.slug = slugify(self.title)
super(Image, self).save(*args, **kwargs)

optimizing django code model

I want to optimizing this code. I think the best solution is to use the method pre_save and not override the save method. This is the function that delete the old image when in editing it upload new image
def delete_old_image(sender, instance):
try:
obj = sender.objects.get(id=instance.id)
except sender.DoesNotExist:
pass
else:
if not obj.image == instance.image:
try:
os.remove(obj.image.path)
except:
pass
under the code of the model
class Service(models.Model):
title= models.CharField(max_length=170)
slug = models.SlugField(max_length=200, blank=True, unique=True, editable=False)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Service, self).save(*args, **kwargs)
class Portfoglio(models.Model):
title= models.CharField(max_length=170, unique=True)
slug = models.SlugField(max_length=200, blank=True, unique=True, editable=False)
image=models.ImageField(upload_to = 'images/' , default= 'images/foto.jpg', verbose_name='upload')
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
if self.id is not None:
delete_old_image(Portfoglio, self)
super(Portfoglio, self).save(*args, **kwargs)
class Image(models.Model):
title= models.CharField(max_length=200)
image=models.ImageField(upload_to = 'images/' , default= 'images/foto.jpg', verbose_name='upload')
def save(self, *args, **kwargs):
if self.id is not None:
delete_old_image(Portfoglio, self)
super(Image, self).save(*args, **kwargs)
class Team(models.Model):
name= models.CharField(max_length=200)
image= models.ImageField(upload_to = 'images/' , default= 'images/foto.jpg', verbose_name='upload')
def save(self, *args, **kwargs):
if self.id is not None:
delete_old_image(Team, self)
super(Team, self).save(*args, **kwargs)
from django.db.models.signals import pre_save
from django.dispatch import receiver
#receiver(pre_save)
def pre_delete_old_image(sender, instance, created, **kwargs):
if sender not in [Service, Portfoglio, Image, Team]:
return
if getattr(sender, 'slug', False):
instance.slug = slugify(instance.title)
if not created and getattr(sender, 'image', False):
delete_old_image(sender, instance)
def delete_old_image(sender, instance):
try:
obj = sender.objects.get(id=instance.id)
except sender.DoesNotExist:
pass
else:
if obj.image != instance.image:
try:
os.remove(obj.image.path)
except Exception as e:
pass

Overload ModelMultipleChoiceField __init__ for many to many

I have something working but I am sure it's not the best way of doing it because I have duplicated code.
It's a widget to display checkboxes for groups that clients can belong to. A client can belong to multiple groups.
I am sure I have messed up the overloading of the widget somehow.
models.py
class Client(models.Model):
name = models.CharField(u'Name', max_length=250, unique=True)
groups = models.ManyToManyField('ClientGroup', related_name="client")
def __unicode__(self):
return self.name
class ClientGroup(models.Model):
name = models.CharField(u'Name', max_length=250, unique=True)
def __unicode__(self):
return self.name
forms.py
# -*- coding: utf-8 -*-
from django import forms
from models import ClientGroup
class ClientGroupForm(forms.ModelForm):
class Meta:
model = ClientGroup
fields = ('groups',)
groups = forms.ModelMultipleChoiceField(queryset=ClientGroup.objects.all(),
widget=forms.CheckboxSelectMultiple(),
label='',
required=False)
def __init__(self, client_id = None, *args, **kwargs):
super(ClientGroupForm, self).__init__(*args, **kwargs)
if client_id:
self.fields['groups'] = forms.ModelMultipleChoiceField(queryset = ClientGroup.objects.all(),
initial=ClientGroup.objects.filter(client = client_id),
widget=forms.CheckboxSelectMultiple(),
label='',
required=False)
def __unicode__(self):
return self.groups
views.py
def client(request, pk = 0):
client={}
if pk != 0:
client = Client.objects.get(pk = pk)
groupsForm = ClientGroupForm(pk)
context = {'client':client, 'groupsForm': groupsForm}
return render(request, 'client.html', context)
Can anyone show me a better way of coding the forms.py?
Thanks
You could replace
class ClientGroupForm(forms.ModelForm):
# stuff
def __init__(self, client_id = None, *args, **kwargs):
super(ClientGroupForm, self).__init__(*args, **kwargs)
if client_id:
self.fields['groups'] = forms.ModelMultipleChoiceField(queryset = ClientGroup.objects.all(),
initial=ClientGroup.objects.filter(client = client_id),
widget=forms.CheckboxSelectMultiple(),
label='',
required=False)
with
class ClientGroupForm(forms.ModelForm):
# stuff
def __init__(self, *args, **kwargs):
self.client_id = kwargs.get('client_id', None)
super(ClientGroupForm, self).__init__(*args, **kwargs)
if client_id:
self.fields['groups'].queryset = ClientGroup.objects.filter(client=client_id)