I'm building a news web site as a part of a task I was given, homework.
I have a "articles.html" template which renders all of my news articles by publish date. I added a for loop in the template to loop over the Category model and display Categories as a list.
What I'm trying to do now is to filter my articles by category, so when I click "sports" on the list, my site now displays only sports related articles.
I have read so much online, and I just got confused, I'm supposed to do this today but I'm having a rough day and would appreciate some guidance !
Here are my models.py :
from django.db import models
from datetime import datetime
from autoslug import AutoSlugField
class Category(models.Model):
category_title = models.CharField(max_length=200, default="")
def __str__(self):
return self.category_title
class Article(models.Model):
title = models.CharField('title', max_length=200, blank=True)
slug = AutoSlugField(populate_from='title', default="",
always_update=True, unique=True)
author = models.CharField('Author', max_length=200, default="")
description = models.TextField('Description', default="")
is_published = models.BooleanField(default=False)
article_text = models.TextField('Article text', default="")
pub_date = models.DateTimeField(default=datetime.now, blank=True)
article_image = models.ImageField('Article Image')
article_category = models.ForeignKey(Category, on_delete="models.CASCADE", default="")
img2 = models.ImageField('Article Image 2', default="", blank=True)
img3 = models.ImageField('Article Image 3', default="", blank=True)
img4 = models.ImageField('Article Image 4', default="", blank=True)
img5 = models.ImageField('Article Image 5', default="", blank=True)
img6 = models.ImageField('Article Image 6', default="", blank=True)
def __str__(self):
return self.title
My views.py :
from django.shortcuts import render, reverse, get_object_or_404
from django.views import generic
from news.models import Article, Category
from .forms import CommentForm
from django.http import HttpResponseRedirect
class IndexView(generic.ListView):
template_name = 'news/index.html'
context_object_name = 'latest_article_list'
def get_queryset(self):
return Article.objects.order_by("-pub_date").filter(is_published=True)[:6]
class CategoryView(generic.ListView):
template_name = 'news/categories.html'
context_object_name = 'category'
def get_queryset(self):
return Category.objects.all()
def article(request, article_id):
article = get_object_or_404(Article, pk=article_id)
context = {'article': article}
return render(request, 'news/article.html', context)
class ArticlesView(generic.ListView):
context_object_name = 'latest_article_list'
template_name = 'news/articles.html'
queryset = Article.objects.order_by("-pub_date")
def get_context_data(self, **kwargs):
context = super(ArticlesView, self).get_context_data(**kwargs)
context['category'] = Category.objects.all()
return context
def add_comment_to_article(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = article
comment.save()
return HttpResponseRedirect(reverse('news:article', kwargs={"article_id": article.pk}))
else:
form = CommentForm()
return render(request, 'news/add_comment_to_article.html', {'form': form})
my urls.py :
from django.urls import path, include
from . import views
app_name = "news"
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:article_id>/', views.article, name='article'),
path('articles/', views.ArticlesView.as_view(), name='articles'),
path('search/', include('haystack.urls')),
path('<int:pk>/comment/', views.add_comment_to_article, name='add_comment_to_post'),
path('category/<int:category_id>', views.CategoryView.as_view(), name="category")
]
And the template im trying to render everything in, articles.html:
<div class="container">
{% block articles %}
<!-- ***************************************** -->
<ul>
<li>Categories:</li>
{% for category in category %}
<li>
<h1>{{ category.id}}</h1>
<a href="#">{{ category.category_title }}</a>
</li>
{% endfor %}
</ul>
<!-- ***************************************** -->
<hr class="hr-style1">
<h2 class="article-list-title">Article List :</h2>
<hr class="hr-style2">
<div class="container list-wrapper">
{% for article in latest_article_list %}
<div class="container">
<div class="well">
<div class="media">
<a class="pull-left" href="{% url 'news:article' article.id %}">
<img class="media-object" src="{{ article.article_image.url }}">
</a>
<div class="media-body">
<h4 class="media-heading"><a href="{% url 'news:article' article.id %}">{{ article.title }}</a>
</h4>
<p class="text-right">{{ article.author }}</p>
<p>{{ article.description }}</p>
<ul class="list-inline list-unstyled">
<li><span><i class="glyphicon glyphicon-calendar"></i> {{ article.pub_date }} </span></li>
<li>|</li>
<span><i class="glyphicon glyphicon-comment"></i> 2 comments</span>
<li>|</li>
<li>
<span class="glyphicon glyphicon-star"></span>
<span class="glyphicon glyphicon-star"></span>
<span class="glyphicon glyphicon-star"></span>
<span class="glyphicon glyphicon-star"></span>
<span class="glyphicon glyphicon-star-empty"></span>
</li>
<li>|</li>
<li>
<span><i class="fa fa-facebook-square"></i></span>
<span><i class="fa fa-twitter-square"></i></span>
<span><i class="fa fa-google-plus-square"></i></span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
Apologies for the messy code, still trying to learn.
Thank you for taking the time to read this !
I'm not asking you to do this for me, an explanation will be enough ! Thanks !
You can override the get_queryset
method on your ArticlesView
by passing a filter param as follow:
class ArticlesView(generic.ListView):
context_object_name = 'latest_article_list'
template_name = 'news/articles.html'
def get_context_data(self, **kwargs):
context = super(ArticlesView, self).get_context_data(**kwargs)
# It would be best to rename the context to categories
# as you are returning multiple objects
context['categories'] = Category.objects.all()
return context
def get_queryset(self):
# Note you do not have to use the article PK you can use any
# article field just update the template argument accordingly
category_pk = self.request.GET.get('pk', None)
if category_pk:
return Article.objects.filter(article_category__pk=category_pk).order_by("-pub_date")
return Article.objects.order_by("-pub_date")
In your template you can then update the category links as follow:
<ul>
<li>Categories:</li>
{% for category in categories %}
<li>
<h1>{{ category.id}}</h1>
<a href="{% url 'news:articles' %}?pk={{category.id}}">{{ category.category_title }}</a>
</li>
{% endfor %}
<ul>
Give this a try and let me know if it works