Slug is not created correctly due to using django parler
I have a problem creating a slug based on the title and pk, due to the use of parler.
I'm using Parler for multilinguality in my API, and when I try to create a slug like
title-pk
I'm getting various bugs.
My model:
class Events(TranslatableModel):
...
translations = TranslatedFields(
title=models.CharField(max_length=255, verbose_name=_("Event title")),
body=MDTextField(verbose_name=_("Event body")),
)
slug = models.SlugField(blank=True, max_length=255, verbose_name=_("Event slug"))
...
So, I tried using the save() method, but I got a slug like -None
or -pk
.
def save(self, *args, **kwargs):
if not self.slug:
title = self.safe_translation_getter("title", language_code="uk")
base_slug = slugify(title)
self.slug = f"{base_slug}-{self.pk}"
super().save(*args, **kwargs)
I also used a signal, which also did not work.
@receiver(post_save, sender=Events)
def generate_slug_after_translation(sender, instance, **kwargs):
if not instance.slug:
uk_title = instance.safe_translation_getter("title", language_code="uk")
if uk_title:
base_slug = f"{uk_title}-{instance.pk}"
instance.slug = slugify(base_slug)[:255]
instance.save(update_fields=["slug"])
What did I do wrong, and what should I do next?
Thanks for the suggestion in the comments — debugging helped me find the issue.
So, the issue. The save()
method didn’t generate the slug
correctly because it was being executed before the object was saved to the database — which means pk
was None
and the translated title
wasn't guaranteed to exist yet.
I tried calling super().save(*args, **kwargs)
before accessing the translated title
, like this:
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.slug:
title = self.safe_translation_getter("title", language_code="uk")
base_slug = slugify(title)
logging.debug(f"[DEBUG] Event base_slug: {base_slug}")
self.slug = f"{base_slug}-{self.pk}"
logging.debug(f"[DEBUG] Event slug: {self.slug}")
super().save(*args, **kwargs)
But the logs showed this:
[DEBUG] Event base_slug:
[DEBUG] Event slug: -9
Which confirmed that title
was still empty.
Working solution: use post_save signal
I found the root of the issue: the generated slug was incorrect when using Cyrillic characters.
My previous signal produced the following logs:
[DEBUG] base_slug: тест-11
[DEBUG] instance.slug: 11
Turns out, slugify()
doesn't transliterate non-ASCII characters by default.
I installed unidecode
and used it to transliterate the base string before passing it to slugify()
:
from unidecode import unidecode
@receiver(post_save, sender=Event)
def generate_slug_after_translation(sender, instance, **kwargs):
if not instance.slug:
uk_title = instance.safe_translation_getter("title", language_code="uk")
if uk_title:
base_slug = f"{uk_title}-{instance.pk}"
slug = slugify(unidecode(base_slug))[:255]
logging.debug(f"[DEBUG] instance.slug: {slug}")
instance.slug = slug
instance.save(update_fields=["slug"])
Now the Cyrillic title gets properly transliterated to Latin characters in the slug: "тест-11" → "test-11"