简体   繁体   中英

Django: how to create slugs in django?

i want to create a slug in django, i have used the slug = models.SlugField(unique=True) . Now when i create a post with a slug of learning-to-code it works, but if i create another post with the same slug learning-to-code , it shows an error like Unique Constraint Failed . But i want to create posts with the same slug, is there a way to make slugs unique only to the time a post was created?

this is how my model looks

class Article(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField(blank=True, null=True)
    slug = models.SlugField(unique=True)
    user = models.ForeignKey('userauths.User', on_delete=models.SET_NULL, null=True)

How can i go about achieving this?

If you just like to convert the title into a slug but do not want it as an alternative to the oprimary key, you could use the slugify utility of django to convert the title on save and store it in a generic CharField .

https://docs.djangoproject.com/en/4.0/ref/utils/#django.utils.text.slugify

from django.utils.text import slugify

class Article(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField(blank=True, null=True)
    slug = models.CharField(max_length=300)
    user = models.ForeignKey('userauths.User', on_delete=models.SET_NULL, null=True)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        return super().save(*args, **kwargs)

you can use save to check slug before add like:

    from django.utils.text import slugify
    import itertools
    # other 
    
    class Article(models.Model):
        title = models.CharField(max_length=200)
        description = models.TextField(blank=True, null=True)
        slug = models.SlugField(unique=True)
        user = models.ForeignKey('userauths.User', on_delete=models.SET_NULL, null=True)
    
        def save(self, *args, **kwargs):
            if self._state.adding:
                self._generate_slug()
            super(Article, self).save(*args, **kwargs)
    
        def _generate_slug(self):
            if self.slug:
                slug_candidate = slug_original = self.slug
            else:
                # To Generate New Slug If None, You can change
                slug_candidate = slug_original = slugify(self.title, allow_unicode=True)
                if not slug_candidate:
                    slug_candidate = slug_original = lambda: random.randint(1, 10000000)
            # Check if Slug exists and add ( learning-to-code-1, learning-to-code-2,................)
            for i in itertools.count(1):
                if not Article.objects.filter(slug=slug_candidate).exists():
                    break
                slug_candidate = '{}-{}'.format(slug_original, i)
    
            self.slug = slug_candidate

Creating posts with the same slug makes not much sense, since a slug is used to determine what Article is used. If two items have learning-to-code , then you can not determine which of the two Article s is the right one.

If your Article for example has a DateField , you can use this such that is works with both the date and the slug to determine the Article . The easist way to achieve that is likely with the django-autoslug package [PyPi] . This will work with a UniqueConstraint [Django-doc] , such that the combination of the date and the slug is unique, so:

from autoslug import AutoSlugField
from django.conf import settings

class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = AutoSlugField(populate_from='title', unique_with=['publish_date'])
    description = models.TextField(blank=True, null=True)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True
    )
    publish_date = models.DateField(auto_now_add=True)

The urls.py will need to add specifications for the date, so:

path('<int:year>/<int:month>/<int:date>/<slug:slug>', some_view, name='article_detail')

then in a DetailView , or other views, we can filter on the QuerySet with the date:

from datetime import date
from django.views.generic import DetailView

class ArticleDetailView(DetailView):
    model = Article
    
    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs).filter(
            publish_date=date(self.kwargs['year'], self.kwargs['month'], self.kwargs['day'])
        )

Note : It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation .

from django.utils.text import slugify

Add this function to your class

def save(self,*args,**kwargs):
    self.slug=slugify(self.slug)
    super().save(*args,**kwargs)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM