Creating Tag Index Django - django

Im using django-taggit to create a tagging system for a blog. How do you separate and filter objects so that only ones with selected tags are shown? Kind of like how on StackOverflow if you click on django
it will give you all the questions tagged django. I have tried the method described on this blog post, but I get an IndexError: tuple index out of range. This is the code I am using:
url(r'^tagged/(?P<tag>[\w-]+)/$', TagView.as_view(), name='tag_url'),
class TagView(ListView):
context_object_name = 'blog'
template_name = 'links/index.html'
def get_queryset(self):
return Blog.objects.filter(tags__name__in=[self.args[0]])
def get_context_data(self, **kwargs):
context = super(TagView, self).get_context_data(**kwargs)
context['requested_tag'] = self.args[0]
return context
<a href='{% url tag_url tag=tag %}'>{{ tag.name }}</a>
Am I missing something to get this method to work?
It seems like this is a pretty common programming necessity. Maybe you know a better method... Thanks for your ideas!
EDIT: TagView based on #catherine's suggestions:
class TagView(ListView):
model = Blog
context_object_name = 'blog_list'
template_name = 'tag-list.html'
def get_queryset(self):
queryset = super(TagView, self).get_queryset()
return queryset.filter(tags__name__in=self.kwargs['tags'])
class Blog(models.Model):
name = models.CharField(max_length=50)
date = models.DateTimeField()
slug = models.SlugField()
article = models.TextField()
tags = TaggableManager()
def __unicode__(self):
return self.name
tag-list.html:
{% block content %}
stuff
{% for blog in blog_list %}
{{ blog.article }}
{{ blog.name }}
{% endfor %}
{% endblock %}
The blog_list does not exist in the template, and no blog objects are available. Rather, only 'stuff' is rendered to the template. Any ideas are appreciated! Thanks!

class TagView(ListView):
model = Blog
......
def get_queryset(self):
# Fetch the queryset from the parent get_queryset
queryset = super(TagView, self).get_queryset()
return queryset.filter(tags__name__in=self.kwargs['tag'])

This answer is based on "EDIT: TagView based on #catherine's suggestions:".
You have a typo, in get_queryset method:
return queryset.filter(tags__name__in=self.kwargs['tags'])
you use tag and not tags thus it should be:
return queryset.filter(tags__name__in=[self.kwargs['tag']])

Related

Django Queryset to search for article title

I aim to search for the article title using a query set, I am following this 'basic filtering' guide however it doesn't work for me.
terminal traceback-
AttributeError: 'DeferredAttribute' object has no attribute 'filter'
views.py
class SearchResultsView(ListView):
model = Article
template_name = 'search_results.html'
queryset = Article.title.filter(name__icontains='1st')
I tried using queryset = Article.objects.filter(name__icontains='1st') however this resulted in the below which is why I used 'title' rather than 'objects'
File "/Users/Lucas/Python/Projects/news/.venv/lib/python3.10/site-packages/django/db/models/sql/query.py", line 1677, in names_to_path
raise FieldError(
django.core.exceptions.FieldError: Cannot resolve keyword 'name' into field. Choices are: author, author_id, body, comment, date, id, title
models.py
class Article(models.Model):
title = models.CharField(max_length=225)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("article_detail", kwargs={"pk": self.pk})
I looked at this but can't get it to work. Also tried the documentation.
If i remove the query set line at the bottom of the class the search function returns all of the values as per the below .html file. Which displays all the article content but without any filters of course.
search_results.html
<ul>
{% for article in article_list %}
<li>
{{ article.title }}, {{ article.body }} {{ article.date }}{{ article.author }}
</li>
{% endfor %}
</ul>
Am I missing something from the model perhaps?
Thanks.
Try:
queryset = Article.objects.filter(title__icontains='1st') in SearchResultsView
The error mentions that you do not have name field. Since you are using title field, you need to use that instead.

How do I pass an additional object to a Django form so I can render some fields in the template?

I have a form in Django where site visitors can submit "gear" to be included in a person's set of gear. Here's the URL to the change form:
# urls.py
path('person/<slug:slug>/gear/submit/', GearSubmitView.as_view(), name='forms/submit_gear'),
You can see that the person for whom the gear is being submitted is represented by a slug in the URL.
Here's the first part of the CreateView:
# views.py
class GearSubmitView(LoginRequiredMixin, CreateView):
"""Allows for third-party submissions for a pro's gear collection."""
template_name = 'forms/submit_gear.html'
form_class = GearSubmitForm
success_message = 'Success: Submission added.'
And the form:
# forms.py
class GearSubmitForm(forms.ModelForm):
class Meta:
model = PersonProduct
fields = ['product', 'version', 'setup_note', 'usage_start_date', 'evidence_link', 'evidence_text']
where PersonProduct is a junction table between my Person and Product models.
And the template:
# submit_gear.html
{% extends '_base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h2 id="content-header">Submit Gear For {{ person.full_name }}</h2>
{% crispy form %}
</div>
{% endblock content %}
where you can see what I'm trying to do. I want to insert the name of the person represented by the slug in the URL in the form template.
How can I do it?
You can override get_context_data method in your views.py as mentioned in FormMixin.
class GearSubmitView(LoginRequiredMixin, CreateView):
"""Allows for third-party submissions for a pro's gear collection."""
template_name = 'forms/submit_gear.html'
form_class = GearSubmitForm
success_message = 'Success: Submission added.'
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
person = Person.objects.filter(slug=self.kwargs.get("slug")).first()
data['full_name'] = person.full_name if person else ""
return data
You can change the variable name with full_name in the html file. You can also pass whole instance if you need, I just minimize the data sending from view to html. I didn't run the code block above but it should something like this.
Here you can use the get_context_data method inside of your createview it will look like :
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
and on your template you will add :
{{user}}
Hope it help you out !

How do I get an image from class Account, and use it in PostListView?

I got the images working on my template, but the image being displayed is the current logged in user's image.
How do I filter it so that Profile.objects.get(user=[the owner of the post's user])?
class PostListView(ListView):
model = Post
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['user_post'] = Post.objects.filter(user_id=self.request.user.id)
# context['user_profile'] = Profile.objects.get(user=self.request.user.id)
context['user_profile'] = Profile.objects.get(user=1)
return context
The error code says: 'PostListView' object has no attribute 'user'. I don't understand this error code because from my understand PostListView's model is set to Post which has the following models:
class Post(models.Model):
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
...
So in other words every post made to the blog has a user.
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['user_post'] = Post.objects.filter(user_id=self.request.user.id)
# context['user_profile'] = Profile.objects.get(user=self.request.user.id)
context['user_profile'] = Profile.objects.get(user=1)
return context
What I believe self is doing here is represents the current object in PostListView right?
Can you please ELI5? I am having some trouble understanding the documentation since the language used in there is difficult for me.
Try something like this,
class PostListView(ListView):
template_name = 'add_template_name.html'
model = Post
context_object_name = 'all_posts'
def get_queryset(self):
return Post.objects.all()
Now in your template add this,
{% for post in all_posts %}
<img src="{{ post.user.profile_picture }}" alt="image">
<p> {{ post.title }} </p>
{% endfor %}
Whenever you want to use foreign key element, in this case user, you can choose post.user. Assuming your user model has profile_picture and username, you can display them as post.user.profile_picture and post.user.username respectively. For more info, you can go through Django docs.

Django select_related FK model

I'm trying to list all the objects that are in Movie model that are Genre ForeignKey related say comedy genre list all Movie objects related to comedy
# models.py
class Genre(models.Model):
name = models.CharField(max_length=80, unique=True)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.name
def get_absolute_url(self):
return reverse("genres:list", kwargs={"slug": self.slug})
class Movie(models.Model):
title = models.CharField(max_length=80)
genre = models.ForeignKey(Genre)
# views.py
def genre_list(request, slug):
instance = get_object_or_404(Genre, slug=slug)
movies = Movie.objects.select_related('genre').all()
context = {
"objects_list": movies,
}
return render(request, 'genres/genres_list.html', context)
template view genres/genres_list.html
{% for obj in objects_list %}
<div class="box">
<b>{{ obj.title }}</b>
</div>
{% endfor %}
nothing turns up what am I doing wrong?
Your variable name in the context, object_list, doesn't match the variable name in the template, objects_list.
context = {
"object_list": shows,
}
{% for obj in objects_list %}
You need to use the same variable name in both places. I would recommend object_list because it is more common in Django, however something descriptive like movies would be even better.
Finally, if you want to select all the movies in a particular genre, then you should use filter.
instance = get_object_or_404(Genre, slug=slug)
movies = Movie.objects.filter(genre=instance)
You are currently using select_related, which does something different.

Slug Url Regex in Django

After reading a lot about proper use of a slug to create a detail view from a list of objects. However, I am still having problems getting it to work for me. I am displaying a list of objects in my template like:
{% for thing in thing_list %}
<div class='thing-detail'><a href='{% url detail %}'><img src='theimage.png' />
{% endfor %}
But am getting a NoReverseMatch error on detail.
I figure that there is either something wrong with my regex, or there is just a better way of doing this that I am missing.
Regex:
url(r'^thing/(?P<slug>[\w-]+)/$', 'views.detail', name='detail'),
View:
def detail(request, slug):
thing = get_object_or_404(Thing, slug=slug)
return render(request, 'detail.html', {'thing': thing})
Model:
class Thing(models.Model):
user = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add=True)
slug = models.SlugField()
def save(self, **kwargs):
slug = '%s' % (self.user)
unique_slugify(self, slug) ## from http://djangosnippets.org/snippets/1321/
super(Thing, self).save()
Thank you for helping!
You're not passing any arguments to build the detail URL. You probably want to do this:
{% url "detail" thing.slug %}
Which will create a detail URL with the given slug filled in.