0

I have a purpose about learning django and rest framework. So I faced a problem when I worked on a tutorial project. The slug value changing every time when the article object updating. If the title value has not changed, I want the slug value not to change either. How can I handle this process?

For example: When I update article but I haven't made any changes to the title field, the slug value also changes.

before update

"title" : "article 1",
"slug" : "article-1",

after update

"title" : "article 1",
"slug" : "article-1-1",

models.py

class Post(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    title = models.CharField(max_length=120)
    content = models.TextField()
    draft = models.BooleanField(default=False)  # taslağa kaydetme işlemi
    created = models.DateTimeField(editable=False)
    modified = models.DateTimeField()
    slug = models.SlugField(unique=True, max_length=150, editable=False)
    image = models.ImageField(
        upload_to="media/post/%Y/%m/%d/", blank=True, null=True)
    modified_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name="modified_by")

    def __str__(self):
        return self.title

    def get_slug(self):
        slug = slugify(self.title.replace("ı", "i"))
        unique = slug
        number = 1
        while Post.objects.filter(slug=unique).exists():
            unique = '{}-{}'.format(slug, number)
            number += 1
        return unique

    def save(self, *args, **kwargs):
        if not self.id:
            self.created = timezone.now()
        self.modified = timezone.now()
        self.slug = self.get_slug()

        return super(Post, self).save(*args, **kwargs)

serializers.py

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ["user", "title", "content", "created", "modified", "slug", "image", "modified_by"]

views.py PostUpdateAPIView Class

class PostUpdateAPIView(RetrieveUpdateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    lookup_field = "slug"
    permission_classes = [IsOwner]

    def perform_update(self, serializer):
        serializer.save(modified_by=self.request.user)
ATK
  • 103
  • 1
  • 11
  • well, you _are_, unconditionally, calling `self.get_slug()` in `save()`... – thebjorn Jun 26 '21 at 19:22
  • I know that but I didn't know how to control it until just now. I have knowledge with help of Maciej M's answer. Thank you for your help. – ATK Jun 27 '21 at 05:54

1 Answers1

1

save() method is being called on object creation and update. Therefore, you should call get_slug method only when value of title hasn't changed.

    def save(self, *args, **kw):
        if not self.id:
            self.created = timezone.now()
            self.slug = self.get_slug()
        else:
            orig = Post.objects.get(pk=self.pk)
            if orig.title != self.title:
                self.slug = self.get_slug()
        self.modified = timezone.now()

        return super(Post, self).save(*args, **kwargs)

Please be informed that this solution creates additional call to database. Better, but a little more complex solution is to use post_save signal. When saving, how can you check if a field has changed?

Maciej M
  • 786
  • 6
  • 17
  • It works for me, thank you for your help. I thought this solution but I didn't know how to prevent that additional call. Now I have knowledge about that topic. Also thank you for this link. I applied your solution and working pretty well. It's about tutorial project that's why an additional call isn't a problem for now. However, if I work on a real project, I must pay attention to this matter, I will. – ATK Jun 27 '21 at 05:57