Search code examples
pythondjangoslugsoft-deleteslugify

Django AutoSlugField not considering soft deleted instances by Django Safe Delete


In our model we have a name and slug fields. This is how it looks like:

class MyModel(CommonInfo, SafeDeleteModel):
   name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
   slug = AutoSlugField(_('Url'), populate_from='name', unique=True,)

For the slug field we generate an unique slug every time our model is saved.

We are also using Django Safe Delete library to soft delete our model:

Django Safe Delete

Django Autoslug

That means that for example if we create a new instance of our model with Name "My Model" it will auto generate a slug that will look like this: "/my-model".

Now let's say we "soft delete" this instance with the slug "/my-model". In our database there will be a property deleted which contains the date when the model was deleted. We don't show this one in our application, it is completely ignored (because it is soft deleted, that's fine).

The problem is that next time we create another one with the same name "My Model" it will auto generate the slug "/my-model" again, not considering that there is already one (which is soft deleted) with the same name and slug. We would need something like "/my-model-1" or whatever that is unique.

We are missing the connection between the autoslug and the safe-delete libraries, somehow the autoslug needs to know that there might be soft deleted ones and consider them when generating the unique slug.

Any help would be really appreciated and please consider that we are totally new in Django / Python.

if this doesn't work, our workaround will be generating the slug using 2 fields (name & id). It will generate a slug that will look like this: "/my-model/12345" and will be always unique since id is unique.


Solution

  • I think we found it.

    We needed to create a new Manager that can see all the instances, even the soft deleted ones:

    class MyModelAllManager(SafeDeleteManager):
    _safedelete_visibility = DELETED_VISIBLE
    

    Then in our model we pass it to the AutoSlugField function:

    class MyModel(CommonInfo, SafeDeleteModel):
    
       # We don't know why but it only works if we create a variable and assign the Manager to it. It doesn't work if we pass the Manager directly to the AutoSlugField function.
       all_objects = MyModelAllManager()
    
       name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
       slug = AutoSlugField(_('Url'), populate_from='name', unique=True, manager=all_objects)
    

    That does the magic.