Skip to content

Instantly share code, notes, and snippets.

@eclecticmiraclecat
Created June 13, 2021 14:14
Show Gist options
  • Save eclecticmiraclecat/7ed5b3f02bc58138a982ea7a9af57fbc to your computer and use it in GitHub Desktop.
Save eclecticmiraclecat/7ed5b3f02bc58138a982ea7a9af57fbc to your computer and use it in GitHub Desktop.

Chapter 4. Building a Blog

Model Forms

class PostForm(ModelForm):
    class Meta:
        model = Post
  • modify the text fiels
class PostForm(ModelForm):
    text = forms.CharField(widget=forms.TextArea, label='Entry')
    class Meta:
        model = Post

Check if a form is submitted

form = PostForm(request.POST or None)

or

form.is_valid

Our blog app:

Let’s list out the features we would want to see in our blog:

  • Create/Edit blog post (restricted to admin)
  • View blog post (public)
  • Comment on a blog post (anonymous)
  • Store anonymous user details in session
  • Show month based blog archives
  • Generate RSS feeds

We have two models here: Post and Comment. The data we would like store are:

For Post:

  • Title
  • Text Content
  • Slug
  • Created Date
  • Author

For Comment:

  • Name
  • Website
  • Email
  • Text Content
  • Post related to this comment
  • Created Date

Starting a django project

$ mkdir chapter4 && cd chapter4
$ django-admin.py startproject chap4 .

Create app

$ python manage.py startapp blog

Add app to settings.py

# chap4/settings.py

INSTALLED_APPS = [
    ...
    'blog.apps.BlogConfig',
]

Models

# blog/models.py

from django.db import models
from django.urls import reverse
from django.template.defaultfilters import slugify

from django.contrib.auth.models import User

class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    text = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog_post_detail', kwargs={'slug' :self.slug})

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super(Post, self).save(*args, **kwargs)

class Comment(models.Model):
    name = models.CharField(max_length=42)
    email = models.EmailField(max_length=75)
    website = models.URLField(max_length=200, null=True, blank=True)
    text = models.TextField()
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.text

Views

The views we would need are:

  • Admin should be able to login
  • Add/Edit a post - restricted to admin
  • View a blog post
  • Comment on a blog post
# blog/views.py

from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import redirect, render_to_response, get_object_or_404, render
from django.views.generic.dates import MonthArchiveView, WeekArchiveView

from .models import Post
from .forms import PostForm, CommentForm

@user_passes_test(lambda u: u.is_superuser)
def add_post(request):
    form = PostForm(request.POST or None)
    if form.is_valid():
        post = form.save(commit=False)
        post.author = request.user
        post.save()
        return redirect(post)
    return render(request, 'blog/add_post.html',{ 'form': form })

def view_post(request, slug):
    post = get_object_or_404(Post, slug=slug)
    form = CommentForm(request.POST or None)
    if form.is_valid():
        comment = form.save(commit=False)
        comment.post = post
        comment.save()
        request.session["name"] = comment.name
        request.session["email"] = comment.email
        request.session["website"] = comment.website
        return redirect(request.path)
    form.initial['name'] = request.session.get('name')
    form.initial['email'] = request.session.get('email')
    form.initial['website'] = request.session.get('website')
    return render(request, 'blog/blog_post.html',{'post': post,'form': form,})

class PostMonthArchiveView(MonthArchiveView):
    queryset = Post.objects.all()
    date_field = "created_on"
    allow_future = True

class PostWeekArchiveView(WeekArchiveView):
    queryset = Post.objects.all()
    date_field = "created_on"
    week_format = "%W"
    allow_future = True

Model Forms

  • customize our forms to only display fields which need user input
# blog/forms.py

from django import forms

from .models import Post, Comment

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        exclude = ['author', 'slug']

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        exclude = ['post']

URLs

# chap4/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('accounts/', include('django.contrib.auth.urls')),
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')),
    ]
# blog/urls.py

from django.urls import path, include
from .views import view_post, add_post, PostMonthArchiveView, PostWeekArchiveView


urlpatterns = [
    path('post/<str:slug>', view_post, name='blog_post_detail'),
    path('add/post', add_post, name='blog_add_post'),
    path('archive/<int:year>/month/<int:month>', PostMonthArchiveView.as_view(month_format='%m'), name='blog_archive_month',),
    path('archive/<int:year>/week/<int:week>', PostWeekArchiveView.as_view(), name='blog_archive_week'),
        ]

Templates

$ mkdir -p blog/templates/blog
$ mkdir -p blog/templates/registration
<!-- templates/registration/login.html -->
Log In
<br>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Log In</button>
</form>
<!-- blog/templates/blog/add_post.html -->

<h2>Hello {{ user.username }}</h2>
<br />
<h2>Add new post</h2>
<form action="" method="POST">
    {% csrf_token %}
    <table>
        {{ form.as_table }}
    </table>
    <input type="submit" name="add" value="Add" />
</form>
<!-- blog/templates/blog/blog_post.html -->
<h2>{{ post.title }}</h2>
<div class="content">
    <p>
        {{ post.text }}
    </p>
    <span>
        Written by {{ post.author }}  on {{ post.created_on }}
    </span>
</div>

{% if post.comment_set.all %}
<h2>Comments</h2>
<div class="comments">
    {% for comment in post.comment_set.all %}
        <span>
            <a href="{{ comment.website }}">{{ comment.name }}</a> said on {{ comment.created_on }}
        </span>
        <p>
            {{ comment.text }}
        </p>
    {% endfor %}
</div>
{% endif %}

<br />

<h2>Add Comment</h2>

<form action="" method="POST">
    {% csrf_token %}
    <table>
        {{ form.as_table }}
    </table>
    <input type="submit" name="submit" value="Submit" />
</form>

Links

http://127.0.0.1:8000/blog/archive/2021/month/06
http://127.0.0.1:8000/blog/add/post
http://127.0.0.1:8000/blog/post/title
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment