Issue
I have the follow models:
class Category(models.Model):
....
posts = models.PositiveIntegerField(_('numero post'), default=0)
....
class Post(models.Model):
....
categories = models.ManyToManyField(Category, blank=True, verbose_name=_('categorie'))
....
the Category.posts
field counts the number of the posts related to it's own category. I increase the counter into the CreateView
in the following way:
class CreatePostView(LoginRequiredMixin, CreateView):
model = Post
...
def form_valid(self, form, **kwargs):
...
categories = self.object.categories.all()
for category in categories:
category.posts = category.posts + 1
category.save()
self.object.save()
return super(CreatePostView, self).form_valid(form)
Whene i delete the blog post i need to decrement the counter when the Post
is deleted. I've tried to do in this way:
class DeletePostView(LoginRequiredMixin, DeleteView):
model = Post
...
def form_valid(self, form, **kwargs):
...
categories = post.categories.all()
for category in categories:
if (category.posts - 1) > 0:
category.posts = category.posts - 1
category.save()
return post
But it doesn't work, because the record is deleted before that this method is called.
How can i do this operation before that the record is deleted?
Or there is a built-in way to get the number of related records from Category
?
Solution
There is no form_valid
for a DeleteView
since a DeleteView
has no form.
What we can do is add some logic in the .delete(..)
function, which is the function that deletes the object. The DeleteView
implements this as:
def delete(self, request, *args, **kwargs): """ Call the delete() method on the fetched object and then redirect to the success URL. """ self.object = self.get_object() success_url = self.get_success_url() self.object.delete() return HttpResponseRedirect(success_url)
So we can implement this as:
from django.db.models import F
class DeletePostView(LoginRequiredMixin, DeleteView):
model = Post
# ...
def delete(self, *args, **kwargs):
self.object = self.get_object()
self.object.post.categories.all().update(posts=F('posts')-1)
return super(DeletePostView, self).delete(*args, **kwargs)
The above will update the categories in "bulk" by decrementing the posts
field. Here howver, it can reach zero (which is impossible in your given code).
If you want to prevent that, we could use Greatest
[Django-doc] to avoid that:
from django.db.models import F, Value
from django.db.models.functions import Greatest
class DeletePostView(LoginRequiredMixin, DeleteView):
model = Post
# ...
def delete(self, *args, **kwargs):
self.object.post.categories.all().update(posts=Greatest(F('posts')-1, Value(1)))
return super(DeletePostView, self).delete(*args, **kwargs)
Answered By - Willem Van Onsem
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.