在保存产品和公司后立即保存slug

问题描述:

我有一个产品和公司模型,其中包含slu for以获得更好的url详细视图。在产品和公司保存到数据库后,我已经使用pre_save信号来保存slu save。我已经写不保存蛞蝓,所以当我发布的产品形式的代码,我得到关于塞在保存产品和公司后立即保存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) 

我有一组像鼻涕虫的领域有共同属性的基础混入的。您可以使用模型&视图中的混合将常见属性或方法移入共享(mixin)类; 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-*es 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 
这个看起来很复杂
+0

感谢您的解决方案。这对我来说看起来很复杂,因为我只是初学者。我将不得不花更多的时间来理解这一点。再次感谢 – milan

+0

@米兰哈,在这种情况下,我会稍微更新我的答案。 –

+0

这是一个给我工作。再次感谢。我正在学习文档,您提到的链接以及您的解决方案以更深入地了解。谢谢 – milan