Arsalan Shahid

Extending Blog with Sharing and Commenting Features – DjangoBlog Part 2

My dear reader, how are you? السلام عليكم

I am not young enough to know everything — Oscar Wilde

This is the second part of DjangoBlog tutorial series. This post extends our basic application with a post sharing via email feature and a blog commenting system.


Few useful links to practically follow the project:

  1. DjangoBlog GitHub repository — DirectMe
  2. All other tutorials on Django-Blog — DirectMe
  3. Set your GitHub repo to DjangoBlog Part 1 using the following command:
git fetch origin 4dff851fa16284572b1bbc014b762b41830ec5dc

Sharing Blog posts via email

Django supports two types of forms, i.e.,

  1. Form: Standard forms
  2. ModelForm: Forms tied to the model instances

For post sharing we build define a standard form as shown below:

# create blog/forms.py and add the following program into it

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)

We will now use forms in views as shown below:

# open blog/views.py and add the following program

from .forms import EmailPostForm

def post_share(request, post_id):
    post = get_object_or_404(Post, id=post_id, status='published')
    if request.method == 'POST':
        form = EmailPostForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
        else:
            form = EmailPostForm()
            return render(request, 'blog/post/share.html', {'post': post,
                'form': form})

In order to send emails via Django, we first need to configure SMTP server by adding the following lines in config/settings.py

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'your_email@gmail.com'
EMAIL_HOST_PASSWORD = 'your_password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

We now add the emailing functionality to our views by extending the post_share as shown below:

# open views.py and add the program shown in bold

def post_share(request, post_id):
    ...
    ...
            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, 'arsalanadmin@blog.com',
 [cd['to']])
            sent = True
    else:
        form = EmailPostForm()
    return render(request, 'blog/post/share.html', {'post': post,
                                                    'form': form,
                                                    'sent': sent})

We now need to add a route to post share

# open blog/urls.py and add the following path 

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

Finally, we need to add templates for sharing the posts as shown below:

# Create blog/templates/blog/post/share.html and add the following program 

{% extends "blog/base.html" %}
{% block title %}Share a post{% endblock %}
{% block content %}
{% if sent %}
<h1>E-mail successfully sent</h1>
<p>
"{{ post.title }}" was successfully sent to {{ form.cleaned_data.to }}.
</p>
{% 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">
</form>
{% endif %}
{% endblock %}

# open blog/templates/blog/post/detail.html and add the following in bold

{% extends "blog/base.html" %}
...
...
{{ post.body|linebreaks }}
<p>
<a href="{% url "blog:post_share" post.id %}">
Share via email
</a>
</p>
{% endblock %}

A USER Commenting FEATURE on BLOG POSTS

We first will add a model for comment as shown below:

# Open blog/moodel.py and add the following

class Comment(models.Model):
    post = models.ForeignKey(Post,
               on_delete=models.CASCADE,
               related_name='comments')
    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(self.name, self.post)

# add the migrations

(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py makemigrations
(blogenv)Django-Blog-Application/Django-Blog$ python3 manage.py migrate

Let us add a Comment listing on the admin panel as shown below:

# open blog/admin.py and add a customized admin page listing for Comment

from django.contrib import admin
from .models import Post, Comment

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

We now create a form for Comment. This form should use the entities of Comment model so we can use ModelForm instead of the standard form as shown below:

# open blog/forms.py and add the following program

from .models import Comment

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

Let us add this functionality in detail.py template as shown below:

# open blog/templates/blog/post/detail.html and extend with the following program

{% with comments.count as total_comments %}
<h2>
{{ total_comments }} comment{{ total_comments|pluralize }}
</h2>
{% endwith %}

{% for comment in comments %}
<div class="comment">
<p class="info">
Comment {{ forloop.counter }} by {{ comment.name }}
{{ comment.created }}
</p>
{{ comment.body|linebreaks }}
</div>
{% 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>
</form>
{% endif %}

If you run the development server at this point in time you should be able to see the added functionalities at http://127.0.0.1:8000/blog/


I hope you find this tutorial useful. If you find any errors or feel any need for improvement, let me know in your comments below.

Signing off for today. Stay tuned and I will see you next week! Happy learning.

Exit mobile version