2017-12-29 5 views
1

template.html :장고 템플릿의주기에서 태그 끝에 공백을 추가하는 이유는 무엇입니까?

{% for x in post.tags.all %} 
    <a href="{% url 'blog:post_list_by_tag' x.slug %}"> 
    {{ x.name }} 
    </a> 
    {% if not forloop.last %}, {% endif %} 
{% endfor %} 

나는 라인의 끝없는 공간이 나는 이름의 공간을하지 않은 (데이터베이스)하지만 출력은 다음과 같습니다 A를

tag1 , tag2 , tag3 

이름과 쉼표 사이의 공백과 끝의 공백 하나의 태그가 있더라도 끝에는 공간이 있습니다. 나는 taggit을 사용하는데 문제가있을 수 있습니다.

또한 쉼표가있는 뒤에 링크는 공백에도 밑줄을 긋습니다. {{ x.name }}</a>으로 작성하면 공백이 있지만 링크는 공백이 아닌 태그에만 밑줄을 긋습니다.

class Post(models.Model): 
    author = models.ForeignKey('auth.User', blank=True, 
     on_delete=models.PROTECT, verbose_name=_('autore')) 
    title = models.CharField(_('titolo'), max_length=32) 
    text = models.TextField(_('testo')) 
    created_date = models.DateTimeField(_('creato il'), 
      default=timezone.now) 
    published_date = models.DateTimeField(_('pubblicato il'), 
      blank=True, null=True) 
    likes = models.ManyToManyField(settings.AUTH_USER_MODEL, 
     related_name='posts_liked', blank=True, verbose_name=_('piace a')) 
    dislikes = models.ManyToManyField(settings.AUTH_USER_MODEL, 
     related_name='posts_disliked', blank=True, verbose_name=_('non piace a')) 
    tags = TaggableManager() 
    views = models.IntegerField(_('visite'), default=0) 
    block_comment = models.BooleanField(default=False) 

    def publish(self): 
     self.published_date = timezone.now() 
     self.save() 

    def __str__(self): 
     return self.title 

    class Meta: 
     verbose_name = pgettext('singolare', 'post') 
     verbose_name_plural = pgettext('plurale', 'post') 

... 

taggit.managers.py :

0,123,516 MYVIEW

print(post.tags) =>AttributeError: '_TaggableManager' object has no attribute 'name'

print(post.tags.all) =>

<bound method BaseManager.all of <taggit.managers._TaggableManager object at 0x03EFEA70>> 

Mymodel.py에서

... 
class _TaggableManager(models.Manager): 
    def __init__(self, through, model, instance, prefetch_cache_name): 
     self.through = through 
     self.model = model 
     self.instance = instance 
     self.prefetch_cache_name = prefetch_cache_name 
     self._db = None 

    def is_cached(self, instance): 
     return self.prefetch_cache_name in instance._prefetched_objects_cache 

    def get_queryset(self, extra_filters=None): 
     try: 
      return self.instance._prefetched_objects_cache[self.prefetch_cache_name] 
     except (AttributeError, KeyError): 
      kwargs = extra_filters if extra_filters else {} 
      return self.through.tags_for(self.model, self.instance, **kwargs) 

    def get_prefetch_queryset(self, instances, queryset=None): 
     if queryset is not None: 
      raise ValueError("Custom queryset can't be used for this lookup.") 

     instance = instances[0] 
     from django.db import connections 
     db = self._db or router.db_for_read(instance.__class__, instance=instance) 

     fieldname = ('object_id' if issubclass(self.through, CommonGenericTaggedItemBase) 
        else 'content_object') 
     fk = self.through._meta.get_field(fieldname) 
     query = { 
      '%s__%s__in' % (self.through.tag_relname(), fk.name): 
       set(obj._get_pk_val() for obj in instances) 
     } 
     join_table = self.through._meta.db_table 
     source_col = fk.column 
     connection = connections[db] 
     qn = connection.ops.quote_name 
     qs = self.get_queryset(query).using(db).extra(
      select={ 
       '_prefetch_related_val': '%s.%s' % (qn(join_table), qn(source_col)) 
      } 
     ) 
     return (qs, 
       attrgetter('_prefetch_related_val'), 
       lambda obj: obj._get_pk_val(), 
       False, 
       self.prefetch_cache_name) 

    # Django < 1.6 uses the previous name of query_set 
    get_query_set = get_queryset 
    get_prefetch_query_set = get_prefetch_queryset 

    def _lookup_kwargs(self): 
     return self.through.lookup_kwargs(self.instance) 

    @require_instance_manager 
    def add(self, *tags): 
     db = router.db_for_write(self.through, instance=self.instance) 

     tag_objs = self._to_tag_model_instances(tags) 
     new_ids = set(t.pk for t in tag_objs) 

     # NOTE: can we hardcode 'tag_id' here or should the column name be got 
     # dynamically from somewhere? 
     vals = (self.through._default_manager.using(db) 
       .values_list('tag_id', flat=True) 
       .filter(**self._lookup_kwargs())) 

     new_ids = new_ids - set(vals) 

     signals.m2m_changed.send(
      sender=self.through, action="pre_add", 
      instance=self.instance, reverse=False, 
      model=self.through.tag_model(), pk_set=new_ids, using=db, 
     ) 

     for tag in tag_objs: 
      self.through._default_manager.using(db).get_or_create(
       tag=tag, **self._lookup_kwargs()) 

     signals.m2m_changed.send(
      sender=self.through, action="post_add", 
      instance=self.instance, reverse=False, 
      model=self.through.tag_model(), pk_set=new_ids, using=db, 
     ) 

    def _to_tag_model_instances(self, tags): 
     """ 
     Takes an iterable containing either strings, tag objects, or a mixture 
     of both and returns set of tag objects. 
     """ 
     db = router.db_for_write(self.through, instance=self.instance) 

     str_tags = set() 
     tag_objs = set() 

     for t in tags: 
      if isinstance(t, self.through.tag_model()): 
       tag_objs.add(t) 
      elif isinstance(t, six.string_types): 
       str_tags.add(t) 
      else: 
       raise ValueError(
        "Cannot add {0} ({1}). Expected {2} or str.".format(
         t, type(t), type(self.through.tag_model()))) 

     if getattr(settings, 'TAGGIT_CASE_INSENSITIVE', False): 
      # Some databases can do case-insensitive comparison with IN, which 
      # would be faster, but we can't rely on it or easily detect it. 
      existing = [] 
      tags_to_create = [] 

      for name in str_tags: 
       try: 
        tag = (self.through.tag_model()._default_manager 
          .using(db) 
          .get(name__iexact=name)) 
        existing.append(tag) 
       except self.through.tag_model().DoesNotExist: 
        tags_to_create.append(name) 
     else: 
      # If str_tags has 0 elements Django actually optimizes that to not 
      # do a query. Malcolm is very smart. 
      existing = (self.through.tag_model()._default_manager 
         .using(db) 
         .filter(name__in=str_tags)) 

      tags_to_create = str_tags - set(t.name for t in existing) 

     tag_objs.update(existing) 

     for new_tag in tags_to_create: 
      tag_objs.add(
       self.through.tag_model()._default_manager 
       .using(db) 
       .create(name=new_tag)) 

     return tag_objs 

    @require_instance_manager 
    def names(self): 
     return self.get_queryset().values_list('name', flat=True) 

    @require_instance_manager 
    def slugs(self): 
     return self.get_queryset().values_list('slug', flat=True) 

    @require_instance_manager 
    def set(self, *tags, **kwargs): 
     """ 
     Set the object's tags to the given n tags. If the clear kwarg is True 
     then all existing tags are removed (using `.clear()`) and the new tags 
     added. Otherwise, only those tags that are not present in the args are 
     removed and any new tags added. 
     """ 
     db = router.db_for_write(self.through, instance=self.instance) 
     clear = kwargs.pop('clear', False) 

     if clear: 
      self.clear() 
      self.add(*tags) 
     else: 
      # make sure we're working with a collection of a uniform type 
      objs = self._to_tag_model_instances(tags) 

      # get the existing tag strings 
      old_tag_strs = set(self.through._default_manager 
           .using(db) 
           .filter(**self._lookup_kwargs()) 
           .values_list('tag__name', flat=True)) 

      new_objs = [] 
      for obj in objs: 
       if obj.name in old_tag_strs: 
        old_tag_strs.remove(obj.name) 
       else: 
        new_objs.append(obj) 

      self.remove(*old_tag_strs) 
      self.add(*new_objs) 

    @require_instance_manager 
    def remove(self, *tags): 
     if not tags: 
      return 

     db = router.db_for_write(self.through, instance=self.instance) 

     qs = (self.through._default_manager.using(db) 
       .filter(**self._lookup_kwargs()) 
       .filter(tag__name__in=tags)) 

     old_ids = set(qs.values_list('tag_id', flat=True)) 

     signals.m2m_changed.send(
      sender=self.through, action="pre_remove", 
      instance=self.instance, reverse=False, 
      model=self.through.tag_model(), pk_set=old_ids, using=db, 
     ) 
     qs.delete() 
     signals.m2m_changed.send(
      sender=self.through, action="post_remove", 
      instance=self.instance, reverse=False, 
      model=self.through.tag_model(), pk_set=old_ids, using=db, 
     ) 

    @require_instance_manager 
    def clear(self): 
     db = router.db_for_write(self.through, instance=self.instance) 

     signals.m2m_changed.send(
      sender=self.through, action="pre_clear", 
      instance=self.instance, reverse=False, 
      model=self.through.tag_model(), pk_set=None, using=db, 
     ) 

     self.through._default_manager.using(db).filter(
      **self._lookup_kwargs()).delete() 

     signals.m2m_changed.send(
      sender=self.through, action="post_clear", 
      instance=self.instance, reverse=False, 
      model=self.through.tag_model(), pk_set=None, using=db, 
     ) 

    def most_common(self, min_count=None, extra_filters=None): 
     queryset = self.get_queryset(extra_filters).annotate(
      num_times=models.Count(self.through.tag_relname()) 
     ).order_by('-num_times') 
     if min_count: 
      queryset = queryset.filter(num_times__gte=min_count) 

     return queryset 

    @require_instance_manager 
    def similar_objects(self): 
     lookup_kwargs = self._lookup_kwargs() 
     lookup_keys = sorted(lookup_kwargs) 
     qs = self.through.objects.values(*six.iterkeys(lookup_kwargs)) 
     qs = qs.annotate(n=models.Count('pk')) 
     qs = qs.exclude(**lookup_kwargs) 
     qs = qs.filter(tag__in=self.all()) 
     qs = qs.order_by('-n') 

     # TODO: This all feels like a bit of a hack. 
     items = {} 
     if len(lookup_keys) == 1: 
      # Can we do this without a second query by using a select_related() 
      # somehow? 
      f = _get_field(self.through, lookup_keys[0]) 
      remote_field = _remote_field(f) 
      rel_model = _related_model(_remote_field(f)) 
      objs = rel_model._default_manager.filter(**{ 
       "%s__in" % remote_field.field_name: [r["content_object"] for r in qs] 
      }) 
      for obj in objs: 
       items[(getattr(obj, remote_field.field_name),)] = obj 
     else: 
      preload = {} 
      for result in qs: 
       preload.setdefault(result['content_type'], set()) 
       preload[result["content_type"]].add(result["object_id"]) 

      for ct, obj_ids in preload.items(): 
       ct = ContentType.objects.get_for_id(ct) 
       for obj in ct.model_class()._default_manager.filter(pk__in=obj_ids): 
        items[(ct.pk, obj.pk)] = obj 

     results = [] 
     for result in qs: 
      obj = items[ 
       tuple(result[k] for k in lookup_keys) 
      ] 
      obj.similar_tags = result["n"] 
      results.append(obj) 
     return results 

    # _TaggableManager needs to be hashable but BaseManagers in Django 1.8+ overrides 
    # the __eq__ method which makes the default __hash__ method disappear. 
    # This checks if the __hash__ attribute is None, and if so, it reinstates the original method. 
    if models.Manager.__hash__ is None: 
     __hash__ = object.__hash__ 


class TaggableManager(RelatedField, Field): 
    # Field flags 
    many_to_many = True 
    many_to_one = False 
    one_to_many = False 
    one_to_one = False 

    _related_name_counter = 0 

    def __init__(self, verbose_name=_("Tags"), 
       help_text=_("A comma-separated list of tags."), 
       through=None, blank=False, related_name=None, to=None, 
       manager=_TaggableManager): 

     self.through = through or TaggedItem 
     self.swappable = False 
     self.manager = manager 

     rel = TaggableRel(self, related_name, self.through, to=to) 

     Field.__init__(
      self, 
      verbose_name=verbose_name, 
      help_text=help_text, 
      blank=blank, 
      null=True, 
      serialize=False, 
      rel=rel, 
     ) 
     # NOTE: `to` is ignored, only used via `deconstruct`. 

    def __get__(self, instance, model): 
     if instance is not None and instance.pk is None: 
      raise ValueError("%s objects need to have a primary key value " 
          "before you can access their tags." % model.__name__) 
     manager = self.manager(
      through=self.through, 
      model=model, 
      instance=instance, 
      prefetch_cache_name=self.name 
     ) 
     return manager 

    def deconstruct(self): 
     """ 
     Deconstruct the object, used with migrations. 
     """ 
     name, path, args, kwargs = super(TaggableManager, self).deconstruct() 
     # Remove forced kwargs. 
     for kwarg in ('serialize', 'null'): 
      del kwargs[kwarg] 
     # Add arguments related to relations. 
     # Ref: https://github.com/alex/django-taggit/issues/206#issuecomment-37578676 
     rel = _remote_field(self) 
     if isinstance(rel.through, six.string_types): 
      kwargs['through'] = rel.through 
     elif not rel.through._meta.auto_created: 
      kwargs['through'] = "%s.%s" % (rel.through._meta.app_label, rel.through._meta.object_name) 

     related_model = _related_model(rel) 
     if isinstance(related_model, six.string_types): 
      kwargs['to'] = related_model 
     else: 
      kwargs['to'] = '%s.%s' % (related_model._meta.app_label, related_model._meta.object_name) 

     return name, path, args, kwargs 

    def contribute_to_class(self, cls, name): 
     if VERSION < (1, 7): 
      self.name = self.column = self.attname = name 
     else: 
      self.set_attributes_from_name(name) 
     self.model = cls 
     self.opts = cls._meta 

     cls._meta.add_field(self) 
     setattr(cls, name, self) 
     if not cls._meta.abstract: 
      # rel.to renamed to remote_field.model in Django 1.9 
      if VERSION >= (1, 9): 
       if isinstance(self.remote_field.model, six.string_types): 
        def resolve_related_class(cls, model, field): 
         field.remote_field.model = model 
        lazy_related_operation(
         resolve_related_class, cls, self.remote_field.model, field=self 
        ) 
      else: 
       if isinstance(self.rel.to, six.string_types): 
        def resolve_related_class(field, model, cls): 
         field.rel.to = model 
        add_lazy_relation(cls, self, self.rel.to, resolve_related_class) 

      if isinstance(self.through, six.string_types): 
       if VERSION >= (1, 9): 
        def resolve_related_class(cls, model, field): 
         self.through = model 
         self.remote_field.through = model 
         self.post_through_setup(cls) 
        lazy_related_operation(
         resolve_related_class, cls, self.through, field=self 
        ) 
       else: 
        def resolve_related_class(field, model, cls): 
         self.through = model 
         _remote_field(self).through = model 
         self.post_through_setup(cls) 
        add_lazy_relation(
         cls, self, self.through, resolve_related_class 
        ) 
      else: 
       self.post_through_setup(cls) 

    def get_internal_type(self): 
     return 'ManyToManyField' 

    def __lt__(self, other): 
     """ 
     Required contribute_to_class as Django uses bisect 
     for ordered class contribution and bisect requires 
     a orderable type in py3. 
     """ 
     return False 

    def post_through_setup(self, cls): 
     if RelatedObject is not None: # Django < 1.8 
      self.related = RelatedObject(cls, self.model, self) 

     self.use_gfk = (
      self.through is None or issubclass(self.through, CommonGenericTaggedItemBase) 
     ) 

     # rel.to renamed to remote_field.model in Django 1.9 
     if VERSION >= (1, 9): 
      if not self.remote_field.model: 
       self.remote_field.model = self.through._meta.get_field("tag").remote_field.model 
     else: 
      if not self.rel.to: 
       self.rel.to = self.through._meta.get_field("tag").rel.to 

     if RelatedObject is not None: # Django < 1.8 
      self.related = RelatedObject(self.through, cls, self) 

     if self.use_gfk: 
      tagged_items = GenericRelation(self.through) 
      tagged_items.contribute_to_class(cls, 'tagged_items') 

     for rel in cls._meta.local_many_to_many: 
      if rel == self or not isinstance(rel, TaggableManager): 
       continue 
      if rel.through == self.through: 
       raise ValueError('You can\'t have two TaggableManagers with the' 
           ' same through model.') 

    def save_form_data(self, instance, value): 
     getattr(instance, self.name).set(*value) 

    def formfield(self, form_class=TagField, **kwargs): 
     defaults = { 
      "label": capfirst(self.verbose_name), 
      "help_text": self.help_text, 
      "required": not self.blank 
     } 
     defaults.update(kwargs) 
     return form_class(**defaults) 

    def value_from_object(self, instance): 
     if instance.pk: 
      return self.through.objects.filter(**self.through.lookup_kwargs(instance)) 
     return self.through.objects.none() 

    def related_query_name(self): 
     return _model_name(self.model) 

    def m2m_reverse_name(self): 
     return _get_field(self.through, 'tag').column 

    def m2m_reverse_field_name(self): 
     return _get_field(self.through, 'tag').name 

    def m2m_target_field_name(self): 
     return self.model._meta.pk.name 

    def m2m_reverse_target_field_name(self): 
     # rel.to renamed to remote_field.model in Django 1.9 
     if VERSION >= (1, 9): 
      return self.remote_field.model._meta.pk.name 
     else: 
      return self.rel.to._meta.pk.name 

    def m2m_column_name(self): 
     if self.use_gfk: 
      return self.through._meta.virtual_fields[0].fk_field 
     return self.through._meta.get_field('content_object').column 

    def db_type(self, connection=None): 
     return None 

    def m2m_db_table(self): 
     return self.through._meta.db_table 

    def bulk_related_objects(self, new_objs, using): 
     return [] 

    def extra_filters(self, pieces, pos, negate): 
     if negate or not self.use_gfk: 
      return [] 
     prefix = "__".join(["tagged_items"] + pieces[:pos - 2]) 
     get = ContentType.objects.get_for_model 
     cts = [get(obj) for obj in _get_subclasses(self.model)] 
     if len(cts) == 1: 
      return [("%s__content_type" % prefix, cts[0])] 
     return [("%s__content_type__in" % prefix, cts)] 

    def get_extra_join_sql(self, connection, qn, lhs_alias, rhs_alias): 
     model_name = _model_name(self.through) 
     if rhs_alias == '%s_%s' % (self.through._meta.app_label, model_name): 
      alias_to_join = rhs_alias 
     else: 
      alias_to_join = lhs_alias 
     extra_col = _get_field(self.through, 'content_type').column 
     content_type_ids = [ContentType.objects.get_for_model(subclass).pk for 
          subclass in _get_subclasses(self.model)] 
     if len(content_type_ids) == 1: 
      content_type_id = content_type_ids[0] 
      extra_where = " AND %s.%s = %%s" % (qn(alias_to_join), 
               qn(extra_col)) 
      params = [content_type_id] 
     else: 
      extra_where = " AND %s.%s IN (%s)" % (qn(alias_to_join), 
                qn(extra_col), 
                ','.join(['%s'] * 
                  len(content_type_ids))) 
      params = content_type_ids 
     return extra_where, params 

    # This and all the methods till the end of class are only used in django >= 1.6 
    def _get_mm_case_path_info(self, direct=False): 
     pathinfos = [] 
     linkfield1 = _get_field(self.through, 'content_object') 
     linkfield2 = _get_field(self.through, self.m2m_reverse_field_name()) 
     if direct: 
      join1infos = linkfield1.get_reverse_path_info() 
      join2infos = linkfield2.get_path_info() 
     else: 
      join1infos = linkfield2.get_reverse_path_info() 
      join2infos = linkfield1.get_path_info() 
     pathinfos.extend(join1infos) 
     pathinfos.extend(join2infos) 
     return pathinfos 

    def _get_gfk_case_path_info(self, direct=False): 
     pathinfos = [] 
     from_field = self.model._meta.pk 
     opts = self.through._meta 
     linkfield = _get_field(self.through, self.m2m_reverse_field_name()) 
     if direct: 
      join1infos = [PathInfo(self.model._meta, opts, [from_field], _remote_field(self), True, False)] 
      join2infos = linkfield.get_path_info() 
     else: 
      join1infos = linkfield.get_reverse_path_info() 
      join2infos = [PathInfo(opts, self.model._meta, [from_field], self, True, False)] 
     pathinfos.extend(join1infos) 
     pathinfos.extend(join2infos) 
     return pathinfos 

    def get_path_info(self): 
     if self.use_gfk: 
      return self._get_gfk_case_path_info(direct=True) 
     else: 
      return self._get_mm_case_path_info(direct=True) 

    def get_reverse_path_info(self): 
     if self.use_gfk: 
      return self._get_gfk_case_path_info(direct=False) 
     else: 
      return self._get_mm_case_path_info(direct=False) 

    def get_joining_columns(self, reverse_join=False): 
     if reverse_join: 
      return ((self.model._meta.pk.column, "object_id"),) 
     else: 
      return (("object_id", self.model._meta.pk.column),) 

    def get_extra_restriction(self, where_class, alias, related_alias): 
     extra_col = _get_field(self.through, 'content_type').column 
     content_type_ids = [ContentType.objects.get_for_model(subclass).pk 
          for subclass in _get_subclasses(self.model)] 
     return ExtraJoinRestriction(related_alias, extra_col, content_type_ids) 

    def get_reverse_joining_columns(self): 
     return self.get_joining_columns(reverse_join=True) 

    @property 
    def related_fields(self): 
     return [(_get_field(self.through, 'object_id'), self.model._meta.pk)] 

    @property 
    def foreign_related_fields(self): 
     return [self.related_fields[0][1]] 

    ... 

taggit.models.py :

어쩌면
... 
@python_2_unicode_compatible 
class TagBase(models.Model): 
    name = models.CharField(verbose_name=_('Name'), unique=True, max_length=100) 
    slug = models.SlugField(verbose_name=_('Slug'), unique=True, max_length=100) 

    def __str__(self): 
     return self.name 

    class Meta: 
     abstract = True 

    def save(self, *args, **kwargs): 
     if not self.pk and not self.slug: 
      self.slug = self.slugify(self.name) 
      from django.db import router 
      using = kwargs.get("using") or router.db_for_write(
       type(self), instance=self) 
      # Make sure we write to the same db for all attempted writes, 
      # with a multi-master setup, theoretically we could try to 
      # write and rollback on different DBs 
      kwargs["using"] = using 
      # Be oportunistic and try to save the tag, this should work for 
      # most cases ;) 
      try: 
       with atomic(using=using): 
        res = super(TagBase, self).save(*args, **kwargs) 
       return res 
      except IntegrityError: 
       pass 
      # Now try to find existing slugs with similar names 
      slugs = set(
       self.__class__._default_manager 
       .filter(slug__startswith=self.slug) 
       .values_list('slug', flat=True) 
      ) 
      i = 1 
      while True: 
       slug = self.slugify(self.name, i) 
       if slug not in slugs: 
        self.slug = slug 
        # We purposely ignore concurrecny issues here for now. 
        # (That is, till we found a nice solution...) 
        return super(TagBase, self).save(*args, **kwargs) 
       i += 1 
     else: 
      return super(TagBase, self).save(*args, **kwargs) 

    def slugify(self, tag, i=None): 
     slug = default_slugify(unidecode(tag)) 
     if i is not None: 
      slug += "_%d" % i 
     return slug 


class Tag(TagBase): 
    class Meta: 
     verbose_name = _("Tag") 
     verbose_name_plural = _("Tags") 
     app_label = 'taggit' 
... 
+0

왜 당신은이 –

답변

2

; 줄 바꿈을 포함하여 모든 공백을 공백으로 변환합니다. 공백을 원하지 않으면 모든 것을 같은 줄에 놓아야합니다.

<a href="{% url 'blog:post_list_by_tag' x.slug %}">{{ x.name }}</a>{% if not forloop.last %}, {% endif %} 
0

당신이 시도 할 수 :

HTML 작동 단지 방법
{{ x.name.strip }} 
+0

은 감사하지만 아무것도 – fabio

+0

당신이 당신의 모델을 게시 할 수 변경되지 장고 또는 HTML 문제가있는 경우 알이 순수한 HTML 렌더링하지? – Walucas

관련 문제