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).