Why doesn't my save method work in the admin? - django

In my model I overwrite the save-method for my blog model to auto-populate the slug field using the slugify method.
class BlogPost(models.Model):
title = models.CharField(max_length=100,unique=True)
slug = models.SlugField(max_length=100,unique=True)
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(Author)
body = models.TextField()
category = models.ForeignKey(BlogCategory)
def save(self, *args, **kwargs):
if not self.id:
# Newly created object, so set slug
self.slug = slugify(self.title)
super(BlogPost, self).save(*args, **kwargs)
But creating a new object in the admin interface doesn't work without either setting the slug field manually or doing something like
class BlogPostAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
Basically I currently have the same functionality defined twice. Any ideas on how to avoid this? And: why doesn't work my own save method in the admin?

You should put blank=True in the definition of the slug field.

Related

Queryset from non-related model in __init__ modelform method

I have two model classes. They are not related models (no relationship).
# models.py
class Model1(models.Model):
description = models.TextField()
option = models.CharField(max_length=64, blank=False)
def __str__(self):
return self.option
class Model2(models.Model):
name = models.CharField(max_length=64, blank=False)
def __str__(self):
return self.name
I have respective form from where I am submitting and saving data in my table. I want to use my Model2 data to fill-in 'option' field as select field, so I am introducing below init method.
# forms.py
class Model1Form(forms.ModelForm):
def __init__(self, *args, **kwargs):
all_options = Model2.objects.all()
super(Model1Form, self).__init__(*args, **kwargs)
self.fields['option'].queryset = all_options
class Meta:
model = Model1
fields = ('description', 'option')
It does not render the dropdown on my template, so I am wondering whether it is right way to address the issue (acknowledging that models are not related to each other).

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)

Update datetimefiled of all related models when model is updated

I have two models (Post and Display). Both have Datetime-auto fields. My problem is that i want to update all display objects related to a post, once a post is updated.
I have read here that you could override one models save method, but all the examples are About updating the model with the foreign key in it and then call the save method of the other model. In my case it's the other way arround. How can i do this ?
class Post(models.Model):
title = models.CharField(max_length=40)
content = models.TextField(max_length=300)
date_posted = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rooms = models.ManyToManyField(Room, related_name='roomposts', through='Display')
def __str__(self):
return self.title
def get_absolute_url(self):
return "/post/{}/".format(self.pk)
class Display(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
isdisplayed = models.BooleanField(default=0)
date_posted = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.isdisplayed)
i want to update the date_posted of all related Display-objects once their related post is changed. I do not know if overriding the save-method works here.
in this case you should have a look at django's reverse foreign key documentation
https://docs.djangoproject.com/en/2.2/topics/db/queries/#following-relationships-backward
in your case you can override the save method on your Post model
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
#either: this excutes many sql statments
for display in self.display_set.all():
display.save()
#or faster: this excute only one sql statements,
#but note that this does not call Display.save
self.display_set.all().update(date_posted=self.date_posted)
The name display_set can be changed using the related_name option
in Display, you can change it:
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='displays')
Then, instead of using self.display_set in your code, you can use self.displays
Overriding save method works, but that's not were you should go, imo.
What you need is signals:
#receiver(post_save, sender=Post)
def update_displays_on_post_save(sender, instance, **kwargs):
if kwargs.get('created') is False: # This means you have updated the post
# do smth with instance.display_set
Usually it goes into signals.py.
Also you need to include this in you AppConfig
def ready(self):
from . import signals # noqa

Django model audit mixin

Hello I wanted to know how to create a few fields and convert them into a mixin.
Let's say I have the following.
class Supplier(models.Model):
name = models.CharField(max_length=128)
created_by = models.ForeignKey(get_user_model(), related_name='%(class)s_created_by')
modified_by = models.ForeignKey(get_user_model(), related_name='%(class)s_modified_by')
created_date = models.DateTimeField(editable=False)
modified_date = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created_date = timezone.now()
self.modified_date = timezone.now()
return super(Supplier, self).save(*args, **kwargs)
I want to create a mixin to avoid writing every time the last 4 fields into different models.
Here is the mixin I would create:
class AuditMixin(models.Model):
created_by = models.ForeignKey(get_user_model(), related_name='%(class)s_created_by')
modified_by = models.ForeignKey(get_user_model(), related_name='%(class)s_modified_by')
created_date = models.DateTimeField(editable=False)
modified_date = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created_date = timezone.now()
self.modified_date = timezone.now()
return super(Supplier, self).save(*args, **kwargs)
class Supplier(AuditMixin):
name = models.Charfield(max_length=128)
How can I make sure that the related_name is relevant to the class the mixin is included into? Also in the save function, How can I make sure the class the mixin is included into is returned (as per the last line)?
Thank you!
Firstly, in any super call, you must always use the current class. So it will always be super(AuditMixin, self)... and your question does not apply.
Django itself takes care of substituting the current class name in related_name if you use the %(class)s syntax, which you have, so again there is nothing else for you to do. See the model inheritance docs.

Django: Generate slug before save model

When i save the model from admin interface a first time i need to autogenerate the slug. I use slugify and pre_save signal and my slug field have unique=True option. But when i push save button the object slugfield raise validation error (Required field) because field is unique, i think. I thought that pre_save goes before validation. Or i am wrong?
# models.py
class Slugified(models.Model):
slug = models.CharField(unique=True, max_length=50)
class Meta:
abstract = True
class Post(Slugified):
title = models.Charfield(max_length=50)
#receiver(pre_save, sender=Post)
def save_slug(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.title)
The form automatically generated by the admin sees the slug field as a required field. The pre_save signal receiver works correctly, but the code never tries to save the model as the form doesn't validate.
The solution to this is to all blank values:
class Slugified(models.Model):
slug = models.CharField(unique=True, max_length=50, blank=True)
This way, the field is not required in the form and the slug gets set before the instance is saved.
Also, please note that your code does not correctly handle duplicate slugs. If two post titles generate identical slugs, an IntegrityError would be raised. This is easier solved in the save method than in the pre_save signal receiver:
class Slugified(models.Model):
slug = models.CharField(unique=True, max_length=50, blank=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
while True:
try:
return super(Slugified, self).save(*args, **kwargs)
except IntegrityError:
# generate a new slug