7

I want to create a slug field stored in database.

I searched and I found http://flask.pocoo.org/snippets/5/ but I'm having trouble integrating the code in my app.

This is my modele.py:

from unicodedata import normalize


def slugfy(text, encoding=None,
        permitted_chars='abcdefghijklmnopqrstuvwxyz0123456789-'):
    if isinstance(text, str):
        text = text.decode(encoding or 'ascii')
    clean_text = text.strip().replace(' ', '-').lower()
    while '--' in clean_text:
        clean_text = clean_text.replace('--', '-')
    ascii_text = normalize('NFKD', clean_text).encode('ascii', 'ignore')
    strict_text = map(lambda x: x if x in permitted_chars else '', ascii_text)
    return ''.join(strict_text)


class Chanteur(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    nom = db.Column(db.String(200), index = True, unique = True)
    slug = db.Column(db.String(255))
    chanteurs = db.relationship('Chanson', backref = 'chanteur', lazy = 'dynamic')

    def __repr__(self):
        return '<Chanteur %r>' % (self.nom)

    def __setattr__(self, key, value):
        super(Chanteur, self).__setattr__(key, value)
        if key == 'nom':
            self.slug = slugfy(self.nom)


class Chanson(db.Model):
        id = db.Column(db.Integer, primary_key = True)
        titre = db.Column(db.String(255))
        chanteur_id = db.Column(db.Integer, db.ForeignKey('chanteur.id'))

        def __repr__(self):
            return '<Chanson %r>' % (self.titre)

It is not working: when I add a new objet (chanteur) the slug field is empty

Mel
  • 5,837
  • 10
  • 37
  • 42
anouar
  • 125
  • 1
  • 1
  • 10

6 Answers6

18

To persist the slug in the database, I use the following approach (employing the very helpful python-slugify library):

from slugify import slugify  # among other things

class Song(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(255))
    slug = db.Column(db.String(255))

    def __init__(self, *args, **kwargs):
        if not 'slug' in kwargs:
            kwargs['slug'] = slugify(kwargs.get('title', ''))
        super().__init__(*args, **kwargs)
Berislav Lopac
  • 16,656
  • 6
  • 71
  • 80
7

A small improvement on @berislav-lopac suggestion's, Using python-slugify you can

from slugify import slugify


class SlugModel(Base):
    name = Column(String)
    slug = Column(String)

    @staticmethod
    def slugify(target, value, oldvalue, initiator):
        if value and (not target.slug or value != oldvalue):
            target.slug = slugify(value)

event.listen(SlugModel.name, 'set', SlugModel.slugify, retval=False)

This will update slug's column content on create\update

if you want even a better solution, you can use the @hybrid_property decorator as suggested here

Liran BG
  • 71
  • 1
  • 2
1

Install package called Webhelpers and it makes slugification a piece of cake.

from webhelpers.text import urlify


class Chanteur(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nom = db.Column(db.String(200), index=True, unique=True)
    chanteurs = db.relationship('Chanson', backref='chanteur', lazy='dynamic')

    def __repr__(self):
        return '<Chanteur %r>' % (self.nom)

    @property
    def slug(self):
        return urlify(self.nom)


class Chanson(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    titre = db.Column(db.String(255))
    chanteur_id = db.Column(db.Integer, db.ForeignKey('chanteur.id'))

    def __repr__(self):
        return '<Chanson %r>' % (self.titre)

    @property
    def slug(self):
        return urlify(self.nom)

Refer docs for more info.

ajknzhol
  • 6,322
  • 13
  • 45
  • 72
  • 3
    What if the OP wants to *search* for objects by slug? – Martijn Pieters May 14 '14 at 14:34
  • 1
    thanks but the code that you give me is not stored the slug in database so if i change the title of some chanson it will change the slug too , i think this is bad for SEO – anouar May 14 '14 at 14:36
  • @MartijnPieters `@classmethod def find_by_slug(cls, slug): return SomeThing.query.filter(SomeThing.slug == slug).first()` – ajknzhol May 14 '14 at 14:39
  • 2
    @ajkumar25: **but you are not storing the slug**. – Martijn Pieters May 14 '14 at 14:41
  • @MartijnPieters I think maybe slugs are one way --- if you need search you need another layer of indirection (i.e., pass an ID in your url) –  May 12 '16 at 22:52
0

Salute,

you can do this way if you want the slug field represents le titre de la Chanson.

from unicodedata import normalize


def slug(text, encoding=None,
         permitted_chars='abcdefghijklmnopqrstuvwxyz0123456789-'):
    if isinstance(text, str):
        text = text.decode(encoding or 'ascii')
    clean_text = text.strip().replace(' ', '-').lower()
    while '--' in clean_text:
        clean_text = clean_text.replace('--', '-')
    ascii_text = normalize('NFKD', clean_text).encode('ascii', 'ignore')
    strict_text = map(lambda x: x if x in permitted_chars else '', ascii_text)
    return ''.join(strict_text)



class Chanson(object):

    titre = ''
    slugfield = ''

    def __setattr__(self, key, value):
        super(Chanson, self).__setattr__(key, value)
        if key == 'titre':
            self.slugfield = slug(self.titre)

m = Chanson()
m.titre = 'Non, je ne regrette rien'
print m.titre
print m.slugfield

The slug method was grabbed from here

Edited

def slug(text):
    #slugfy logic here


class Chanson(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    titre = db.Column(db.String(255))
    chanteur_id = db.Column(db.Integer, db.ForeignKey('chanteur.id'))
    slugfield = db.Column(db.String(255))

    def __setattr__(self, key, value):
        super(Chanson, self).__setattr__(key, value)
        if key == 'titre':
            self.slugfield = slug(self.titre)
andmart
  • 554
  • 2
  • 10
  • Which orm lib are you using? – andmart May 14 '14 at 16:50
  • Are you importing sqlalchemy as db? – andmart May 14 '14 at 20:57
  • yes i am importing sqlalchemy and yes i am using Flask-SQLAlchemy – anouar May 15 '14 at 12:29
  • @anouar your code is working. Could you explain what is not working? – andmart May 15 '14 at 15:11
  • i have flask-admin to add objet(chanteur) , check this photo http://postimg.org/image/cdj4xxob7/ , i the first test i aded the slug manually like that first-test and when i enter to localhost:5000/first-test it work . in the second test i write the nom of chanteur so the slug will be insered automaticlly right ? but it not working . – anouar May 15 '14 at 18:40
0

I wrote an article about this topic. My approach is to use the 'before_commit' event for the session. You can read about it here

schumskie
  • 157
  • 1
  • 7
-2

in the slug function above, you need to do the loop

clean_text.replace('--', '-')

AFTER you reduce it to permitted characters. If you don't: This & That will be returned as this--that.

timo.rieber
  • 3,727
  • 3
  • 32
  • 47