How to create a unique slug in Django

2019-01-21 23:49发布

I am trying to create a unique slug in Django so that I can access a post via a url like this: http://www.example.com/buy-a-new-bike_Boston-MA-02111_2

The relevant models:

class ZipCode(models.Model):
    zipcode = models.CharField(max_length=5)
    city = models.CharField(max_length=64)
    statecode = models.CharField(max_length=32)

class Need(models.Model):
    title = models.CharField(max_length=50)
    us_zip = models.CharField(max_length=5)
    slug = ?????

    def get_city():
        zip = ZipCode.objects.get(zipcode=self.us_zip)
        city = "%s, %s %s" % (zip.city, zip.statecode, zip.zipcode)
        return city

A sample ZipCode record:

  • zipcode = "02111"
  • city = "Boston"
  • statecode = "MA"

A sample Need record:

  • title = "buy a new bike"
  • us_zip = "02111"
  • slug = "buy-a-new-bike_Boston-MA-02111_2" (desired)

Any tips as to how to create this unique slug? Its composition is:

  • Need.title + "_" + Need.get_city() + "_" + an optional incrementing integer to make it unique. All spaces should be replaced with "-".

NOTE: My desired slug above assumes that the slug "buy-a-new-bike_Boston-MA-02111" already exists, which is what it has the "_2" appended to it to make it unique.

I've tried django-extensions, but it seems that it can only take a field or tuple of fields to construct the unique slug. I need to pass in the get_city() function as well as the "_" connector between the title and city. Anyone solved this and willing to share?

Thank you!

UPDATE

I'm already using django-extensions for its UUIDField, so it would be nice if it could also be usable for its AutoSlugField!

7条回答
女痞
2楼-- · 2019-01-21 23:57

My little code:

def save(self, *args, **kwargs):
    strtime = "".join(str(time()).split("."))
    string = "%s-%s" % (strtime[7:], self.title)
    self.slug = slugify(string)
    super(Need, self).save()
查看更多
兄弟一词,经得起流年.
3楼-- · 2019-01-21 23:57

Django provides a SlugField model field to make this easier for you. Here's an example of it in a "blog" app's

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField(blank=True)

    slug = models.SlugField(unique=True)

    @models.permalink
    def get_absolute_url(self):
        return 'blog:post', (self.slug,)

Note that we've set unique=True for our slug field — in this project we will be looking up posts by their slug, so we need to ensure they are unique. Here's what our application's views.py might look like to do this:

from .models import Post

def post(request, slug):
    post = get_object_or_404(Post, slug=slug)

    return render(request, 'blog/post.html', {
        'post': post,
    })
查看更多
混吃等死
4楼-- · 2019-01-21 23:59

I use this snippet for generating unique slug and my typical save method look like below

slug will be Django SlugField with blank=True but enforce slug in save method.

typical save method for Need model might look below

def save(self, **kwargs):
    slug_str = "%s %s" % (self.title, self.us_zip) 
    unique_slugify(self, slug_str) 
    super(Need, self).save(**kwargs)

and this will generate slug like buy-a-new-bike_Boston-MA-02111 , buy-a-new-bike_Boston-MA-02111-1 and so on. Output might be little different but you can always go through snippet and customize to your needs.

查看更多
Anthone
5楼-- · 2019-01-22 00:00
class Need(models.Model):
    title = models.CharField(max_length=50)
    us_zip = models.CharField(max_length=5)
    slug = models.SlugField(unique=True)

    def save(self, **kwargs):
        slug_str = "%s %s" % (self.title, self.us_zip) 
        super(Need, self).save()
查看更多
劳资没心,怎么记你
6楼-- · 2019-01-22 00:05

This is a simple implementation that generate the slug from the title, it doesn't depend on other snippets:

from django.template.defaultfilters import slugify

class Article(models.Model):
    ...
    def save(self, **kwargs):
        if not self.slug:
            slug = slugify(self.title)
            while True:
                try:
                    article = Article.objects.get(slug=slug)
                    if article == self:
                        self.slug = slug
                        break
                    else:
                        slug = slug + '-'
                except:
                    self.slug = slug
                    break

        super(Article, self).save()
查看更多
时光不老,我们不散
7楼-- · 2019-01-22 00:06

Hi can you tried this function

class Training(models.Model):
    title = models.CharField(max_length=250)
    text = models.TextField()
    created_date = models.DateTimeField(
    auto_now_add=True, editable=False, )
    slug = models.SlugField(unique=True, editable=False, max_length=250)

    def __unicode__(self):
       return self.title

    def save(self, *args, **kwargs):
       self.slug =get_unique_slug(self.id,self.title,Training.objects)
       return super(Training, self).save(*args, **kwargs)

def get_unique_slug(id,title,obj):
    slug = slugify(title.replace('ı', 'i'))
    unique_slug = slug
    counter = 1
    while obj.filter(slug=unique_slug).exists():
       if(obj.filter(slug=unique_slug).values('id')[0]['id']==id):
           break
       unique_slug = '{}-{}'.format(slug, counter)
       counter += 1
    return unique_slug
查看更多
登录 后发表回答