Alibi Tolebay
Alibi Tolebay

Reputation: 35

Django Blog - Comment System

I am new to Django, and I am wondering how I can intelligently link the system of comments and posts using class-based views. Here's my 'models.py' file in the 'blog' app:

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse


class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)


    def __str__(self):
        return self.title

    
    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})


class Comment(models.Model):
    post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)

    class Meta:
        ordering = ['date_posted']

    def __str__(self):
        return '{} - {}'.format(self.author, self.date_posted)

There's my 'post_detail.html' template that shows the specific post and here I just want to show all comments under the post:

{% extends 'blog/base.html' %}

{% block content %}
    <article class="media content-section">
        <img class="rounded-circle article-img" src="{{ object.author.profile.image.url }}">
        <div class="media-body">
            <div class="article-metadata">
                <a class="mr-2" href="{% url 'user-posts' object.author.username %}">
                    {{ object.author }}
                </a>
                <small class="text-muted">{{ object.date_posted  | date:"F d, Y" }}</small>
                {% if object.author == user %}
                    <div>
                        <a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}">Update</a>
                        <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'post-delete' object.id %}">Delete</a>
                    </div>
                {% endif %}
            </div>
            <h2 class="article-title">
                {{ object.title }}
            </h2>
            <p class="article-content">{{ object.content }}</p>
        </div>
    </article>
    <div>
        <strong><h2>Comments Section</h2></strong>
    </div>
{% endblock content %}

'views.py' file looks like this:

from blog.forms import CommentForm
from django.db import models
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth.models import User
from django.shortcuts import redirect, render
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from django.views.generic import DetailView 
from django.views.generic import CreateView
from django.views.generic import UpdateView
from django.views.generic import DeleteView
from .models import Post
from .models import Comment


def home(request):
    context = {
        'title': 'Home',
        'posts': Post.objects.all()
    }
    return render(request, 'blog/home.html', context)


class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'
    context_object_name='posts'
    ordering = ['-date_posted']
    paginate_by = 7


class UserPostListView(ListView):
    model = Post
    template_name = 'blog/user_posts.html'
    context_object_name='posts'
    paginate_by = 7

    def get_queryset(self):
        user = get_object_or_404(User, username=self.kwargs.get('username'))
        return Post.objects.filter(author=user).order_by('-date_posted')


class PostDetailView(DetailView):
    model = Post


def comment(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.post = post
            comment.save()
            return redirect('post-detail', pk=post.pk)
        else:
            form = CommentForm()
    return render(request, 'blog/post_detail.html', {'form': form})


class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = ['title', 'content']

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)


class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Post
    fields = ['title', 'content']

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

    def test_func(self):
        post = self.get_object()
        if self.request.user == post.author:
            return True
        return False



class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = Post
    success_url = '/'

    def test_func(self):
        post = self.get_object()
        if self.request.user == post.author:
            return True
        return False


def about(request):
    return render(request, 'blog/about.html', {'title': 'About'})

I understand that everything should be done in the PostDetailView class but I have no clue how to start. I will appreciate any ideas or suggestions

Upvotes: 2

Views: 205

Answers (1)

Daniel Hepper
Daniel Hepper

Reputation: 29977

You have a foreign key from Comment to Post, so if you have a Post object, you can get all related comments with post.comments.all().

You can do that directly in the template, you just have to omit the parenthesis. Here is a template snippet for illustration:

<div>
    <strong><h2>Comments Section</h2></strong>
    <ul>
    {% for comment in object.comments.all %}
        <li>{{ comment }} {{ comment.context }}</li>
    {% endfor %}
    </ul>
</div>

This kind of hides the database query to fetch the comments in the template. If you'd rather perform the queries in the view, you can overwrite PostDetailView.get_context_data():

class PostDetailView(DetailView): model = Post

def get_context_data(self, **kwargs):
    ctx = super().get_context_datat(**kwargs)
    ctx["comments"] = ctx["object"].comments.all()
    return ctx

In this case, the for-loop in the template would look like this:

{% for comment in object.comments.all %}

Upvotes: 2

Related Questions