0

I am currently trying to implement a tag system into my Django project. I am trying to add the tags within each post, and have a category on the right hand side that displays maybe 10-20 of the tags. I am trying to implement this into the feed view, but i am unsure of how to call the slug for each tag in order to do /posts/tag/feed. So once you click on a tag it will redirect to the slug of the tag. Which would make the tag clickable. I tried to follow the link below but it only shows how to do it with the class view.

https://godjango.com/33-tagging-with-django-taggit/

views.py

    def post_feed(request):
        if not request.user.is_staff or not request.user.is_superuser:
            raise Http404
            queryset_list = Post.objects.all()
            tags = Tag.objects.all()

            query = request.GET.get("q")
            if query:
                queryset_list = queryset_list.filter(
                Q(title__icontains=query)|
                Q(tags__icontains=query)|
                Q(description__icontains=query)|
                Q(user__first_name__icontains=query)|
                Q(user__last_name__icontains=query)
                ).distinct()
                paginator = Paginator(queryset_list, 5)
                page_request_var = "page"
                page = request.GET.get(page_request_var)
                try:
                    queryset = paginator.page(page)
                except PageNotAnInteger:
                    # If page is not an integer, deliver first page.
                    queryset = paginator.page(1)
                except EmptyPage:
                    # If page is out of range (e.g. 9999), deliver last page of results.
                    queryset = paginator.page(paginator.num_pages)

                    context = {
                    "object_list": queryset,
                    "title": "List",
                    "page_request_var": page_request_var,
                    }

        return render(request, "post_feed.html", context)

And here is my url

url(r'^tag/(?P<slug>[-\w]+)/$', post_feed, name='tagged'),

the Tag.objects.all() only pulls up the tags but doesnt request the slugs.

I am unsure of how to add this to my view without changing it.

from django.views.generic import DetailView, ListView

from taggit.models import Tag

from .models import Product

taggit view to add url and query slug:

class TagMixin(object):
    def get_context_data(self, kwargs):
        context = super(TagMixin, self).get_context_data(kwargs)
        context['tags'] = Tag.objects.all()
        return context

class ProductDetail(DetailView):
    template_name = 'product/detail.html'
    context_object_name = 'product'
    model = Product


class ProductIndex(TagMixin, ListView):
    template_name = 'product/index.html'
    model = Product
    paginate_by = '10'
    queryset = Product.objects.all()
    context_object_name = 'products'

class TagIndexView(TagMixin, ListView):
    template_name = 'product/index.html'
    model = Product
    paginate_by = '10'
    context_object_name = 'products'

    def get_queryset(self):
        return Product.objects.filter(tags__slug=self.kwargs.get('slug'))

I have been stuck on this a few days. Any advice would be helpful.

Here is my models.py, sorry had to format it this way to show up as the whole models code.

            from django.db import models

            from django.db.models import Count, QuerySet, F
            from django.utils import timezone
            from django.conf import settings
            from django.contrib.contenttypes.models import ContentType
            from django.core.urlresolvers import reverse
            from django.db.models.signals import pre_save
            from django.utils.text import slugify
            from markdown_deux import markdown
            from django.utils.safestring import mark_safe
            from taggit.managers import TaggableManager

            from comments.models import Comment

            def upload_location(instance, filename):
                return "%s/%s" %(instance.slug, filename)


            class Post(models.Model):
                user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1 )
                title = models.CharField(max_length=75)
                slug = models.SlugField(unique=True)
                image = models.ImageField(
                        upload_to=upload_location,
                        null=True,
                        blank=True,
                        width_field="width_field",
                        height_field="height_field")
                height_field = models.IntegerField(default=0)
                width_field = models.IntegerField(default=0)
                description = models.TextField()
                tags = TaggableManager()
                public = models.BooleanField(default=False)
                updated = models.DateTimeField(auto_now_add=False, auto_now=True)
                created = models.DateTimeField(auto_now_add=True, auto_now=False)


                def __str__(self):
                    return self.title

                def get_absolute_url(self):
                    return reverse("posts:detail", kwargs={"slug": self.slug})

                class Meta:
                    ordering = ["-created", "-updated" ]

                def get_markdown(self):
                    description = self.description
                    markdown_text = markdown(description)
                    return mark_safe(markdown_text)

                @property
                def comments(self):
                    instance = self
                    qs = Comment.objects.filter_by_instance(instance)
                    return qs

                @property
                def get_content_type(self):
                    instance = self
                    content_type = ContentType.objects.get_for_model(instance.__class__)
                    return content_type


            def create_slug(instance, new_slug=None):
                    slug = slugify(instance.title)
                    if new_slug is not None:
                        slug = new_slug
                    qs = Post.objects.filter(slug=slug).order_by("-id")
                    exists = qs.exists()
                    if exists:
                        new_slug = "%s-%s" %(slug, qs.first().id)
                        return create_slug(instance, new_slug=new_slug)
                    return slug



            def pre_save_post_receiver(sender, instance, *args, **kwargs):
                if not instance.slug:
                    instance.slug = create_slug(instance)


            pre_save.connect(pre_save_post_receiver, sender=Post)

here is my template

    <div class="row">
      <div class="col-sm-2">
         <div class="panel panel-primary">
          <div class="panel-heading">
            Tags
          </div>
          <div class="panel-body">
            <ul class="list-group">
              {% for tag in tags %}
                <li><a href="{% url 'tagged' tag.slug %}"></a></li>
              {% empty %}
                <li>No Tags</li>
              {% endfor %}
            </ul>
          </div>
        </div>
      </div>
    </div>


    <div class="container">
    <div class='col-sm-6 col-sm-offset-3'>
    <h1> Post Feed </h1>
    <form method='GET' action'' class='row'>
      <div class='col-sm-6'>
          <div class='input-group'>
              <input class='form-control' type='text' name='q' placeholder='Search posts' value='{{ request.GET.q }}'/>
              <span class='input-group-btn'>
              <input class= 'btn btn-default' type='submit' value='Search'/>
          </div>
      </div>
    </form>
    {% for obj in object_list %}
    <div class="row">
      <div class="col-sm-12">
        <div class="thumbnail">
          {% if obj.image %}
          <img src='{{ obj.image.url }}' class='img-responsive' />
          {% endif %}
          <div class="caption post-detail-item">
            <h3><a href='{{ obj.get_absolute_url }}'><strong>{{ obj.title }}</strong></a> <small>{{ obj.created|timesince }} ago</small>
            </h3>
            {% if obj.user.get_full_name %}<p>Poster: {{ obj.user.get_full_name }}</p>{% endif %}
            {{ obj.get_markdown|truncatechars_html:50 }}
            <p>Tags: {{ obj.tags|join:" | "|title }}</p>
            <p><a href="{{ obj.get_absolute_url }}" class="btn btn-primary" role="button">View</a></p>
          </div>
        </div>
      </div>
    </div>
    {% endfor %}

    <div class="pagination">
        <span class="step-links">
            {% if object_list.has_previous %}
                <a href="?{{ page_request_var }}={{ object_list.previous_page_number }}{% if request.GET.q %}&
                q={{ request.GET.q }}{% endif %}">previous</a>
            {% endif %}

            <span class="current">
                Page {{ object_list.number }} of {{ object_list.paginator.num_pages }}.
            </span>

            {% if object_list.has_next %}
                <a href="?{{ page_request_var }}={{ object_list.next_page_number }}&q={{ request.GET.q }}">next</a>
            {% endif %}
        </span>
    </div>

    <footer>
      <p class="pull-right"><a href="#">Back to top</a></p>
      <p>&copy; 2016 Holms, Inc. &middot; <a href='{% url "privacy" %}'>Privacy</a> &middot; <a href="#">Terms</a></p>
    </footer>
    </div>
    {% endblock content %}
    </div>
joseph.h
  • 79
  • 11
  • Ok so you want all the POSTS that contain the TAG you click--yes? I think the issue is easy to fix but your question needs to be clarified. You also never use the Tags.objects.all() you query. The tags are connected via a relationship to a post. So `Post.objects.all().prefetch_related('tags')` and then accessing it inside your template looping over `object.tags.all()` is most likely what you want. Does this kind of make sense? Let me know if I'm reading your question wrong.. Please post your template -- it may clarify your question. – shark3y Aug 23 '16 at 05:17
  • Sorry, I actually pulled it out. What i was trying to do is be able to have a list of 10-20 tags in a list. The tags on the list would be click-able. once you click on the tag it goes to the slug of the tag, and brings up all post associated with the tag, if that helps. In the class based view it is easy to do as it all integrates into one another. But i am unsure of how to set this up in my current view. Any help would be great. – joseph.h Aug 23 '16 at 16:14
  • Ok, I think I understand. Can you include your template as well? Then I should be able to submit an answer. Oh, and your models ideally. Just the post model should be enough. You could have modified the class based views you show to do the job as well but I'll stick to fixing your current function based view. – shark3y Aug 23 '16 at 16:36
  • I went ahead and added my models and my template. Thanks shark3y. – joseph.h Aug 23 '16 at 17:49

1 Answers1

1

Just copy and paste these.

Change the urls.py entry to this:

url(r'^tag/(?P<pk>[-\w]+)/$', tag_list, name='tagged'),

Your post_feed function to this (views.py):

def post_feed(request):
    if not request.user.is_staff or not request.user.is_superuser:
        raise Http404
    queryset = Post.objects.all()
    query = request.GET.get("q")
    if query: # this is a separate variable (for searching I'm assuming)
        queryset_list = queryset_list.filter(
                Q(title__icontains=query)|
                Q(tags__icontains=query)| # I would consider taking this out. It's gonna cause problems.
                Q(description__icontains=query)|
                Q(user__first_name__icontains=query)|
                Q(user__last_name__icontains=query)
        ).distinct()
    # bring pagination/tag lookup outside of the if query block -- so you don't NEED a query
    paginator = Paginator(queryset, 5)
    page_request_var = "page"
    page = request.GET.get(page_request_var)
    try:
        queryset = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        queryset = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        queryset = paginator.page(paginator.num_pages)
    context = {
        "object_list": queryset,
        "tags": tags.objects.all()[0:20], # first 20 tags of all tags
        "title": "List",
        "page_request_var": page_request_var,
    }

    return render(request, "post_feed.html", context)

And your new function to show just posts based on a specific tag to this (views.py):

""" modelled after the function above -- so it's easy to understand """
def tag_list(request, tag_id):
    if not request.user.is_staff or not request.user.is_superuser:
        raise Http404
    queryset = Post.objects.filter(tag__id=tag_id)
    paginator = Paginator(queryset, 5)
    page_request_var = "page"
    page = request.GET.get(page_request_var)
    try:
        queryset = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        queryset = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        queryset = paginator.page(paginator.num_pages)
    context = {
        "object_list": queryset,
        "tags": tags.objects.all()[0:20], # first 20 tags of all tags
        "title": "List",
        "page_request_var": page_request_var,
    }
    return render(request, "post_feed.html", context)

then change the template to (post_feed.html):

<li><a href="{% url 'tagged' tag.pk %}">{{tag.name}}</a></li>

also, read this: https://docs.djangoproject.com/en/1.9/topics/http/urls/

shark3y
  • 166
  • 6
  • I got an error: name 'tag_id' is not defined | queryset.filter(tags__id=tag_id) # this will get all posts that correspond to the tag you clicked ... – joseph.h Aug 23 '16 at 22:52
  • Right I forgot something. On your method declaration you need to define that variable. I'll edit the post right now, but it's `def post_feed(request, tag_id):`. As shown in this post: http://stackoverflow.com/a/150518/3866487 – shark3y Aug 23 '16 at 23:11
  • Thanks! I am getting a new error now, | post_feed() missing 1 required positional argument: 'tag_id' – joseph.h Aug 24 '16 at 00:03
  • Oh you gotta change your url regex, cause now you want it to capture a numeric tag id. So change it to `url(r'^tag/(?P\d+)/$', post_feed, name='tagged'),` Cause before it was tailored for a slug string (words seperated by dashes) and so it captured nothing. I'll also edit my answer to reflect this. Stick with it man. It's frustrating at first but you get better at it over time. Also, you can name the url capture whatever you want. In this situation calling it 'pk' is standard to denote it's a primary key value. – shark3y Aug 24 '16 at 00:17
  • Thanks for the encouragement. I tried again but for some reason got the same answer. i can add all my urls if that helps – joseph.h Aug 24 '16 at 00:33
  • It's worth reading this: https://docs.djangoproject.com/en/1.10/topics/http/urls/ --> what url are you trying to go to? Cause you can't go to that page without a tag argument with your current setup.. So what url are you trying to visit when it gives that error? it's gotta look like http://127.0.0.1:8000/tag/1/ (notice the number 1). That's the PK of the tag you want to filter on. – shark3y Aug 24 '16 at 00:41
  • You have the same function working for two purposes. This can be done but wasn't clear in the original post. I'm gonna edit my response to reflect one that will work with either url call. So it should work for both url calls now. I added a default value for the parameter. – shark3y Aug 24 '16 at 00:50
  • sorry I didnt make that clear. i was going to http://127.0.0.1:8000/posts/feed which is the main page of my post. http://127.0.0.1:8000/posts/tag/1/ is what I believe the url we setup is supposed to do. sorry for the miscommunication. – joseph.h Aug 24 '16 at 00:54
  • All good, try it with my edits. That way it's not required by the function so it'll work for /feed/ and /tag/#/ – shark3y Aug 24 '16 at 00:54
  • i am getting (name 'tags' is not defined) for the tags in content. Do I need to import it from somewhere? just want to be sure. – joseph.h Aug 24 '16 at 01:05
  • I made the edit. Your model must be Tag, not tags which I erroneously typed. – shark3y Aug 24 '16 at 01:10
  • Great! its moving along lol. I got a template issue now (Reverse for 'tagged' with arguments '(1,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []) for (
  • {{tag.name}}
  • ). when I take out the href i can see the tags and the page comes up. – joseph.h Aug 24 '16 at 01:19
  • Now you gotta change that to show that it's a kwargs instead of an arg. So that line becomes: `
  • {{tag.name}}
  • ` That way it knows you want to pass that as kwargs (which is now that tag_id is). – shark3y Aug 24 '16 at 01:26
  • (Reverse for 'tagged' with arguments '()' and keyword arguments '{'tag_id': 1}' not found. 0 pattern(s) tried: []) got this error when I switched it. :( – joseph.h Aug 24 '16 at 01:32
  • Yeah this is one of the reasons I wouldn't have done it all in one function. That said you can try this: `url(r'^tag/(?P\d+)/$', post_feed, name='tagged', kwargs={"tag_id": None}),` for the url. That said I don't remember it being necessary... So there might be something else wrong. – shark3y Aug 24 '16 at 01:43
  • cool, i will try that. If you are suggesting maybe try two functions, where would i start with that? – joseph.h Aug 24 '16 at 01:53
  • This really isn't the place for more discussion on this. I'd have made it all a generic GET variable like you did for 'q' (maybe called 'tag') and then you'd filter on that. That way you can search by whatever fields you want in GET parameters. SO is more for debugging your current code, and sometimes suggesting changes in design are not taken well by the original poster. So I stick to fixing it the way it is (design wise). – shark3y Aug 24 '16 at 02:02
  • As one looking to absorb as much information as I can, I really take no offense lol. thanks for all the help and I will continue to work on this. Please feel free to add any other suggestions that could help me on my way. thanks again. – joseph.h Aug 24 '16 at 02:10
  • I'm gonna do a final edit and show what another function would look like. That's gonna be the quickest way to get this working right. – shark3y Aug 24 '16 at 02:18
  • Sweet!! That would be awesome. – joseph.h Aug 24 '16 at 02:56
  • Hey Shark3y, I may me on to something. I added the function based view on here (https://github.com/alex/django-taggit/blob/develop/taggit/views.py) and i think it may work. the tags are now link and go to the right url but I get an error (tagged_object_list() missing 1 required positional argument: 'queryset') I will update my views and the url. let me know what you think. – joseph.h Aug 24 '16 at 19:18
  • I think you were better off adding the view I showed above. – shark3y Aug 24 '16 at 19:23
  • Hey! I tried and for some reason kept getting that same error no matter what I did. It was unable to locate those id no matter what I tried. (Reverse for 'tagged' with arguments '(1,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []) – joseph.h Aug 24 '16 at 19:24
  • Well you need to change your template back to: `
  • {{tag.name}}
  • ` of course. Did you do that? Your urls are wrong -- please post them all. – shark3y Aug 24 '16 at 19:26