Home > Blockchain >  Django unique slug field for two or more models
Django unique slug field for two or more models

Time:11-27

I have such structure:

class Category(models.Model):
    name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
    parent = models.ForeignKey('self', blank=True, null=True,
                               related_name='children',
                               on_delete=models.CASCADE
                               )
    slug = models.SlugField(max_length=255, null=False, unique=True)


class Product(models.Model):
    name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
    to_category = models.ForeignKey(Category, on_delete=models.SET_NULL,
                                    blank=True, null=True,
                                    )
    slug = models.SlugField(max_length=255, null=False, unique=True)

I have created one category with slug "test". When I try to create new category with slug "test" I got warning message and it is Ok. But If I try to create product with slug "test" I dont have warning and this is not good in my case. Is there a solution or method to validate slug field for uniqueness with Product and Category model?

CodePudding user response:

You can override the save method for each, and then check if the given slug already exists for a product or category.

def is_slug_unique(slug):
    product_exists = Product.objects.filter(slug=slug).exists()
    category_exists = Category.objects.filter(slug=slug).exists()
    if product_exists or category_exists:
        return False
    else:
        return True

class Category(models.Model)
    ...

    def save(self, *args, **kwargs):
        slug_unique = is_slug_unique(self.slug)
        if not slug_unique:
            # do something when the slug is not unique
        else:
            # do something when the slug is unique
            super().save(*args, **kwargs)

class Product(models.Model)
    ...

    def save(self, *args, **kwargs):
        slug_unique = is_slug_unique(self.slug)
        if not slug_unique:
            # do something when the slug is not unique
        else:
            # do something when the slug is unique
            super().save(*args, **kwargs)


CodePudding user response:

An idea might be to create a Slug model that stores all the slugs, optionally with a backreference to the object:

class Slug(models.Model):
    slug = models.SlugField(max_length=255, primary_key=True)

Then the slugs in your models are ForeignKeys to that Slug model, and you check if such slug already exists:

from django.core.exceptions import ValidationError


class Product(models.Model):
    name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
    to_category = models.ForeignKey(
        Category, on_delete=models.SET_NULL, blank=True, null=True
    )
    slug = models.ForeignKey(Slug, on_delete=models.PROTECT)

    def validate_slug(self):
        if self.pk is not None and Slug.objects.filter(pk=self.slug_id).exclude(
            product__pk=self.pk
        ):
            raise ValidationError('The slug is already used.')

    def clean(self, *args, **kwargs):
        self.validate_slug()
        return super().clean(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.validate_slug()
        return super().save(*args, **kwargs)

That being said, often overlapping slugs for different entity types are allowed.

  • Related