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
form = PostForm(request.POST or None)
or
form.is_valid
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
- Text Content
- Post related to this comment
- Created Date
$ mkdir chapter4 && cd chapter4
$ django-admin.py startproject chap4 .
$ python manage.py startapp blog
# chap4/settings.py
INSTALLED_APPS = [
...
'blog.apps.BlogConfig',
]
# 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
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
- 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']
# 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'),
]
$ 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>
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