You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
83 lines
2.9 KiB
83 lines
2.9 KiB
# -*- coding: utf-8 -*- |
|
from __future__ import unicode_literals |
|
# Erik Stein <code@classlibrary.net>, 2008-2015 |
|
|
|
import re |
|
from django.db.models import fields |
|
from django.utils import six |
|
from django.utils.translation import ugettext_lazy as _ |
|
if six.PY3: |
|
from functools import reduce |
|
|
|
from .text import slugify_long as slugify |
|
from . import SLUG_HELP |
|
|
|
|
|
DEFAULT_SLUG = _("item") |
|
|
|
|
|
def unique_slug(instance, slug_field, slug_value, max_length=50, queryset=None): |
|
""" |
|
TODO Doesn't work with model inheritance, where the slug field is part of the parent class. |
|
""" |
|
if not slug_value: |
|
raise ValueError("Cannot uniquify empty slug") |
|
orig_slug = slug = slugify(slug_value) |
|
index = 0 |
|
if not queryset: |
|
queryset = instance.__class__._default_manager.get_queryset() |
|
|
|
def get_similar_slugs(slug): |
|
return queryset.exclude(pk=instance.pk) \ |
|
.filter(**{"%s__istartswith" % slug_field: slug}).values_list(slug_field, flat=True) |
|
|
|
similar_slugs = get_similar_slugs(slug) |
|
while slug in similar_slugs or len(slug) > max_length: |
|
index += 1 |
|
slug = "%s-%i" % (orig_slug, index) |
|
if len(slug) > max_length: |
|
orig_slug = orig_slug[:-(len(slug) - max_length)] |
|
slug = "%s-%i" % (orig_slug, index) |
|
similar_slugs = get_similar_slugs(orig_slug) |
|
return slug |
|
|
|
|
|
def unique_slug2(instance, slug_source, slug_field): |
|
slug = slugify(slug_source) |
|
all_slugs = [sl.values()[0] for sl in instance.__class__._default_manager.values(slug_field)] |
|
if slug in all_slugs: |
|
counter_finder = re.compile(r'-\d+$') |
|
counter = 2 |
|
slug = "%s-%i" % (slug, counter) |
|
while slug in all_slugs: |
|
slug = re.sub(counter_finder, "-%i" % counter, slug) |
|
counter += 1 |
|
return slug |
|
|
|
|
|
class AutoSlugField(fields.SlugField): |
|
# AutoSlugField based on http://www.djangosnippets.org/snippets/728/ |
|
|
|
def __init__(self, *args, **kwargs): |
|
kwargs.setdefault('max_length', 50) |
|
kwargs.setdefault('help_text', SLUG_HELP) |
|
if 'populate_from' in kwargs: |
|
self.populate_from = kwargs.pop('populate_from') |
|
self.unique_slug = kwargs.pop('unique_slug', False) |
|
super(AutoSlugField, self).__init__(*args, **kwargs) |
|
|
|
def pre_save(self, model_instance, add): |
|
value = getattr(model_instance, self.attname) |
|
if not value: |
|
if hasattr(self, 'populate_from'): |
|
# Follow dotted path (e.g. "occupation.corporation.name") |
|
value = reduce(lambda obj, attr: getattr(obj, attr), self.populate_from.split("."), model_instance) |
|
if callable(value): |
|
value = value() |
|
if not value: |
|
value = DEFAULT_SLUG |
|
if self.unique_slug: |
|
return unique_slug(model_instance, self.name, value, max_length=self.max_length) |
|
else: |
|
return slugify(value) |
|
|
|
|