django project case understanding, using advanced features to improve the blog program

Chapter II Contents:
Using django to send mail
Create forms and process them in views
Create form from model
Consolidate third-party applications
Building a complex QuerySet

2.1 sharing posts by email

For the mail sending function of a post, you need to do the following:

  1. Create user form, fill in name, email, recipient, optional note function.
  2. Create views in file, process data and send mail
  3. In the file of the blog application, add the url path for the new view.
  4. ’Create a template and display the form.

django's own base class: Form,ModelForm
Create in the blog directory

from django import forms

class EmailPostForm(forms.Form):
    name = forms.CharField(max_length=25)
    email = forms.EmailField()
    to = forms.EmailField()
    comments = forms.CharField(required=False, widget=forms.Textarea)

Configure mail parameters in setting:

# Send mail configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# smpt service address
EMAIL_PORT = 25   # Port default is 25, no need to modify
# The mailbox for sending mail needs to be configured to enable SMTP
# Client authorization password set in mailbox
# Sender seen by recipient
EMAIL_FROM = 'Signed<>'

To process a form in

def post_share(request, post_id):
    post = get_object_or_404(Post, id=post_id, status='published')
    sent = False
    if request.method == 'POST':
        form = EmailPostForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            post_url = request.build_absolute_uri(post.get_absolute_url())
            subject = '{} ({}) recommends you reading"{}"'.format(cd['name'], cd['email'], post.title)
            message = 'Read "{}" at {}\n\n{}\'s comments:{}'.format(post.title, post_url, cd['name'], cd['comments'])
            send_mail(subject, message, '', [cd['to']])
            sent = True
        form = EmailPostForm()
    return render(request, 'blog/post/share.html', {"post": post, "form": form, 'sent': sent})

Configure blog/

path('<int:post_id>/share/', views.post_share, name='post_share'),

Create a new file, share.html, in blog/templates/blog/post, and add the following code:

{% extends "blog/base.html" %}
{% block title %}share a post {% endblock %}
{% block content %}
{% if sent %}
<h1>E-mail successfully sent</h1>
    "{{ post.title }}" was successfully sent to {{ }}.
{% else %}
<h1>Share"{{ post.title }}"by e-mail</h1>
<form action="." method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send e-mail">
{% endif %}
{% endblock %}

Then run the program to send email for sharing. Note that the email address in setting and view should be the same.

I feel a little tired, but come on, we just started... keep trying.

2.2 build comment system

Step 1: create the model. Write the code in the file in the blog:

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE,
    name = models.CharField(max_length=80)
    email = models.EmailField()
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    active = models.BooleanField(default=True)

    class Meta:
        ordering = ('created',)

    def __str__(self):
        return 'Comment by {} on {}'.format(,

Run the command line for migration

python makemigrations blog
python migrate

To import the comment model in blog/

class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'email', 'post', 'created', 'active')
    list_filter = ('active', 'created', 'updated')
    search_fields = ('name', 'email', 'body')

Create the form in the model. Use ModelForm here:

from blog.models import Comment
class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')

Modify the post detail function in blog/

def post_detail(request, year, month, day, post):  # Get independent posts
    post = get_object_or_404(Post, slug=post,
    comments = post.comments.filter(active=True)
    new_comment = None
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            new_comment =
   = post
        comment_form = CommentForm()
    return render(request, 
                  {"post": post,
                    'comments': comments,
                    'new_comment': new_comment,
                    'comment_form': comment_form})

Modify post/detail.html

  1. Show all comments
  2. Show comment list
  3. Show users a form and add new comments.
{% extends "blog/base.html" %}
{% block title %}{{ post.title }} {% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class="date">
    Published {{ post.publish }} by {{ }}
{{ post.body|linebreaks }}
    <a href="{% url 'blog:post_share' %}">
    Share this post</a>
{% with comments.count as total_comments %}
    {{ total_comments}} comment{{ total_comments|pluralize }}
{% endwith %}
{% for comment in comments %}
<div class="comment">
    <p class="info">
        Comment{{ forloop.counter }} by {{ }}
        {{ comment.created }}
    {{ comment.body|linebreaks}}
{% empty %}
<p>There are no comments yet.</p>
{% endfor %}
{% if new_comment %}
<h2>Your comment has been added.</h2>
{% else %}
<h2>Add a new comment</h2>
<form action="." method="post">
    {{ comment_form.as_p }}
    {% csrf_token %}
    <p><input type="submit" value="Add comment"></p>
{% endif %}

{% endblock %}

Visit the website, you can see the following content, submit comments and test successfully.

2.3 add label function

The third-party plug-in Django taggit module is 0.22.2 in the book, and I installed 1.2.0.

pip install django_taggit==0.22.2

Registered in


Instantiate in the Post function in

class Post(models.Model):  # Inherit models.Model
    tags = TaggableManager()  # Add tags

Then database migration is a two-step operation.

python makemigrations blog
python migrate

Running projects, opening
Add labels. Tag each article.
In blog/post/list.html, add p tag under post title h2, and use comma join:

<p class="tags">Tags:{{ post.tags.all|join:"," }}</p>

Modify blog/ and use function view here. The complete code is as follows:

def post_list(request, tag_slug=None):  # Get post list
    object_list = Post.published.all()
    tag = None
    if tag_slug:
        tag = get_object_or_404(Tag, slug=tag_slug)
        object_list = object_list.filter(tags__in=[tag])

    paginator = Paginator(object_list, 3)  # Three per page
    page = request.GET.get("page")
        posts =
    except PageNotAnInteger:  # Not an integer, return to the first page
        posts =
    except EmptyPage:
        posts =
    return render(request, 'blog/post/list.html',
                  {'page': page, 'posts': posts, 'tag': tag})

Pay attention to modifying the url. I will post the whole blog/ at this time:

from django.urls import path
from . import views
urlpatterns = [
    path('', views.post_list, name='post_list'),
    # path('', views.PostListView.as_view(), name='post_list'),
    path('<int:post_id>/share/', views.post_share, name='post_share'),
    path('tag/<slug:tag_slug>/', views.post_list, name='post_list_by_tag'),

The two paths here point to the same view, but use different name s. The first one does not contain parameters. The second one uses the tag ﹣ slug parameter and the slug path converter.

Modify blog/post/list.html, and I will post all the codes:

{% extends "blog/base.html" %}
{% block title %}{% endblock %}
{% block content %}
<h1>My blog</h1>

{% if tag %}
<h2>Posts tagged with "{{ }}"</h2>
{% endif %}

{% for post in posts %}
    <a href="{{ post.get_absolute_url }}">
        {{ post.title }}
<!--Change how labels are displayed-->
<p class="tags">Tags:
    <!--{{ post.tags.all|join:"," }}-->
    {% for tag in post.tags.all %}
    <a href="{% url 'blog:post_list_by_tag' tag.slug %}">
        {{ }}
    {% if not forloop.last %}, {% endif %}
    {% endfor %}
    Published {{ post.publish }} by {{ }}
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
<!--{% include "pagination.html" with page=page_obj %}-->
{% endblock %}

Visit the link, test the view, and need to tag the article in admin in advance.
Click tag to access.

2.4 retrieve posts based on similarity.

Display similar posts by common tag number. In this way, when users visit a post, they can recommend reading related posts.

  1. Retrieve all tags for the current post,
  2. Get all posts with specific tags,
  3. Exclude the current post from the results list to avoid recommending the same post,
  4. Sort the results by the tag number of the current post,
  5. If there are two or more posts with the same tag number, the most recent post is recommended,
  6. Limit queries to the number of posts you want to recommend
    In view function, add:
from taggit.models import Tag
def post_detail(request, year, month, day, post):
	post_tags_ids = Post.tags.values_list('id', flat=True)
    similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(
    similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags', '-publish')[:4]

    return render(request,
                   'similar_posts': similar_posts})

In blog/post/detail.html, add:

<!--Add similar recommendation-->
<h2>Similar posts</h2>
{% for post in similar_posts %}
    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
{% empty %}
There are no similar posts yet.
{% endfor %}

Published 38 original articles, won praise 0, visited 541
Private letter follow

Keywords: Django Python pip Database

Added by harishkumar09 on Tue, 10 Mar 2020 04:44:45 +0200