for scalability, better to make a model.
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
def get_absolute_url(self): #url name
# return reverse('article-detail', args=(str(self.id)))
return reverse('home')
change in model: makemigrations - migrate
change in admin.py
admin.site.register(Category)
Note that field 'category' added as Select
forms.py
'category': forms.Select(attrs={'class': 'form-control'}),
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields =
('title', 'title_tag', 'author', 'category', 'body')
widgets = {
~
'category':
forms.Select(attrs={'class': 'form-control'}),
~
}
forms.py
choice = [('coding', 'coding'), ('sports', 'sports')]
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'title_tag', 'author',
'category', 'body')
widgets = {
~
'category': forms.Select(choices=choice,
attrs={'class': 'form-control'}),
~
}
'placeholder':choices -> it's like print, to check the value
forms.py
choices = Category.objects.all().values_list('name','name')
#name is from model field
choice_list = []
for item in choices:
choice_list.append(item)
class PostForm(forms.ModelForm):
~
widgets = {
'category': forms.Select(choices=choice_list,
attrs={'class': 'form-control'}),
}
views.py
class AddCategoryView(CreateView):
model = Category
template_name = 'add_category.html'
fields = '__all__'
create add_category.html
{% extends 'base.html' %}
{% block title %} New Category {% endblock %}
{% block content %}
{% if user.is_authenticated %}
<h1> Add Category </h1>
<br/>
<div class="form-group">
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<br/>
<button class="btn btn-secondary"> Category </button>
</form>
</div>
{% else %}
You are not allowed here. Please log in.
{% endif %}
{% endblock %}
urlpatterns = [
~
path('add_category', AddCategoryView.as_view(),
name="add_category"),
def get_absolute_url
at models.pymodels.py
class Category(models.Model):
~
def get_absolute_url(self):
return reverse('home')
for a change, make it as a function based view.
path('category/<str:cats>/', CategoryView, name="category"),
def CategoryView(request, cats):
category_posts = Post.objects.filter(category=cats)
return render(request, 'categories.html',
{'cats':cats, 'category_posts':category_posts})
categories.html
{% extends 'base.html' %}
{% block content %}
{% if category_posts %}
<h1> Categorised by {{cats}} </h1>
<ul>
{% for post in category_posts %}
<li><a href="{% url 'article-detail' post.pk %}">
{{ post.title }}</a> {{ post.category }} - {{ post.author.first_name }}{{ post.author.last_name }} - {{ post.post_date}}
{% if user.is_authenticated %}
<small><a href="{% url 'update_post' post.pk %}"> (Edit) </a></small>
<small><a href="{% url 'delete_post' post.pk %}"> (Delete) </a></small>
{% endif %}
<br/>{{ post.body|slice:"100"|safe }} </li>
{% endfor %}
</ul>
{% else%}
sorry this page does not exist.
{% endif %}
{% endblock %}
home.html
<a href="{% url 'category' post.category %}">
{{ post.category }}</a>
A Slug used in URLs. A slug field in Django is used to store and generate valid URLs for your dynamically created web pages.
When making url, It should have space in between words; however, when creating category, we might not take it into account. Hence this happenes
Note that there is a whitespace in between coding and tutorial.
Solution:
pipe slugify removes whitespace
post.category|slugify
<a href="{% url 'category' post.category|slugify %}">
{{ post.category }}</a>
use python replace() function
def CategoryView(request, cats):
category_posts = Post.objects.filter(
category=cats.replace('-', ' '))
return render(request, 'categories.html',
{'cats':cats.replace('-', ' '),
'category_posts':category_posts})
outside authentication. we want to show it regardless authority.
base.html
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
{% if cat_menu %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Categories
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
{% for item in cat_menu %}
<a class="dropdown-item" href="{% url 'category' item|slugify %}">{{ item }}</a>
{% endfor %}
</div>
</li>
{% endif %}
{% if user.is_authenticated %}
class HomeView(ListView):
~
#pass context
def get_context_data(self, *args, **kwargs):
cat_menu = Category.objects.all()
context = super(HomeView, self).get_context_data(*args, **kwargs)
context["cat_menu"] = cat_menu
return context
Because we made 'get_context_data' function in a HomeView, Categories dropdown menu disapears when we move to other pages. (Even though it was written in base.html)
We have to add def get_context_data(self, *args, **kwargs):
under each Views.
The error message "Reverse for geometry dash lite'' not found. '' is not a valid view function or pattern name" typically occurs when there is an issue with specifying the URL in Django templates.