Django primary key url issues - django

I am makeing a simple blog project. In my project, there is one page where there is a list of all the blog posts. If you click on the title of the post you are taken to the post. I used (?P\d+) I the URL path so the user would be directed to the correct post. However, this did not work. Any help would be much appreciated.
all_posts.html
{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Blog Posts</h1>
</div>
<div class="container">
{% for blog_post in object_list %}
<table class="table table-striped">
<ul class="list-group">
<li><a class="btn btn-primary" href="{% url 'blog_app:view' pk=blog_post.pk %}">{{ blog_post.post_title }}</a></li>
</ul>
</table>
{% endfor %}
</div>
{% endblock %}
modles.py
from django.db import models
# Create your models here.
class Blog_Post(models.Model):
slug = models.SlugField(max_length=1000, editable=False, null=True)
post_title = models.CharField(max_length=100, editable=True, blank=False, null=True)
blog_content = models.TextField(max_length=10000, blank=False, editable=True, null=True)
files = models.FileField(blank=True, null=True, upload_to=True)
date = models.DateTimeField(blank=False, null=True, auto_now=True, editable=False)
likes = models.IntegerField(blank=True, null=True, editable=False)
urls.py
from django.urls import path
from . import views
app_name = "blog_app"
urlpatterns = [
path('create/', views.create_blog_post.as_view(), name='create'),
path('view/(?P<pk>\d+)/', views.view_blog_post.as_view(), name='view'),
path('all_posts/', views.all_blog_posts.as_view(), name='all'),
path('delete/<slug:slug>', views.delet_blog_post.as_view(), name='delete')
]

path is for converters like slug, str, int, to work with regex you need re_path
you can just redefine it as "view/<int:pk>/"

{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Blog Posts</h1>
</div>
<div class="container">
{% for blog_post in object_list %}
<table class="table table-striped">
<ul class="list-group">
<li><a class="btn btn-primary" href="{% url 'blog_app:view' pk=blog_post.pk %}">{{ blog_post.post_title }}</a></li>
</ul>
</table>
{% endfor %}
</div>
{% endblock %}
i think the problem is in this line
<li><a class="btn btn-primary" href="{% url 'blog_app:view' pk=blog_post.pk %}">
when you send data in url tag you don't assign it to prametars ,
the right line will be this :
<li><a class="btn btn-primary" href="{% url 'blog_app:view' blog_post.pk %}">

Related

Django, how can I show variables in other model class?

I'm learning Django and I'm trying to understand its logic. There are 2 models I created. I am designing a job listing page. The first model is "Business". I publish the classes inside this model in my html page and for loop. {% job %} {% endfor %}. But inside my second model in this loop I want to show company_logo class inside "Company" model but I am failing.
Methods I tried to learn;
1- I created a second loop inside the job loop, company loop, and published it failed.
2- I assigned a variable named company_list to the Job class on the views page and made it equal to = Company.objects.all() but it still fails.
Here are my codes.
models.py
from django.db import models
from ckeditor.fields import RichTextField
from djmoney.models.fields import MoneyField
JobTypes = (
('Full-Time', 'Full-Time'), ('Part-Time', 'Part-Time'),
('Internship', 'Internship'), ('Temporary', 'Temporary'), ('Government', 'Government')
)
class Job(models.Model):
job_title = models.CharField( max_length=100, null=True)
job_description = RichTextField()
job_application_url = models.URLField(unique=True)
job_type = models.CharField(max_length=15, choices=JobTypes, default='Full-Time')
#job_category
job_location = models.CharField( max_length=100)
job_salary = MoneyField(max_digits=14, decimal_places=4, default_currency='USD')
created_date = models.DateTimeField(auto_now_add=True)
featured_listing = models.BooleanField( default=False)
company = models.ForeignKey(
"Company", on_delete=models.CASCADE)
def __str__(self):
return f"{self.company}: {self.job_title}"
class Company(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
company_name = models.CharField(max_length=100, unique=True)
company_url = models.URLField(unique=True)
company_logo = models.ImageField(
upload_to='company_logos/')
def __str__(self):
return f"{self.company_name}"
views.py
from django.utils import timezone
from django.shortcuts import render
from .models import Company, Job
def index(request):
jobs = Job.objects.all().order_by('-created_date')[:3]
company_list = Company.objects.all()
context = {
'jobs': jobs,
'company_list':company_list,
}
return render(request, 'index.html', context)
"{% static 'company.company_logo.url' %}" on this page this code is not working. For example, in the coming days I will create a user model and I will have this problem again while publishing the "user.user_name" variable, which will be an example, in my html page. What is the logic here?
index.html
{% extends '_base.html' %}
{% load static %}
{% block content %}
<div class="container">
<!-- Recent Jobs -->
<div class="eleven columns">
<div class="padding-right">
<h3 class="margin-bottom-25">Recent Jobs</h3>
<ul class="job-list">
{% for job in jobs %}
<li class="highlighted"><a href="job-page.html">
<img src="{{ job.company.company_logo.url}}" alt="Logo" class="company-logo">
<div class="job-list-content">
<h4>{{ job.job_title }} <span class="full-time">{{ job.job_type }}</span></h4>
<div class="job-icons">
<span><i class="fa fa-briefcase"></i>{{ job.company }}</span>
<span><i class="fa fa-map-marker"></i> {{ job.job_location }}</span>
{% if job.job_salary %}
<span><i class="fa fa-money"></i> {{job.job_salary}}</span>
{% else %}
<span><i class="fa fa-money"></i>Salary undisclosed</span>
{% endif %}
</div>
</div>
</a>
<div class="clearfix"></div>
</li>
{% endfor %}
</ul>
<i class="fa fa-plus-circle"></i> Show More Jobs
<div class="margin-bottom-55"></div>
</div>
</div>
<!-- Job Spotlight -->
<div class="five columns">
<h3 class="margin-bottom-5">Featured</h3>
<!-- Navigation -->
<div class="showbiz-navigation">
<div id="showbiz_left_1" class="sb-navigation-left"><i class="fa fa-angle-left"></i></div>
<div id="showbiz_right_1" class="sb-navigation-right"><i class="fa fa-angle-right"></i></div>
</div>
<div class="clearfix"></div>
<!-- Showbiz Container -->
<div id="job-spotlight" class="showbiz-container">
<div class="showbiz" data-left="#showbiz_left_1" data-right="#showbiz_right_1" data-play="#showbiz_play_1" >
<div class="overflowholder">
<ul>
{% for job in jobs %}
{% if job.featured_listing %}
<li>
<div class="job-spotlight">
<h4>{{job.job_title}} <span class="freelance">{{job.job_type}}</span></h4>
<span><i class="fa fa-briefcase"></i>{{job.company}}</span>
<span><i class="fa fa-map-marker"></i> {{job.job_location}}</span>
<span><i class="fa fa-money"></i> {{job.job_salary}} / hour</span>
<p>{{ job.job_description | safe | truncatechars:150 }}</p>
Apply For This Job
</div>
</li>
{% endif %}
{% endfor %}
</ul>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</div>
index.html problem:
urls.py
from django.urls import path
from . import views
from django.conf.urls.static import static
from django.conf import settings
from django.urls import path
from . import views
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('', views.index,name="index"),
#path("jobs/", JobListView.as_view(), name="jobs")
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
the syntax is not
{% static 'company.company_logo.url' %} or **{% static 'company.company_logo.url' %}**
use this instead
{{ company.company_logo.url }}
also if you want to make relations between Job and Company, you can make it like so
inside Job model add this new field
company = models.ForeignKey('Company', on_delete=models.CASCADE)
So that one company can have multiple jobs
Then inside the job for loop in HTML just add this to display company_logo
{% for job in jobs %}
*..........remaining code..........*
<img src="{{ job.company.company_logo.url }}" alt="">
*..........remaining code..........*
{% endfor %}

Only Show Edit Button On Owner's Posts - Django

I have a Django project with posts and the ability to edit posts.
On the main index page, I am showing all posts, like a news feed.
Currently anyone can edit any post, but I want to make it so that only the owner of the post can edit. I'm just not sure how to write the urls.py file since I'm using:
path("", views.index, name="index"),
I would probably need to pass either the post id or the username to this, but I'm not sure how to write it. I tried:
path("index", views.index, name="index"),
path("index/<str:pk>", views.index, name="index"),
path("index/<str:username>", views.index, name="index"),
But I get errors.
views.py
def index(request):
list_of_posts = Post.objects.all().order_by('id').reverse()
paginator = Paginator(list_of_posts, 10)
num = request.GET.get('page', 1)
get_page_num = paginator.get_page(num)
return render(request, "network/index.html", {
"list_of_posts": list_of_posts,
"get_page_num": get_page_num,
})
models.py
class User(AbstractUser):
pass
class Post(models.Model):
text = models.TextField(max_length=500, blank=True,
null=True)
username = models.ForeignKey('User',
on_delete=models.CASCADE, related_name='author',
null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(
User, blank=True, related_name="liked_user")
def __str__(self):
return self.username.username
html to show edit button. I've tried:
{% if post.username == request.user.username %}
<button class="btn-btn primary" my-id="{{i.id}}" id="ebutton-
{{i.id}}" onclick="edit_form(this)" >Edit</button>
<br><br><br>
{% endif %}
Full html of this page:
{% extends "network/layout.html" %}
{% load static %}
{% block body %}
<br>
<h3> <center> Welcome. Here is your news feed: </center> </h3>
<br>
{% for i in get_page_num %}
<div class='card mb-3' style="max-width: 530px;" id="card-posts">
<div class="row no-gutters">
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title"><a href="{% url 'profile'
username=i.username %}">{{i.username}}</a></h5>
<span id="textinfo-{{i.id}}"
class="post">{{i.text}}</span> <br>
<textarea my-id="{{i.id}}" id="edit-me-{{i.id}}"
style="display:none;" class="form-control
textarea" row="3">{{i.text}}</textarea>
<br>
<p class="card-text">{{i.timestamp}}</p>
<div class="like mt-1">
<img my-id="{{i.id}}" id="is-it-liked-{{i.id}}"
class="like-class"
{% if not request.user in i.like.all %}
clicked="no"
src="https://img.icons8.com/emoji/452/white-heart.png"
{%else%}
clicked="yes"
src="https://img.icons8.com/emoji/452/red-heart.png"
{%endif%}
/> <span id="post-count-{{i.id}}">{{i.like.count}}.
</span>
</div>
<br>
{% if request.user == post.username %}
<button class="btn-btn primary" my-id="{{i.id}}"
id="ebutton-{{i.id}}" onclick="edit_form(this)"
>Edit</button>
<br><br><br>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
<br><br>
<div class="pagination">
<span class="step-links">
{% if get_page_num.has_previous %}
<a href="?page={{ get_page_num.previous_page_number }}"
class="page-link">Previous</a>
{% else %}
<li class="page-item disabled"><a class="page-
link">Previous</a></li>
{% endif %}
{% if get_page_num.has_next %}
<a href="?page={{ get_page_num.next_page_number }}" class="page-
link">Next</a>
{% else %}
<a class="page-link">Next</a>
{% endif %}
</span>
</div>
{% endblock %}
{% block script %} <script src="{% static
'network/network.js'%}"></script> {% endblock %}
With this way I need to pass username to the url but I cannot, without getting errors.
Overall I'm just looking for advice, on how to make the edit button only appear on posts that the current user is an owner on. So that way no one can edit anyone else's posts.
If you want to show all posts and only allow a post author to edit it, you need to inject all posts and the request user in your template context. Then in your template iterate through the posts and check if the author is equal to the request user before showing the edit button. But first you need a foreign key in your post model that refers to the author.
Urls.py
urlpatterns = [path("index/", views.index, name="index")]
Views
def index(request):
list_of_posts = Post.objects.order_by('-id')
request_user = request.user
return render(request, "network/index.html", {
"list_of_posts": list_of_posts,
"request_user": request_user
})
Models
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='posts')
# Other fields
Template
{% if post.user == request_user %}
<!--Show the edit button-->
{% endif %}
You can also access directly the request object in your template:
{% if post.user == request.user %}
<!--Show the edit button-->
{% endif %}
I needed to add
{% if request.user == i.username %}
because I was looping in the html page, for i in get_page_num:. I did not previously have the i.
{% if request.user == post.author %}
<button>Delete</button>
{% endif %}
This assumes that you have a Post model with an attribute of author

FIlter Class Based View ListView that has pagination

I'm trying to do a ListView from a Class Based View that has filtering, and pagination
I was able to get the table showing and the pagination working as I want, this gives me a basic list of all invoices that I need to show:
but I'm not able to make the filtering work, for what I understand I need to override the "get_queryset" method and this would return a queryset, (in this cased called "invoices_filtered_list") however I don't know how should I render this in the .html page so it will have the textbox in which I should type.
These are my files:
views.py
from django.views.generic import (TemplateView, ListView)
from .models import WoodhistAzolveInvoices
from .filters import WoodhistAzolveInvoicesFilter
class InvoicesListView(ListView):
model = WoodhistAzolveInvoices
template_name = 'home.html'
context_object_name = 'invoices'
ordering = ['suppliername', 'invoicenumber']
paginate_by = 50
def get_queryset(self):
qs = self.model.objects.all()
invoices_filtered_list = WoodhistAzolveInvoicesFilter(self.request.GET, queryset=qs)
return invoices_filtered_list.qs
urls.py
from django.urls import path
from .views import InvoicesListView
app_name = 'web'
urlpatterns = [
path('', InvoicesListView.as_view(), name='home'),
]
filters.py
import django_filters
from .models import *
class WoodhistAzolveInvoicesFilter(django_filters.FilterSet):
class Meta:
model = WoodhistAzolveInvoices
fields = ['suppliername']
models.py
from django.db import models
class WoodhistAzolveInvoices(models.Model):
docid = models.CharField(db_column='DocId', primary_key=True, max_length=50) # Field name made lowercase.
supplierreference = models.CharField(db_column='SupplierReference', max_length=50, blank=True, null=True) # Field name made lowercase.
suppliername = models.CharField(db_column='SupplierName', max_length=100, blank=True, null=True) # Field name made lowercase.
invoicenumber = models.CharField(db_column='InvoiceNumber', max_length=50, blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'WoodHist_Azolve_Invoices'
home.html
{% extends "base.html" %}
{% block title %} base title {% endblock title %}
{% block content %}
<h1> azolve home </h1>
<div class="row">
<div class="col">
<div class="card card-body">
<form method="get">
{{ invoices_filtered_list.form }}
<button class="btn btn-primary" type="submit">Search</button>
</form>
</div>
</div>
</div>
<table id="myTable1" class="table table-striped table-bordered" style="width:100%">
<thead>
<tr>
<th scope="col">Supplier Name</th>
<th scope="col">Invoice Number</th>
<th scope="col">Reference</th>
</tr>
</thead>
<tbody>
{% for datarow in invoices %}
<tr>
<td>{{datarow.suppliername}}</td>
<td>{{datarow.invoicenumber}}</td>
<td>{{datarow.supplierreference}}</td>
</tr>
{% endfor %}
{% if is_paginated %}
{% if page_obj.has_previous %}
<a class="btn btn-outline-info" href="?page=1">First</a>
<a class="btn btn-outline-info" href="?page={{ page_obj.previous_page_number}}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class="btn btn-info" href="?page={{ num }}"> {{ num }}</a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="btn btn-outline-info" href="?page={{ num }}"> {{ num }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="btn btn-outline-info" href="?page={{ page_obj.next_page_number }}">Next</a>
<a class="btn btn-outline-info" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
{% endif %}
</tbody>
</table>
{% endblock content %}
If it worth noting, I'm using the package "django_filters" and Django 3.2.4
You want filtering and pagination at the same time.
you need to make your pagination to keep your querystrings then you can have them at the same time. Because when you click on next page your querystring will be like this .../?page=page_number so your filtering will be gone.
you can do this:
Inside your app directory create a folder called templatetags.
Inside the templatetags folder create a file called pagination_tags.py(name is up to you).
# Your template tag in app_name/templatetags/pagination_tags.py
from django import template
from urllib.parse import urlencode
register = template.Library()
#register.simple_tag
def url_replace (request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
After creating your template tag load it in your html page and use it.
{% load pagination_tags %}
{% if is_paginated %}
<nav aria-label="Page navigation example" class="d-flex justify-content-center pt-3">
<ul class="pagination">
{% if page_obj.has_previous and page_obj.number != 2 %}
<li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' 1 %}" tabindex="-1" aria-disabled="true">First Page</a></li>
{% endif %}
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' page_obj.previous_page_number %}">{{ page_obj.previous_page_number }}</a></li>
{% endif %}
<li class="page-item disabled"><a class="page-link" href="#">{{ page_obj.number }}</a></li>
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' page_obj.next_page_number %}">{{ page_obj.next_page_number }}</a></li>
{% endif %}
{% if page_obj.paginator.num_pages != page_obj.number and page_obj.paginator.num_pages != page_obj.next_page_number %}
<li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' page_obj.paginator.num_pages %}">Last Page</a></li>
{% endif %}
</ul>
</nav>
{% endif %}
And your ListView should be replaced by FilterView
from django_filters.views import FilterView
from .models import WoodhistAzolveInvoices
from .filters import WoodhistAzolveInvoicesFilter
class InvoicesListView(FilterView):
model = WoodhistAzolveInvoices
template_name = "home.html"
filterset_class = WoodhistAzolveInvoicesFilter
context_object_name = 'invoices'
ordering = ['suppliername', 'invoicenumber']
paginate_by = 50

Django :NoReverseMatch at /spacemissions/organisation/2/

I am getting the following error when, from the list view, I try to access the detail view.
Error screenshot
The odd thing is that some of the detail views work, some not, and I do not understand where is the problem. For example, here's the detail view of the organisation with pk=1
organisation detail view
Organisation model
class Organisation(models.Model):
code = models.CharField(max_length=256, unique=True)
name = models.CharField(max_length=256, null=True)
english_name = models.CharField(max_length=256, null=True)
location = models.CharField(max_length=256, null=True)
country = models.ForeignKey('space_missions.Country',
on_delete=models.SET_NULL, null=True, blank=True)
longitude = models.FloatField(null=True)
latitude = models.FloatField(null=True)
parent_organisation = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True)
Views
class OrganisationDetail(generic.DetailView):
model = models.Organisation
class OrganisationList(generic.ListView):
model = models.Organisation
organisation_list.html
{% extends 'base.html' %}
{% block content %}
<h1>Organisation List</h1>
{% if organisation_list %}
<ul>
{% for organisation in organisation_list %}
<li>
<a href="{% url 'space_missions:organisation-detail' pk=organisation.pk %}">
{{ organisation.name }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No organisation is stored in the database!</p>
{% endif %}
{% endblock %}
organisation_detail.html
{% extends 'base.html' %}
{% block content %}
<div class="container">
<h1>{{ organisation.name }}'s details:</h1>
<ul>
<li><strong>Code: </strong> {{ organisation.code }}</li>
<li><strong>English Name: </strong> {{ organisation.english_name }}</li>
<li><strong>Location: </strong> {{ organisation.location }}</li>
<li><strong>Country: </strong>
<a href="{% url 'space_missions:country-detail' pk=organisation.country_id %}">
{{ organisation.country.name }}
</a>
</li>
<li><strong>Longitude: </strong> {{ organisation.longitude }}</li>
<li><strong>Latitude: </strong> {{ organisation.latitude }}</li>
<li>
<strong>Parent organisation: </strong>
<a href="{% url 'space_missions:organisation-detail' pk=organisation.parent_organisation_id %}">
{{ organisation.parent_organisation.name }}
</a>
</li>
</ul>
</div>
{% endblock %}
urls.py
from django.urls import path
from . import views
app_name = 'space_missions'
urlpatterns = [
path('countries/', views.CountryList.as_view(), name='countries'),
path('country/<int:pk>/', views.CountryDetail.as_view(), name='country-detail'),
path('astronauts/', views.AstronautList.as_view(), name='astronauts'),
path('astronaut/<int:pk>/', views.AstronautDetail.as_view(), name='astronaut-detail'),
path('organisations/', views.OrganisationList.as_view(), name='organisations'),
path('organisation/<int:pk>/', views.OrganisationDetail.as_view(), name='organisation-detail'),
path('engines/', views.EngineList.as_view(), name='engines'),
path('engine/<int:pk>/', views.EngineDetail.as_view(), name='engine-detail'),
path('stages/', views.StageList.as_view(), name='stages'),
path('stage/<int:pk>/', views.StageDetail.as_view(), name='stage-detail'),
path('launchvehicles/', views.LaunchVehicleList.as_view(), name='launch-vehicles'),
path('launchvehicle/<int:pk>/', views.LaunchVehicleDetail.as_view(), name='launch-vehicle-detail'),
path('missions/', views.MissionList.as_view(), name='missions'),
path('mission/<int:pk>/', views.MissionDetail.as_view(), name='mission-detail'),
path('selections/', views.SelectionList.as_view(), name='selections'),
path('selection/<int:pk>/', views.SelectionDetail.as_view(), name='selection-detail')
]
When parent_organisation is None then your detail page can not find url of organisation-detail with pk=None
<a href="{% url 'space_missions:organisation-detail' pk=organisation.parent_organisation_id %}">
{{ organisation.parent_organisation.name }}
</a>
Wrap your code with if block
{% if organisation.parent_organisation_id %}
<a href="{% url 'space_missions:organisation-detail' pk=organisation.parent_organisation_id %}">
{{ organisation.parent_organisation.name }}
</a>
{% endif %}

Can't add normal Comment section in my Django web-application

I have trouble with adding comments to my django web-application. I want to add comments to allow users comment posts, like in normal web blog. I've tried a few ways, but nothing worked correctly for me.
After a few weeks searching and trying different ways to solve this question, I've stacked on this question. I have some progress, but it's now what I want completely.
Now I can only add "comments" from admin panel
and it look like this (from admin panel)
and like this (from user interface)
And I have this issue with padding comments( I don't really understand why it's occurs ¯_(ツ)_/¯ )
Anyway, here is my code, hope someone know how to solve this problem:
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.text
...
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
categories = models.ManyToManyField('Category', related_name='posts')
image = models.ImageField(upload_to='images/', default="images/None/no-img.jpg")
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'slug': self.slug})
post_detail.html
<article class="media content-section">
{% for comment in post.comments.all %}
<ul>
{{ comment.text }}
{% for reply in comment.replies.all %}
<li>
{{ reply.text }}
</li>
{% endfor %}
<ul>
{% endfor %}
</article>
I also read this article about creation blog for beginners, and they have working comment section as I want, but I tried to implement their code to my web-app, and nothing worked for me.
They have comment section like this (I need completely the same one):
But when I tried to follow their tutorial, I have only like this:
And here is the code for this unsuccessful solution( but I feel like it is working one, maybe I did something wrong)
post_detail.html
{% extends 'blog/base.html' %}
{% block content %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ object.author.profile.image.url }}" alt="">
<div class="article-metadata">
<a class="mr-2 author_title" href="{% url 'user-posts' object.author.username %}">#{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"N d, Y" }}</small>
<div>
<!-- category section -->
<small class="text-muted">
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blog_category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</small>
</div>
{% if object.author == user %}
<div>
<a class='btn btn-secondary btn-sm mt-1 mb-1' href="{% url 'post-update' object.slug %}">Update</a>
<a class='btn btn-danger btn-sm mt-1 mb-1' href="{% url 'post-delete' object.slug %}">Delete</a>
</div>
{% endif %}
</div>
</article>
<article class="media content-section">
<div class="media-body">
<img class="img-fluid center" id="rcorners3" src="{{ object.image.url }}" alt="none">
<h2 class="article-title text-center">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
<article class="media content-section">
<form action="/blog/{{ post.pk }}/" method="post">
{% csrf_token %}
<div class="form-group">
{{ form.author }}
</div>
<div class="form-group">
{{ form.body }}
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<h3>Comments:</h3>
{% for comment in comments %}
<p>
On {{comment.created_on.date }}
<b>{{ comment.author }}</b> wrote:
</p>
<p>{{ comment.body }}</p>
<hr>
{% endfor %}
</article>
{% endblock content %}
views.py
...
def comment(request):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = Comment(
author=form.cleaned_data["author"],
body=form.cleaned_data["body"],
post=post
)
comment.save()
comments = Comment.objects.filter(post=post)
context = {
"post": post,
"comments": comments,
"form": form,
}
models.py
...
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.text
forms.py
from django import forms
class CommentForm(forms.Form):
author = forms.CharField(
max_length=60,
widget=forms.TextInput(attrs={
"class": "form-control",
"placeholder": "Your Name"
})
)
body = forms.CharField(widget=forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Leave a comment!"
})
)
You are creating unnecessary lists. Try this one
<article class="media content-section">
<ul>
{% for comment in post.comments.all %}
<li>{{ comment.text }}</li>
{% if comment.replies.all %}
<ul>
{% for reply in comment.replies.all %}
<li>{{ reply.text }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
<ul>
</article>