Django Builds a Personal Blog: Comment on the Blog

Keywords: Python Django Attribute emoji

In the age of no Internet, we use diaries to record our daily experience. When I was young, I had a diary with a lock. I was afraid that others would see what was written in it. The key was hidden in it.

Now the times have changed, the online version of the diary: blog, but the more people want to see the better.

After others have read your deep and good writing, they will inevitably want to talk about it. This is the function of "comment".

There are few new knowledge points in the comment module to be written in this chapter, but the comprehensive application of the content of the previous chapter.

It is strongly recommended that readers try to compile this part of the content and test their knowledge level.

Dead work

Comments are a relatively independent function, so create a new app for comments:

(env) E:\django_project\my_blog > ppython manage.py startapp comment

Some people find it strange that there is no comment without blog posts. Why do you say that comment is an "independent" function?

That's because not only blog posts can be commented on, but photos, videos and even the website itself can be "commented on". It can be encapsulated as a separate module to facilitate future expansion.

After confirming that the app was created successfully, remember to register in settings.py:

my_blog/settings.py

...
INSTALLED_APPS = [
    ...
    'comment',
]
...

TIME_ZONE = 'Asia/Shanghai'

...

Because we want to show the time of comment, modify the time zone to set TIME_ZONE as the time zone of Shanghai.

Then register the root route in my_blog/urls.py:

my_blog/urls.py

...
urlpatterns = [
    ...
    # comment
    path('comment/', include('comment.urls', namespace='comment')),
]
...

Writing Core Functions

Commentary Model

First, write a review model:

comment/models.py

from django.db import models
from django.contrib.auth.models import User
from article.models import ArticlePost

# Comments on Bowen
class Comment(models.Model):
    article = models.ForeignKey(
        ArticlePost,
        on_delete=models.CASCADE,
        related_name='comments'
    )
    user = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        related_name='comments'
    )
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('created',)

    def __str__(self):
        return self.body[:20]

There are two foreign keys in the model:

  • Article is a commented article
  • user is the publisher of comments

Don't forget that every time you add or modify a Model, you have to migrate data.

Tip: You must register an app in set.py before the data migration in this app can take effect.

A form of comments

Forms are used when users submit comments, so create a new form class:

comment/forms.py

from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['body']

Because the two foreign keys in the model will be filled in automatically through the view logic, it is enough to submit the body here.

Commentary url

Create a new routing file in comment app:

comment/urls.py

from django.urls import path
from . import views

app_name = 'comment'

urlpatterns = [
    # Comment
    path('post-comment/<int:article_id>/', views.post_comment, name='post_comment'),
]

Comments must be associated with a specific blog post, so the id of the blog post is passed in to facilitate subsequent calls.

The post_comment() view has not been written yet, so take a name first.

View of comments

The view functions of the comments are as follows:

comment/views.py

from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse

from article.models import ArticlePost
from .forms import CommentForm

# Article review
@login_required(login_url='/userprofile/login/')
def post_comment(request, article_id):
    article = get_object_or_404(ArticlePost, id=article_id)

    # Processing POST requests
    if request.method == 'POST':
        comment_form = CommentForm(request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.article = article
            new_comment.user = request.user
            new_comment.save()
            return redirect(article)
        else:
            return HttpResponse("The form is incorrect. Please fill it in again.")
    # Handling error requests
    else:
        return HttpResponse("Comments are accepted only POST Request.")

There are two new faces in the code.

get_object_or_404(): It basically has the same function as Model.objects.get(). The difference is that in a production environment, if a user requests a non-existent object, Model.objects.get() returns Error 500 (server internal error), while get_object_or_404 () returns Error 404. By contrast, returning 404 errors is more accurate.

redirect(): Return to an appropriate url: that is, after the user sends a comment, redirect to the article details page. When its parameter is a Model object, the get_absolute_url() method of the Model object is automatically called. So I'll modify the article model right away.

In fact, redirect() has been used in previous chapters. The functions are the same and the implementation is slightly different.

Model of article

As mentioned above, add the get_absolute_url() method to the article model:

article/models.py

...
# Remember to import
from django.urls import reverse

class ArticlePost(models.Model):
    ...

    # Get the article address
    def get_absolute_url(self):
        return reverse('article:article_detail', args=[self.id])

The reverse() method is used to return the url of the article details page, and the route redirection is realized.

Detailed View of Articles

The comment module needs to be displayed on the article details page, so the context of the comment module must also be passed to the template.

So modify article_detail() in article/views.py:

article/views.py

...

from comment.models import Comment

def article_detail(request, id):
    # Existing code
    article = ArticlePost.objects.get(id=id)

    # Remove article reviews
    comments = Comment.objects.filter(article=id)
    ...
    
    # Add comments context
    context = { 'article': article, 'toc': md.toc, 'comments': comments }

    ...

filter() can take out more than one satisfying object, but get() can only take out one, pay attention to distinguishing between use

Article Details Template

At the last step, stick to it. All the back-end functions have been written, so it's almost impossible to show them all on the page.

Modify the article details page:

templates/article/detail.html

...

<div class="col-9">
    ...
    <!-- Existing code, text of article -->
    <div class="col-12">
        ...
    </div>

    <!-- Comment -->
    <hr>
    {% if user.is_authenticated %}
        <div>
            <form 
                action="{% url 'comment:post_comment' article.id %}" 
                method="POST"
            >
            {% csrf_token %}
                <div class="form-group">
                    <label for="body">
                        <strong>
                            //I would also like to make a statement:
                        </strong>
                    </label>
                    <textarea 
                        type="text" 
                        class="form-control" 
                        id="body" 
                        name="body" 
                        rows="2"></textarea>
                </div>
                <!-- Submit button -->
                <button type="submit" class="btn btn-primary ">Send out</button>                    
            </form>
        </div>
        <br>
    {% else %}
        <br>
        <h5 class="row justify-content-center">
            //Please <a href="{% URL'userprofile: login'%}"> login </a> and reply
        </h5>
        <br>
    {% endif %}
    


    <!-- Display comments -->
    <h4>Share{{ comments.count }}Article comment</h4>
    <div>
        {% for comment in comments %}
            <hr>
            <p>
                <strong style="color: pink">
                    {{ comment.user }}
                </strong> to 
                <span style="color: green">
                    {{ comment.created|date:"Y-m-d H:i:s" }}
                </span> He said:
            </p>
            <pre style="font-family: inherit; font-size: 1em;">
{{ comment.body }}</pre>
        {% endfor %}
    </div>
</div>

<!-- Catalog -->
<div class="col-3 mt-4">
    ...
</div>

...
  • action in the form component specifies which url the data is submitted to
  • Displaying comments.count in comments is a built-in method in template objects that counts the elements contained
  • | date:"Y-m-d H:i:s": Pipeline characters are familiar to you and are used to "paste" certain attributes or functions to objects. Here is how to format the date. Please try to modify some of the characters to try the effect.
  • The key role of <pre> in defining pre-formatted text in our project is to preserve spaces and newline characters. This tag changes the font, size, etc. of the text, so it redefines the relevant content with the style attribute. Try to replace < pre > with div, and enter multiple lines of text to try the effect.

    Previously, it was said that it was better not to copy and paste code, otherwise you would not notice some "pits". For example, text in the <pre> tag must not be indented.

test

It's time for exciting testing.

Log in to your account and go to the details page of an article. You find that you can leave a message.

If logged out, the prompt is displayed:

Click login to go back to the login page.

The publishing and displaying functions of the comment module are completed.

Sweeping work

Data deletion, change function has been done many times, here do not intend to repeat.

Comments can also support Markdown grammar or insert Emoji emoticons.

Readers can realize their own functions of interest.

Some websites simply do not have the function of deleting and updating comments. Because for small stations, the number of times these functions are used is too small, it is better to focus on more valuable places. such as My blog No.

Other websites provide soft deletion, deletion is only not shown, in fact, the data still exist.

What you should do depends on your preferences.

summary

This chapter realizes the function of commenting and displaying comments. As I said at the beginning, the content of this chapter is a synthesis of the previous chapters.

If you don't read the code in this chapter, but complete the comment function on your own, congratulations on winning the title of "Django Initial Programmer"! Don't underestimate the word "entry". Everything is difficult at the beginning.

Please indicate the source for reprinting.

Posted by mr_griff on Fri, 10 May 2019 03:16:38 -0700