2017-09-16 64 views
1

我有一個產品和公司模型,其中包含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) 

回答

1

我有一組像鼻涕蟲的領域有共同屬性的基礎混入的。您可以使用模型&視圖中的混合將常見屬性或方法移入共享(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-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 
這個看起來很複雜
+0

感謝您的解決方案。這對我來說看起來很複雜,因爲我只是初學者。我將不得不花更多的時間來理解這一點。再次感謝 – milan

+0

@米蘭哈,在這種情況下,我會稍微更新我的答案。 –

+0

這是一個給我工作。再次感謝。我正在學習文檔,您提到的鏈接以及您的解決方案以更深入地瞭解。謝謝 – milan