Skip to content

Chapter 13: Complete Blog System ​

13.2 Core Function Implementation ​

Article CRUD ​

Implement article create, read, update, delete functions:

python
# views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.contrib import messages
from .models import Article, Category, Tag
from .forms import ArticleForm

class ArticleListView(ListView):
    model = Article
    template_name = 'blog/article_list.html'
    context_object_name = 'articles'
    paginate_by = 10
    
    def get_queryset(self):
        return Article.objects.filter(status='published').select_related('author', 'category')
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['categories'] = Category.objects.all()
        context['tags'] = Tag.objects.all()
        return context

class ArticleDetailView(DetailView):
    model = Article
    template_name = 'blog/article_detail.html'
    context_object_name = 'article'
    
    def get_queryset(self):
        return Article.objects.select_related('author', 'category').prefetch_related('tags')
    
    def get_object(self, queryset=None):
        article = super().get_object(queryset)
        # Increase view count
        article.view_count += 1
        article.save(update_fields=['view_count'])
        return article
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Get related articles
        context['related_articles'] = Article.objects.filter(
            category=self.object.category,
            status='published'
        ).exclude(pk=self.object.pk)[:5]
        return context

class ArticleCreateView(LoginRequiredMixin, CreateView):
    model = Article
    form_class = ArticleForm
    template_name = 'blog/article_form.html'
    
    def form_valid(self, form):
        form.instance.author = self.request.user
        response = super().form_valid(form)
        messages.success(self.request, 'Article created successfully!')
        return response

class ArticleUpdateView(LoginRequiredMixin, UpdateView):
    model = Article
    form_class = ArticleForm
    template_name = 'blog/article_form.html'
    
    def get_queryset(self):
        # Users can only edit their own articles
        return Article.objects.filter(author=self.request.user)
    
    def form_valid(self, form):
        response = super().form_valid(form)
        messages.success(self.request, 'Article updated successfully!')
        return response

class ArticleDeleteView(LoginRequiredMixin, DeleteView):
    model = Article
    template_name = 'blog/article_confirm_delete.html'
    success_url = reverse_lazy('blog:article_list')
    
    def get_queryset(self):
        # Users can only delete their own articles
        return Article.objects.filter(author=self.request.user)
    
    def delete(self, request, *args, **kwargs):
        messages.success(self.request, 'Article deleted successfully!')
        return super().delete(request, *args, **kwargs)

# forms.py
from django import forms
from .models import Article, Category, Tag

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'category', 'tags', 'content', 'excerpt', 'featured_image', 'status']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'category': forms.Select(attrs={'class': 'form-control'}),
            'tags': forms.SelectMultiple(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 15}),
            'excerpt': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
            'featured_image': forms.FileInput(attrs={'class': 'form-control'}),
            'status': forms.Select(attrs={'class': 'form-control'}),
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['tags'].widget.attrs.update({'size': '10'})

Categories and Tags ​

Implement category and tag functions:

python
# views.py
class CategoryDetailView(ListView):
    model = Article
    template_name = 'blog/category_detail.html'
    context_object_name = 'articles'
    paginate_by = 10
    
    def get_queryset(self):
        self.category = get_object_or_404(Category, slug=self.kwargs['slug'])
        return Article.objects.filter(
            category=self.category,
            status='published'
        ).select_related('author', 'category')
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['category'] = self.category
        return context

class TagDetailView(ListView):
    model = Article
    template_name = 'blog/tag_detail.html'
    context_object_name = 'articles'
    paginate_by = 10
    
    def get_queryset(self):
        self.tag = get_object_or_404(Tag, slug=self.kwargs['slug'])
        return Article.objects.filter(
            tags=self.tag,
            status='published'
        ).select_related('author', 'category')
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['tag'] = self.tag
        return context

Comment System ​

Implement comment function:

python
# models.py
class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
    parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='replies')
    content = models.TextField()
    is_approved = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['created_at']
    
    def __str__(self):
        return f'{self.author.username}\'s comment on {self.article.title}'

# forms.py
class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']
        widgets = {
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 4, 'placeholder': 'Please enter your comment...'}),
        }

# views.py
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse

@login_required
def add_comment(request, article_id):
    article = get_object_or_404(Article, id=article_id)
    
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.article = article
            comment.author = request.user
            comment.save()
            messages.success(request, 'Comment posted successfully!')
            return redirect('blog:article_detail', slug=article.slug)
    
    return redirect('blog:article_detail', slug=article.slug)

class ArticleDetailView(DetailView):
    # ... other code ...
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Get comments
        context['comments'] = self.object.comments.filter(
            is_approved=True,
            parent=None
        ).select_related('author').prefetch_related('replies__author')
        context['comment_form'] = CommentForm()
        return context

Search Function ​

Implement article search function:

python
# views.py
from django.db.models import Q

class SearchView(ListView):
    model = Article
    template_name = 'blog/search_results.html'
    context_object_name = 'articles'
    paginate_by = 10
    
    def get_queryset(self):
        query = self.request.GET.get('q')
        if query:
            return Article.objects.filter(
                Q(title__icontains=query) |
                Q(content__icontains=query) |
                Q(excerpt__icontains=query)
            ).filter(status='published').select_related('author', 'category').distinct()
        return Article.objects.none()
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['query'] = self.request.GET.get('q', '')
        return context

Summary ​

Key points in implementing blog system core functions:

  1. ✅ Use Django's generic views to simplify CRUD operations
  2. ✅ Implement article category and tag display functions
  3. ✅ Build a complete comment system with reply support
  4. ✅ Develop efficient search functions
  5. ✅ Focus on user experience and data security

With the implementation of these core functions, the blog system already has basic usability.

Next Article ​

We will implement advanced features of the blog system.

13.3 Advanced Features →

Directory ​

Return to Course Directory

Released under the [BY-NC-ND License](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.en).