每天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/[email protected]/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/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

八、流程图

九、扩展功能建议

  1. 文章功能扩展

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

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

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

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

十、总结

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

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

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

相关推荐
workflower3 分钟前
使用谱聚类将相似度矩阵分为2类
人工智能·深度学习·算法·机器学习·设计模式·软件工程·软件需求
.生产的驴4 分钟前
Docker 部署Nexus仓库 搭建Maven私服仓库 公司内部仓库
java·运维·数据库·spring·docker·容器·maven
嘵奇6 分钟前
Spring Boot中HTTP连接池的配置与优化实践
spring boot·后端·http
知行026 分钟前
MySQL的Docker版本,部署在ubantu系统
数据库·mysql·docker
jndingxin7 分钟前
OpenCV CUDA 模块中在 GPU 上对图像或矩阵进行 翻转(镜像)操作的一个函数 flip()
人工智能·opencv
文人sec9 分钟前
接口自动化测试设计思路--设计实战
python·https·单元测试·自动化·pytest
朝新_17 分钟前
【MySQL】第三弹——表的CRUD进阶(一)数据库约束
数据库·mysql
囚生CY17 分钟前
【速写】TRL:Trainer的细节与思考(PPO/DPO+LoRA可行性)
人工智能
杨德兴19 分钟前
3.3 阶数的作用
人工智能·学习
子燕若水26 分钟前
Flask 调试的时候进入main函数两次
后端·python·flask