0

I'm trying to integrate a custom method on a Django model

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
    title = models.CharField(max_length=200)
    text = MarkdownField()
    slug = models.SlugField(unique=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    published_date = models.DateTimeField(blank=True, null=True)

    def generate_unique_slug(self):
        slug = self._meta.get_field('slug')
        max_length = slug.max_length
        slug = orig = slugify(self.title)[:max_length]

        for x in itertools.count(1):
            if not Post.objects.filter(slug=slug).exists():
                break

            # Truncate the original slug dynamically. Minus 1 for the hyphen.
            slug = "%s-%d" % (orig[:max_length - len(str(x)) - 1], x)

    def publish(self):
        self.published_date = timezone.now()
        self.generate_unique_slug()
        self.save()

When called from "publish", "generate_unique_slug" doesn't work. For example, I'll do this from terminal

>>> p = Post(title="Title Example", text="Text example")
>>> p.publish()
>>> p.title
'Title Example'
>>> p.published_date
datetime.datetime(2016, 6, 18, 14, 45, 12, 710452, tzinfo=<UTC>)
>>> p.slug
u''

I've also tried this

>>> p.generate_unique_slug()
>>> p.slug
u''

What am I doing wrong?

Stefano De Rosso
  • 1,309
  • 1
  • 14
  • 27

3 Answers3

1

You're not updating the slug of your model. Add self.slug = slug at the end of generate_unique_slug:

def generate_unique_slug(self):
    # your code
    self.slug = slug
Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
  • That's it, thanks! Anyway, when I create the Post from the admin panel, the slug isn't created: any thoughts on how I might get that? – Stefano De Rosso Jun 18 '16 at 15:18
  • 1
    The slug is generated only when you run the publish function. Is this where your problem is? If so, you probably want to override the save method. – Martin Hallén Jun 18 '16 at 15:21
  • 1
    Because the method will not be called when creating a new `Post`. You can modify the save method of your model to always call `generate_unique_slug`. See [this](http://stackoverflow.com/questions/4269605/django-override-save-for-model) – Moses Koledoye Jun 18 '16 at 15:24
  • Thank you. I made it by overriding the save method, and putting "super(Post, self).save()" at the end. – Stefano De Rosso Jun 18 '16 at 15:53
0

generate_unique_slug neither change any instance attribute nor return anything.

You should change slug in slug = "%s-%d" % (orig[:max_length - len(str(x)) - 1], x) to self.slug = ....

DeepSpace
  • 78,697
  • 11
  • 109
  • 154
0

This answer does not fix your code, but I think it is a good option as it covers multiple cases.

There is a library called uuslug designed to do exactly this. It might be worth checking out.

A example from the repository:

from django.db import models
from uuslug import uuslug

# Override your object's save method with something like this (models.py)
class CoolSlug(models.Model):
    name = models.CharField(max_length=100)
    slug = models.CharField(max_length=200)

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.slug = uuslug(self.name, instance=self)
        super(CoolSlug, self).save(*args, **kwargs)

name = "john"
c = CoolSlug.objects.create(name=name)
c.save()
print(c.slug) # => "john"

c1 = CoolSlug.objects.create(name=name)
c1.save()
print(c1.slug) # => "john-1"

I also believe you can change slug = models.CharField(max_length=200) to slug = models.SlugField(max_length=200), and it would still work properly.

Edit after comment:

Instead of overriding save, you could do the following:

from uuslug import slugify

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
    title = models.CharField(max_length=200)
    text = MarkdownField()
    slug = models.SlugField(unique=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    published_date = models.DateTimeField(blank=True, null=True)


    def publish(self):
        self.published_date = timezone.now()
        self.slug = slugify(self.title, max_length=self._meta.get_field('slug'))
        self.save()

Does this fix the problem?

Martin Hallén
  • 1,492
  • 1
  • 12
  • 27
  • Thanks for the library, anyway I think you misread the line, as the slug line is "slug = models.SlugField(unique=True, blank=True)" :) – Stefano De Rosso Jun 18 '16 at 15:15