django generic view: detail in category - python

I use a generic view to list my categories.
I would also like to display the title of each items belonging to these categories.
I understand the principle of ListView and DetailView but what about some details in lists ?
Here are my different files:
Models.py
class categories(models.Model):
name = models.CharField(max_length=50,unique=True)
slug = models.SlugField(max_length=100,unique=True)
def __str__(self):
return self.name
class details(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=42)
category = models.ForeignKey('categories', on_delete=models.CASCADE)
def __str__(self):
return self.title
Views.py
class IndexView(generic.ListView):
model = categories
context_object_name = "list_categories"
template_name='show/index.html'
Urls.py
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
]
Index.html
{% load static %}
<p>These is a list of categories</p>
{% for category in list_categories %}
<div class="article">
<h3>{{ category.name }}</h3>
{% for title in category.detail %}
<p> {{title}} </p>
{% endfor %}
</div>
{% endfor %}

You need to first reverse call the details with related name i.e "categories".
{% load staticfiles %}
<p>These is a list of categories</p>
{% for category in list_categories %}
<div class="article">
<h3>{{ category.name }}</h3>
{% for detail in category.categories.all %}
<p> {{detail.title}} </p>
{% endfor %}
</div>
Be careful you must use all after reverse all because there could be more then one reverse relation.
Still have any doubts comment it below.

Related

How to display name of table from queryset in django

Trying to get the table name in django, I need it to display the detailview correctly via if statemnet.
I have such a view to display
class Home(ListView):
template_name = 'home.html'
def get_queryset(self):
qs1 = Book.objects.all()
qs2 = CD.objects.all()
qs3 = Film.objects.all()
queryset = sorted(list(chain(qs1, qs2, qs3)), key=operator.attrgetter('title'))
return queryset
and it returns to me this
[<CD: Music1>, <CD: Music2>, <Book: Some books>]
How can I get "CD" or "Book" in this template
{% block content %}
<div class="row">
{% for object in object_list %}
<div class="col-md-3">
<div class="card card-product-grid">
<img src="{{ object.image.url }}">
{{ object.title }}
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
At the same time, if it's a bad idea to display detailview and listview and it's done differently, I'd appreciate it if you'd let me know
I tried different ways of displaying object.key in a loop but it didn't work very well. And other queryset queries.
I've been down the list(chain(ob1, ob2, .. obn) route. It proved highly tedious from a standpoint of code maintainability and complexity. Django Polymorphic is the way to go here.
from polymorphic.models import PolymorphicModel
class Product(PolymorphicModel):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='products')
title = models.CharField(max_length=100)
slug = ...
... more fields all products share ex: price
def __str__(self):
return str(self.title)
#property
def model_name(self):
return self._meta.model_name
class Cd(Product):
<model fields for CD model>
class Book(Product):
<model fields for book model>
class Film(Product):
<model fields for film model>
Then:
Product.objects.all()
Will return the instances of all CD, Book and Film objects.
In your template, you can use the property model_name to check if the object is a certain type of model.
{% block content %}
<div class="row">
{% for object in object_list %}
{% if object.model_name == 'book' %}
<div class="col-md-3">
<div class="card card-product-grid">
<img src="{{ object.image.url }}">
{{ object.title }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endblock content %}
You can obtain this with the .verbose_name attribute [Django-doc] of the model options, so my_object._meta.verbose_name. There is however a problem here: you can not access variables that start with an underscore, since these are "protected" or "private".
A solution might be to work with a template filter. You can define a templatetags directory:
app_name/
__init__.py
models.py
templatetags/
__init__.py
model_utils.py
views.py
where you create the files in boldface. In model_utils, you can construct a filter with:
from django import template
register = template.Library()
#register.filter
def modelname(value):
return value._meta.verbose_name
then we can use these in the template with:
{% load model_utils %}
{% for object in object_list %}
…
{{ object|modelname }}
{% endfor %}

Nested for loop in html using django to display information from 2 different models

I have a model called Section and a model called SectionInfo. Section has one field and that's name. A user will create a section by giving it a name. SectionInfo has a foreign key section_name which links it to the Section model. I have a few other fields inside SectionInfo such as things like detail, date_created, etc. What I am trying to accomplish here is using a for loop to display section names that have already been created inside or above some cards that I have set up in an html template. Then I want to use a nested loop to gather the data that is tied to that section name which is what the user inputs in for the SectionInfo model. I am able to display the section names correctly, but the issue im having is in the loop inside that loop. The same detail link is being displayed in every section that was made. Information should only be displayed in the chosen section for which it was made.
Here is a bit of code to help you understand what I am trying to accomplish.
Template file:
{% extends "base.html" %}
{% load bootstrap5 %}
{% load crispy_forms_tags %}
{% load static %}
{% block content %}
{% for i in object %}
{{ i.name}}
<div class="row">
<div class="col-sm-4">
<div class="card">
<div class="card-body">
{% for ii in object_2 %}
Details
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock content %}
Here is what I am getting displayed
whats being displayed
Here is the view that's being used
def section_info_detail(request, pk):
object = get_object_or_404(Section, id=pk)
object_2 = get_object_or_404(SectionInfo, id=pk)
context = {
'object': object,
'object_2':object_2,
}
return render(request, 'manufacturing/section_info_detail.html', context)
Models.py:
class Section(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class SectionInfo(models.Model):
section_name = models.ForeignKey(Section, on_delete=models.CASCADE)
detail = models.CharField(max_length=50)
text = models.TextField(max_length=2000)
date_created = models.DateField('Date created', auto_now_add=True)
date_updated = models.DateField('Date updated', auto_now=True)
Forms.py:
class SectionNameForm(forms.ModelForm):
class Meta:
model = Section
fields = [
'name',
]
class SectionInfoForm(forms.ModelForm):
class Meta:
model = SectionInfo
fields = [
'section_name',
'detail',
'text',
]
If any other information is needed, do tell.
<div class="row">
{% for object in object_list %}
<div class="col-sm-4">
<div class="card">
<div class="card-header">
<h1><strong>{{ object.name }}</strong></h1>
<hr>
</div>
<div class="card-body">
<div class="row">
{% for info in object.section_infos.all %}
<ul id="list">
<li>{{ info.date_created }}</li> |
<li>{{ info.detail|truncatechars:20 }}</li>
<hr>
</ul>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
This did it for me. So basically what my problem was I was not drilling down far enough to get the data that I needed to be displayed from the template. Also I had changed some things up in my models and had created a manyTomany and through field

How to get data from Django One to Many Fields Model using views into templates

I want to display 1 Parent Field with associated Child and further there Childs
I have a Model
class GrandParent(models.Model):
name= models.CharField(max_length=50)
....
class Parent(models.Model):
parent = models.ForeignKey(GrandParent, on_delete=models.CASCADE)
name= models.CharField(max_length=50)
....
class Child(models.Models):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
name= models.CharField(max_length=50)
...
I am able to render objects from Grand Parent But I m unable to link further Parent Class objects and Child class objects in templates
This approach I used in views.py
def list(request):
object_list = GrandParent.published.all()
...
return render('app/list.html')
def detail(request, post,):
post = get_object_or_404(GrandParent, slug=post,
status='published')
return render(request, 'app/detail.html',
{'post' : post,
...
})
Now how to link further Classes in Same Detail View But Associated with there Parent Classes using Foreign Key. I'm following Django docs but they only giving max 2 examples using python shell with just One subclass I m confused How to implement there in this structure. Or anyone have already developed any project specifically with this type of models so please share any link of repo or something, would be wonderful.
#list.html
{% extends "_base.html" %}
{% block title %} mysite{% endblock %}
{% block content %}
<h2>Content </h2>
<ul>
{% for post in object_list %}
<a href="{{ post.get_absolute_url }}">
<li>{{ post.name}}</li></a>
{% endfor %}
</ul>
{% endblock %}
#detail.html
{% extends "_base.html" %}
{% block title %} freeStreams {% endblock %}
{% block content %}
<div class="card-body">
<h1 class="card-title">{{ post.title }}</h1>
</div>
{% endblock %}
You can access from one to many in view template by for loop all child of it. In your case, with GrandParent by post.parent_set.all(). For Child by parent.child_set.all(). You can try first in terminal for check you have correct database and relation.
Add it inside block content
detail.html
{% for parent in post.parent_set.all() %}
<h1 class="card-title">{{ parent.name }}</h1>
{% for child in parent.child_set.all() %}
<h2 class="card-title">{{ child.name }}</h2>
{% endfor %}
{% endfor %}

how to add comments to an article in django(Python)?

I'm new to Django, I repeat after the tutorial, but I just can not display comments even from the database in html.
urls.py
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
models.py
from django.db import models
class Post(models.Model):
photo = models.ImageField(upload_to='media/photos/',null=True, blank=True)
name_barber = models.CharField(max_length=30)
description = models.TextField(blank=True, null=True)
def __str__(self):
return self.description[:10]
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
body = models.TextField()
date_add = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.post, self.name)
html file
} </style>
{% for post in posts %}
<img src="{{MEDIA_URL}}{{post.photo.url}}" width="800" />
<h3>{{ post.name_barber}}</h3>
<p>{{ post.description}}</p>
{% endfor %}
<h3> Comments.. </h3>
{% if not post.comments.all %}
no comments yet...Add one
{% else %}
{% for comment in post.comments.all %}
<strong>
{{ comment.name }}
{{ comment.date_add }}
</strong>
{{comment.body }}
{% endfor %}
{% endif %}
after adding a comment through the admin panel, the page displays: no comments yet..
What is the problem please tell me??
Remove the if/else condition and use the {% empty %} tag with your forloop.
{% for comment in post.comments.all %}
<strong>
{{ comment.name }}
{{ comment.date_add }}
</strong>
{{comment.body }}
{% empty %}
no comments yet...Add one
{% endfor %}

Pagination with ListView django

I want to create app with search and pagination. Pagination didn't work with ListView.
When I click on the link "next" I am moving from start page http://127.0.0.1:8001/ ---> to the http://127.0.0.1:8001/?city=2 but elements of the list did not change.
And next click to the "next" link did not changes the url ( http://127.0.0.1:8001/?city=2 --> http://127.0.0.1:8001/?city=2).
Could you help me to find error?
I think that error in *.html file, but can't find it
My code:
models.py
from django.db import models
class City(models.Model):
name = models.CharField(max_length=255)
state = models.CharField(max_length=255)
class Meta:
verbose_name_plural = "cities"
def __str__(self):
return self.name
urls.py
# cities/urls.py
from django.urls import path
from . import views
from .views import HomePageView, SearchResultsView
urlpatterns = [
path('search/', SearchResultsView.as_view(), name='search_results'),
path('', HomePageView.as_view(), name='home'),
path('city/<int:pk>/', views.city_detail, name='city_detail'),
]
views.py
from django.shortcuts import render
from django.views.generic import TemplateView, ListView
from .models import City
from django.db.models import Q
from django.shortcuts import render, get_object_or_404
class HomePageView(ListView):
model = City
template_name = 'cities/home.html'
paginate_by = 3
def city_detail(request, pk):
city = get_object_or_404(City, pk=pk)
return render(request, 'cities/city_detail.html', {'city': city})
class SearchResultsView(ListView):
model = City
template_name = 'cities/search_results.html'
def get_queryset(self): # new
query = self.request.GET.get('q')
object_list = City.objects.filter(
Q(name__icontains=query) | Q(state__icontains=query)
)
return object_list
home.html
<!-- templates/home.html -->
<h1>HomePage</h1>
<form action="{% url 'search_results' %}" method="get">
<input name="q" type="text" placeholder="Search...">
</form>
<ul>
{% for city in object_list %}
<li>
<h1>{{ city.name }}</h1>
</li>
{% endfor %}
</ul>
{{page_obj}}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
The standard name for the page query parameter is 'page' You should either change the name of the queryparameter, or render the template with the ?page= parameter.
Option 1: Changing the page_kwarg
You can change that by altering the page_kwarg attribute [Django-doc]:
class HomePageView(ListView):
model = City
template_name = 'cities/home.html'
paginate_by = 3
page_kwarg = 'city'
Option 2: Changing the template
It might be more sensical however to simply change the template, such that it uses page as parameter:
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>

Categories