每天40分玩转Django:实操博客应用

实操博客应用

一、内容概述

模块 重要程度 主要内容
项目初始化 ⭐⭐⭐⭐ 创建项目和应用
模型设计 ⭐⭐⭐⭐⭐ 文章、评论、用户模型
视图实现 ⭐⭐⭐⭐⭐ 增删改查功能
模板开发 ⭐⭐⭐⭐ 页面布局和样式
用户认证 ⭐⭐⭐⭐⭐ 用户登录和权限

二、项目结构

plaintext 复制代码
blog_project/
├── blog/
│   ├── __init__.py
│   ├── models.py
│   ├── views.py
│   ├── urls.py
│   ├── forms.py
│   └── templates/
│       └── blog/
│           ├── base.html
│           ├── post_list.html
│           ├── post_detail.html
│           └── post_form.html
├── static/
│   └── css/
│       └── style.css
└── manage.py

三、模型设计

python 复制代码
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils import timezone

class Category(models.Model):
    name = models.CharField('分类名', max_length=100)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)

    class Meta:
        verbose_name = '分类'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', '草稿'),
        ('published', '发布'),
    ]
    
    title = models.CharField('标题', max_length=200)
    slug = models.SlugField('URL', max_length=200, unique_for_date='publish')
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='blog_posts',
        verbose_name='作者'
    )
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='posts',
        verbose_name='分类'
    )
    content = models.TextField('内容')
    publish = models.DateTimeField('发布时间', default=timezone.now)
    created = models.DateTimeField('创建时间', auto_now_add=True)
    updated = models.DateTimeField('更新时间', auto_now=True)
    status = models.CharField(
        '状态',
        max_length=10,
        choices=STATUS_CHOICES,
        default='draft'
    )
    views = models.PositiveIntegerField('浏览量', default=0)
    
    class Meta:
        ordering = ('-publish',)
        verbose_name = '文章'
        verbose_name_plural = verbose_name
        
    def __str__(self):
        return self.title
        
    def get_absolute_url(self):
        return reverse('blog:post_detail', args=[
            self.publish.year,
            self.publish.strftime('%m'),
            self.publish.strftime('%d'),
            self.slug
        ])

class Comment(models.Model):
    post = models.ForeignKey(
        Post,
        on_delete=models.CASCADE,
        related_name='comments',
        verbose_name='文章'
    )
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='comments',
        verbose_name='作者'
    )
    content = models.TextField('评论内容')
    created = models.DateTimeField('创建时间', auto_now_add=True)
    active = models.BooleanField('是否可见', default=True)
    parent = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='replies',
        verbose_name='父评论'
    )
    
    class Meta:
        ordering = ('created',)
        verbose_name = '评论'
        verbose_name_plural = verbose_name
        
    def __str__(self):
        return f'Comment by {self.author} on {self.post}'

四、表单设计

python 复制代码
# blog/forms.py
from django import forms
from .models import Post, Comment

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'slug', 'content', 'category', 'status']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'slug': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control'}),
            'category': forms.Select(attrs={'class': 'form-control'}),
            'status': forms.Select(attrs={'class': 'form-control'}),
        }

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']
        widgets = {
            'content': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 4,
                'placeholder': '请输入评论内容'
            }),
        }

五、视图实现

python 复制代码
# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.contrib import messages
from .models import Post, Comment, Category
from .forms import PostForm, CommentForm

def post_list(request):
    post_list = Post.objects.filter(status='published')
    paginator = Paginator(post_list, 10)
    page_number = request.GET.get('page')
    posts = paginator.get_page(page_number)
    categories = Category.objects.all()
    
    return render(request, 'blog/post_list.html', {
        'posts': posts,
        'categories': categories,
    })

def post_detail(request, year, month, day, slug):
    post = get_object_or_404(Post, 
        status='published',
        slug=slug,
        publish__year=year,
        publish__month=int(month),
        publish__day=int(day)
    )
    
    # 增加浏览量
    post.views += 1
    post.save()
    
    # 获取评论
    comments = post.comments.filter(active=True, parent=None)
    
    if request.method == 'POST':
        comment_form = CommentForm(request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.post = post
            new_comment.author = request.user
            new_comment.save()
            messages.success(request, '评论发表成功!')
            return redirect('blog:post_detail', year=year, 
                          month=month, day=day, slug=slug)
    else:
        comment_form = CommentForm()
    
    return render(request, 'blog/post_detail.html', {
        'post': post,
        'comments': comments,
        'comment_form': comment_form,
    })

@login_required
def post_create(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            messages.success(request, '文章创建成功!')
            return redirect(post.get_absolute_url())
    else:
        form = PostForm()
    return render(request, 'blog/post_form.html', {'form': form})

@login_required
def post_edit(request, post_id):
    post = get_object_or_404(Post, id=post_id, author=request.user)
    
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save()
            messages.success(request, '文章更新成功!')
            return redirect(post.get_absolute_url())
    else:
        form = PostForm(instance=post)
    return render(request, 'blog/post_form.html', {'form': form})

六、URL配置

python 复制代码
# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('<int:year>/<int:month>/<int:day>/<slug:slug>/',
         views.post_detail, name='post_detail'),
    path('create/', views.post_create, name='post_create'),
    path('edit/<int:post_id>/', views.post_edit, name='post_edit'),
]

七、模板设计

html 复制代码
<!-- blog/templates/blog/base.html -->
<!DOCTYPE html>
<html lang="zh-hans">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}博客{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{% url 'blog:post_list' %}">博客</a>
            <div class="collapse navbar-collapse">
                <ul class="navbar-nav ms-auto">
                    {% if user.is_authenticated %}
                        <li class="nav-item">
                            <a class="nav-link" href="{% url 'blog:post_create' %}">写文章</a>
                        </li>
                        <li class="nav-item">
                            <span class="nav-link">{{ user.username }}</span>
                        </li>
                    {% else %}
                        <li class="nav-item">
                            <a class="nav-link" href="{% url 'login' %}">登录</a>
                        </li>
                    {% endif %}
                </ul>
            </div>
        </div>
    </nav>

    <div class="container mt-4">
        {% if messages %}
            {% for message in messages %}
                <div class="alert alert-{{ message.tags }}">
                    {{ message }}
                </div>
            {% endfor %}
        {% endif %}

        {% block content %}
        {% endblock %}
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

八、流程图

九、扩展功能建议

  1. 文章功能扩展

    • 文章标签系统
    • 文章搜索功能
    • 文章归档功能
    • 相关文章推荐
  2. 用户功能扩展

    • 用户个人中心
    • 用户关注系统
    • 消息通知系统
  3. 评论功能扩展

    • 评论点赞功能
    • 评论回复通知
    • 评论审核系统
  4. 界面优化

    • 响应式设计
    • 富文本编辑器
    • 图片上传功能

十、总结

以上完成了一个基础的博客系统,包括:

  1. 文章的CRUD操作
  2. 评论系统的实现
  3. 用户认证和权限控制
  4. 分类系统的建立

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

相关推荐
萝卜白菜。20 分钟前
ClassCastException: oracle.sql.BLOB cannot be cast to oracle.sql.BLOB问题
数据库·oracle
czlczl2002092534 分钟前
Mysql的多版本快照MVCC机制与Mysql四种隔离级别
数据库·mysql
远离UE436 分钟前
GPU学习笔记
人工智能
有想法的py工程师37 分钟前
PostgreSQL 事务隔离级别详解(以及与MySQL实现差异)
数据库·mysql·postgresql
CNNACN电商经济39 分钟前
脑洞科技2025年报透露的“超维计算“或将引爆下一轮增长
人工智能
chuxinweihui42 分钟前
MySQL内外连接
数据库·mysql
yuhaiqiang44 分钟前
最强的 AI也许不是无所不知,但一定是最懂你的
人工智能
杨云龙UP2 小时前
ODA服务器RAC节点2/u01分区在线扩容操作记录及后续处理流程(Linux LVM + ext4 文件系统在线扩容操作手册)_20260307
linux·运维·服务器·数据库·ubuntu·centos
John Song3 小时前
Python创建虚拟环境的方式对比与区别?
开发语言·python
geovindu3 小时前
python: Bridge Pattern
python·设计模式·桥接模式