保存后立即为产品和公司保存 slug
save slug for product and company as soon as they are saved
我有一个产品和公司模型,其中包含 slug 以便在 url 中获得更好的详细视图。一旦产品和公司被保存到数据库中,我就使用 pre_save 信号来保存 slug。我写的代码没有保存 slug 所以当我 post 产品表格时我收到关于 slug
的错误
这是我的代码
class Product(models.Model):
name = models.CharField(max_length=200, unique=True, blank=False, null=False)
company = models.ForeignKey('Company', related_name='products', blank=True, null=True, on_delete=models.SET_NULL)
website = models.URLField(unique=True)
slug = models.SlugField(unique=True)
class Meta:
verbose_name= 'Product'
verbose_name_plural= 'Products'
def __str__(self):
return self.name
def hits(self):
self.hits += 1
self.save(update_fields=['hits'])
class Company(models.Model):
name = models.CharField(max_length=200, unique=True, blank=False, null=False)
slug = models.SlugField(unique=True)
description = models.CharField(max_length=400)
editor = models.ForeignKey(User, related_name='company')
# product = models.ForeignKey(Product, related_name='company')
def get_absolute_url(self):
return reverse("products:view-company", kwargs={"slug": self.slug})
def create_slug(instance, new_slug=None):
slug = slugify(instance.name)
if new_slug is not None:
slug = new_slug
qs = Company.objects.filter(slug=slug).order_by('-id')
if qs.exists():
new_slug = "%s-%S" %(slug, qs.first().id)
return create_slug(instance, slug=new_slug)
return slug
def pre_save_slug_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
from django.db.models.signals import pre_save
pre_save.connect(pre_save_slug_receiver, sender=Company)
def create_slug(instance, new_slug=None):
slug = slugify(instance.name)
if new_slug is not None:
slug = new_slug
qs = Product.objects.filter(slug=slug).order_by('-id')
if qs.exists():
new_slug = "%s-%S" %(slug, qs.first().id)
return create_slug(instance, slug=new_slug)
return slug
def pre_save_slug_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
from django.db.models.signals import pre_save
pre_save.connect(pre_save_slug_receiver, sender=Product)
我有一组用于通用属性(如 slug 字段)的基本混合。您可以在模型和视图中使用 mixins 将公共属性或方法移动到共享(mixin)class; https://docs.djangoproject.com/en/1.11/topics/class-based-views/mixins/
查看以下代码,它会向使用它的模型添加一个 slug 字段,并在保存时设置 slug。本质上,您可以将整个块复制并粘贴到 python 文件中,然后将其导入您的模型中,例如 class MyModel(SlugMixin, models.Model):
然后您的模型默认有一个 slug 字段。
# -*- coding: utf-8 -*-
"""
.. module:: base.models.slug
:synopsis: SlugMixin abstract model-mixin
.. moduleauthor:: Mark Walker
"""
# pylint: disable=model-missing-unicode
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from base.utils.slugify import slugify
class SlugMixin(models.Model):
"""
Mixin for models that require a slug field. Defaults to using the
'name' attribute of the model. Can be overridden in the Meta class by
using 'slug_field' to indicate to the pre_save signal which field to use.
Example (using 'title' as an alternate slug field);
class Thing(SlugMixin, models.Model):
title = models.CharField()
class Meta:
slug_field = 'title'
"""
slug = models.SlugField(
verbose_name=_('Slug'),
unique=True,
db_index=True,
blank=True,
default=None,
help_text=_('This field will be auto-populated if left empty.'),
max_length=255,
null=True,
)
class Meta:
"""Metadata for the SlugMixin class"""
abstract = True
@receiver(pre_save)
def slug_mixin_pre_save(sender, instance, **kwargs):
"""
Automatically populate the slug field of SlugMixin models if the
name field is populated.
:param sender: Sender class
:type instance: SlugMixin
:param instance: Model instance
:type instance: SlugMixin
:param kwargs: Not used
:return: None
"""
if issubclass(sender, SlugMixin):
# check for presence of 'slug_field' in object's meta.
try:
# use any named slug_field in the model metadata
field_name = getattr(instance, '_meta').slug_field
except AttributeError:
# default to using the 'name' field
field_name = 'name'
# Note: we don't handle any AttributeErrors here, because we
# WANT to inform the developer that the field he's specified
# does not exist against his model (or the default).
field = getattr(instance, field_name)
if instance.slug is None \
or instance.slug == "" \
and field is not None \
and unicode(field) != u"":
instance.slug = slugify(
unicode(field),
instance=instance,
slug_field='slug',
)
上面依赖于下面的方法,但是django在django.utils.text
中有自己的slugify方法,但是如果你是初学者,这看起来很复杂所以我会专注于SlugMixin
import re
import unicodedata
def slugify(s, instance=None, slug_field='slug', filter_dict=None, unique=True):
"""
Slugify the string 's' stored on the specified field on instance such
that it is unique among all instances
:param s: the string the slugify
:type s: str or unicode
:param instance: optional instance that the string to slugify is stored on
(used to exclude self when searching for duplicates)
:type instance: django.db.models.Model or NoneType
:param slug_field: name of the field on the model-class that any specified
instance belongs to where the slug is stored. Defaults
to ``'slug'``.
:type slug_field: str
:param filter_dict: optional set of kwargs used to filter instances when
checking the uniqueness of any generated slug.
:type filter_dict: dict or NoneType
:param unique: if set to ``True`` (the default), we'll generate unique
slugs, checking for slug-clashes with other instances of
the same model-class as instance. If ``False``, we'll
simply slugify and return without checking for uniqueness.
:type unique: bool
:return: unicode -- the slugified version of the string ``'s'``.
"""
# slugify the input string 's'
s = unicodedata.normalize(
'NFKD', s).encode('ascii', 'ignore').decode('ascii')
s = re.sub(r'[^\w\s-]', '', s).strip().lower()
s = re.sub(r'[-\s]+', '-', s)
slug = s
if instance and unique:
# we have an instance and a request to make a unique slug...check
# for conflicting slugs among instances of the same model-class,
# keep counting until we find an unused slug.
def get_queryset():
"""
Return a QuerySet that checks for conflicts with the current
version of the slug string under consideration
"""
manager = getattr(instance.__class__, '_default_manager')
if hasattr(manager, 'get_admin_query_set'):
queryset = manager.get_admin_query_set().filter(
**{slug_field: slug})
else:
queryset = manager.filter(**{slug_field: slug})
if filter_dict:
queryset = queryset.filter(**filter_dict)
if hasattr(instance, 'id') and instance.id:
queryset = queryset.exclude(pk=instance.id)
return queryset
counter = 1
while get_queryset():
counter += 1
slug = "%s-%s" % (s, counter)
# done!
return slug
我有一个产品和公司模型,其中包含 slug 以便在 url 中获得更好的详细视图。一旦产品和公司被保存到数据库中,我就使用 pre_save 信号来保存 slug。我写的代码没有保存 slug 所以当我 post 产品表格时我收到关于 slug
的错误这是我的代码
class Product(models.Model):
name = models.CharField(max_length=200, unique=True, blank=False, null=False)
company = models.ForeignKey('Company', related_name='products', blank=True, null=True, on_delete=models.SET_NULL)
website = models.URLField(unique=True)
slug = models.SlugField(unique=True)
class Meta:
verbose_name= 'Product'
verbose_name_plural= 'Products'
def __str__(self):
return self.name
def hits(self):
self.hits += 1
self.save(update_fields=['hits'])
class Company(models.Model):
name = models.CharField(max_length=200, unique=True, blank=False, null=False)
slug = models.SlugField(unique=True)
description = models.CharField(max_length=400)
editor = models.ForeignKey(User, related_name='company')
# product = models.ForeignKey(Product, related_name='company')
def get_absolute_url(self):
return reverse("products:view-company", kwargs={"slug": self.slug})
def create_slug(instance, new_slug=None):
slug = slugify(instance.name)
if new_slug is not None:
slug = new_slug
qs = Company.objects.filter(slug=slug).order_by('-id')
if qs.exists():
new_slug = "%s-%S" %(slug, qs.first().id)
return create_slug(instance, slug=new_slug)
return slug
def pre_save_slug_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
from django.db.models.signals import pre_save
pre_save.connect(pre_save_slug_receiver, sender=Company)
def create_slug(instance, new_slug=None):
slug = slugify(instance.name)
if new_slug is not None:
slug = new_slug
qs = Product.objects.filter(slug=slug).order_by('-id')
if qs.exists():
new_slug = "%s-%S" %(slug, qs.first().id)
return create_slug(instance, slug=new_slug)
return slug
def pre_save_slug_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
from django.db.models.signals import pre_save
pre_save.connect(pre_save_slug_receiver, sender=Product)
我有一组用于通用属性(如 slug 字段)的基本混合。您可以在模型和视图中使用 mixins 将公共属性或方法移动到共享(mixin)class; https://docs.djangoproject.com/en/1.11/topics/class-based-views/mixins/
查看以下代码,它会向使用它的模型添加一个 slug 字段,并在保存时设置 slug。本质上,您可以将整个块复制并粘贴到 python 文件中,然后将其导入您的模型中,例如 class MyModel(SlugMixin, models.Model):
然后您的模型默认有一个 slug 字段。
# -*- coding: utf-8 -*-
"""
.. module:: base.models.slug
:synopsis: SlugMixin abstract model-mixin
.. moduleauthor:: Mark Walker
"""
# pylint: disable=model-missing-unicode
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from base.utils.slugify import slugify
class SlugMixin(models.Model):
"""
Mixin for models that require a slug field. Defaults to using the
'name' attribute of the model. Can be overridden in the Meta class by
using 'slug_field' to indicate to the pre_save signal which field to use.
Example (using 'title' as an alternate slug field);
class Thing(SlugMixin, models.Model):
title = models.CharField()
class Meta:
slug_field = 'title'
"""
slug = models.SlugField(
verbose_name=_('Slug'),
unique=True,
db_index=True,
blank=True,
default=None,
help_text=_('This field will be auto-populated if left empty.'),
max_length=255,
null=True,
)
class Meta:
"""Metadata for the SlugMixin class"""
abstract = True
@receiver(pre_save)
def slug_mixin_pre_save(sender, instance, **kwargs):
"""
Automatically populate the slug field of SlugMixin models if the
name field is populated.
:param sender: Sender class
:type instance: SlugMixin
:param instance: Model instance
:type instance: SlugMixin
:param kwargs: Not used
:return: None
"""
if issubclass(sender, SlugMixin):
# check for presence of 'slug_field' in object's meta.
try:
# use any named slug_field in the model metadata
field_name = getattr(instance, '_meta').slug_field
except AttributeError:
# default to using the 'name' field
field_name = 'name'
# Note: we don't handle any AttributeErrors here, because we
# WANT to inform the developer that the field he's specified
# does not exist against his model (or the default).
field = getattr(instance, field_name)
if instance.slug is None \
or instance.slug == "" \
and field is not None \
and unicode(field) != u"":
instance.slug = slugify(
unicode(field),
instance=instance,
slug_field='slug',
)
上面依赖于下面的方法,但是django在django.utils.text
中有自己的slugify方法,但是如果你是初学者,这看起来很复杂所以我会专注于SlugMixin
import re
import unicodedata
def slugify(s, instance=None, slug_field='slug', filter_dict=None, unique=True):
"""
Slugify the string 's' stored on the specified field on instance such
that it is unique among all instances
:param s: the string the slugify
:type s: str or unicode
:param instance: optional instance that the string to slugify is stored on
(used to exclude self when searching for duplicates)
:type instance: django.db.models.Model or NoneType
:param slug_field: name of the field on the model-class that any specified
instance belongs to where the slug is stored. Defaults
to ``'slug'``.
:type slug_field: str
:param filter_dict: optional set of kwargs used to filter instances when
checking the uniqueness of any generated slug.
:type filter_dict: dict or NoneType
:param unique: if set to ``True`` (the default), we'll generate unique
slugs, checking for slug-clashes with other instances of
the same model-class as instance. If ``False``, we'll
simply slugify and return without checking for uniqueness.
:type unique: bool
:return: unicode -- the slugified version of the string ``'s'``.
"""
# slugify the input string 's'
s = unicodedata.normalize(
'NFKD', s).encode('ascii', 'ignore').decode('ascii')
s = re.sub(r'[^\w\s-]', '', s).strip().lower()
s = re.sub(r'[-\s]+', '-', s)
slug = s
if instance and unique:
# we have an instance and a request to make a unique slug...check
# for conflicting slugs among instances of the same model-class,
# keep counting until we find an unused slug.
def get_queryset():
"""
Return a QuerySet that checks for conflicts with the current
version of the slug string under consideration
"""
manager = getattr(instance.__class__, '_default_manager')
if hasattr(manager, 'get_admin_query_set'):
queryset = manager.get_admin_query_set().filter(
**{slug_field: slug})
else:
queryset = manager.filter(**{slug_field: slug})
if filter_dict:
queryset = queryset.filter(**filter_dict)
if hasattr(instance, 'id') and instance.id:
queryset = queryset.exclude(pk=instance.id)
return queryset
counter = 1
while get_queryset():
counter += 1
slug = "%s-%s" % (s, counter)
# done!
return slug