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?
CodePudding user response:
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)
CodePudding user response:
you can use save to check slug before add like:
from django.utils.text import slugify
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
CodePudding user response:
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 theUser
model [Django-doc] directly. For more information you can see the referencing theUser
model section of the documentation.