djangodjango-mptt

Django doesn't recognize/can't find post_save edited field?


I'm trying to create a unique SlugField using a post_save model signal. If the SlugField already exists, a number should be appended to the slug to make it unique.

However, Django does not seem to to recognize when the SlugField already exists.

I'm using a single-tabled inherited MPTT model:

class Text(MPTTModel):
    type = models.CharField(max_length=255, blank=False)  # for STI. Essentially returns the class name.
    title = models.CharField(max_length=255, blank=True)
    slug = models.SlugField(max_length=255, blank=True)
    slug_order = models.CharField(max_length=255, blank=True)

    def __init__(self, *args, **kwargs):
        super(Text, self).__init__(*args, **kwargs)
        # If we don't have a subclass at all, then we need the type attribute to match
        # our current class.
        if not self.__class__.__subclasses__():
            self.type = self.__class__.__name__.lower()
        else:
            subclass = [
                x
                for x in self.__class__.__subclasses__()
                if x.__name__.lower() == self.type
            ]
            if subclass:
                self.__class__ = subclass[0]
            else:
                self.type = self.__class__.__name__.lower()

class Book(Text):
    objects = BookManager()

    class Meta:
        proxy = True

@receiver(post_save, sender=Book, dispatch_uid="create_book_slug")
def create_book_slug(sender, instance, **kwargs):
    slug = slugify(instance.title)
    slug_exists = Book.objects.filter(slug=slug).exists() # This always seems to be False
    counter = 1
    while slug_exists:
        counter += 1
        new_slug = f"{slug}-{counter}"
        slug_exists = Book.objects.filter(slug=new_slug).exists()
    if counter > 1:
        slug = f"{slug}-{counter}"
    instance.slug = slug

My test:

b1 = Book.objects.create(title="book book")
b2 = Book.objects.create(title="book book")

self.assertEqual(b1.slug, "book-book") # True
self.assertEqual(b2.slug, "book-book-2") # False - b2.slug gives "book-book"
self.assertEqual(b1.slug, b2.slug) # This is True... obviously not what I want.

self.assertEqual(Book.objects.filter(slug=b1.slug).exists(), True) # False. I have no idea why.

self.assertEqual(Book.objects.filter(title=b1.title).exists(), True) # True. This works.

I have to use post_save as I actually want to leverage the default MPTT fields (lft, rght, level, get_root() etc.) I actually have another STI model called Chapter that has will utilize slug_order:

class Chapter(Text):
    objects = ChapterManager()

    class Meta:
        proxy = True

@receiver(post_save, sender=Chapter, dispatch_uid="create_chapter_slug")
def create_chapter_slug(sender, instance, **kwargs):
    print(instance.get_root()) # This works and points to the Chapter's top parent
    print(instance.get_root().slug) # This doesn't work and returns nothing
    instance.slug = instance.get_root().slug

    # slug_order code below works
    order = instance.rght - instance.lft
    if instance.level == 1:
        instance.slug_order = order
    else:
        instance.slug_order = f"{instance.parent.slug_order}/{order}"


Solution

  • Looks like the the post_save signal doesn't actually save.

    i.e.

    b1 = Book.objects.create(title="book book")
    self.assertEqual(b1.slug, "book-book") # True
    
    b1.refresh_from_db()
    self.assertEqual(b1.slug, "") # True
    

    And adding instance.save() to the post_save receiver gives a recursion error.

    My initial problem is solved with using pre_save instead for the create_book_slug.

    My create_chapter_slug is actually a whole other problem (my method for calculating the order is wrong) that I will be posting on another thread...