BBS项目-表设计及创建
一个项目中最最最重要的不是业务逻辑的书写,而是前期的表设计,只要将表设计好了,后续的功能书写才会一帆风顺。
BBS项目参考分析博客园网站,提取功能,分析需要用到哪些表。
1 用户表
首页右上角模块分析,包含用户注册和登录,用户个人中心,设置等功能。因此需要建用户表。
我们要利用django框架的auth组件,并且需要扩展字段,如 avatar用户头像字段,因此需要自己建用户表,并继承AbstractUser。
2 个人站点表
通过点击文章下的头像框跳转到博主的主页
每个用户有一个博客主页,这个主页就是个人站点。我们点击多个主页,发现规律:url后输入用户名跳转到该用户主页。
其他表都围绕个人站点表展开,都会与之关联。
3 文章标签表
进入个人站点(博主主页)分析页面,一般由3部分构成,顶部的导航条,左侧是分类、标签、统计等导航条,右侧是文章或随笔。
一篇文章可以有多个标签。
4 文章分类表
一篇文章只能归于一类。
5 文章表
个人站点主题是一篇篇文章
6 点赞点踩表
每篇文章下记录了点赞点踩,用来记录哪个用户给哪篇文章点了赞还是点了踩。
7 文章评论表
每篇文章下可以发表评论,用来记录哪个用户给哪篇文章写了哪些评论内容。
8 表模型分析与创建
通过参考博客园网站模块,我们初步分析出需要上述7张表,接下来利用django ORM创建表模型,在创建过程中,先依次创建普通字段,然后分析表与表之间的关系,创建外键字段。
1、由于django自带的sqlite数据库对日期不敏感,所以我们换成MySQL。
setting.py
init.py
2、创建表模型:用户表,用auth模块,继承 AbstractUser
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class UserInfo(AbstractUser):
phone = models.BigIntegerField(verbose_name='电话号码', null=True)
avatar = models.FileField(verbose_name='用户头像', upload_to='avatar/', default='avatar/default.npg')
create_time = models.DateField(verbose_name='创建时间', auto_now_add=True)
blog = models.OneToOneField(to='Blog', null=True, on_delete=models.CASCADE)
在auth_user表基础上扩展字段,在项目根路径下创建一个avatar文件夹用来保存用户上传的头像文件, 给avatar字段传文件对象,该文件会自动存储到avatar文件夹下,然后avatar字段只保存文件路径,如 avatar/default.png(用户没有上传头像,用默认头像)。
blog外键字段,用户表与个人站点表一对一关系,为了创建表时方便,该字段可以为空,即在没有创建站点表的情况下,可以先创建用户。外键字段设置级联删除,即关联的主表(站点表)数据删除时,同步删除对应的从表(用户表)里的数据。
由于扩展替代了auth_user表,需要在settings.py中进行设置
AUTH_USER_MODEL = 'app01.UserInfo'
个人站点表
class Blog(models.Model):
site_name = models.CharField(verbose_name='站点名称', max_length=32) # url输入的用户名,用来跳转到用户站点
site_title = models.CharField(verbose_name='站点标题', max_length=32)
site_theme = models.CharField(verbose_name='站点样式',max_length=64) # 用来存css/js的文件路径,用户自定义站点样式保存到文件中
site_theme字段只做简单模拟,不实现该功能。
文章标签和文章分类表
class Tag(models.Model):
name = models.CharField(verbose_name='标签名称', max_length=32)
blog = models.ForeignKey(to='Blog', null=True, on_delete=models.CASCADE)
class Categorize(models.Model):
name = models.CharField(verbose_name='分类名称', max_length=32)
blog = models.ForeignKey(to='Blog', null=True, on_delete=models.CASCADE)
一个站点下可以建多个标签和分类,一个标签和分类只能对应一个站点(因为每个站点下建的是该用户唯一的标签和分类),因此标签和分类,与站点都是一对多关系,blog外键字段,为了创建表时方便,该字段可以为空,即在没有创建站点表的情况下,可以先创建标签和分类。外键字段设置级联删除,即关联的主表(站点表)数据删除时,同步删除对应的从表(标签表、分类表)里的数据。
文章表
class Article(models.Model):
title = models.CharField(verbose_name='文章标题', max_length=64)
desc = models.CharField(verbose_name='文章摘要', max_length=255)
content = models.TextField(verbose_name='文章内容')
create_time = models.DateField(verbose_name='发布日期', auto_now_add=True)
up_num = models.BigIntegerField(verbose_name='点赞数', default=0)
down_num = models.BigIntegerField(verbose_name='点踩数', default=0)
comment_num = models.BigIntegerField(verbose_name='评论数', default=0)
tags = models.ManyToManyField(to='Tag',
through='Article2Tag',
through_fields=('article', 'tag')
)
categorize = models.ForeignKey(to='Categorize', null=True, on_delete=models.SET_NULL)
blog = models.ForeignKey(to='Blog', null=True, on_delete=models.CASCADE)
class Article2Tag(models.Model):
article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
tage = models.ForeignKey(to='Tag', on_delete=models.CASCADE)
up_num、down_num、comment_num这三个字段是为了优化数据库设计的。因为文章需要展示这三个数据,每次查文章表,都需要跨表查询才能拿到数据,设计了这三个字段,避免频繁跨表操作。注意,在点赞和评论表创建数据时,同步更新这3个字段数据即可,并且初始默认值都为0。
tags外键字段,文章和标签多对多关系,我们用半自动方式,自己创建了第三张关系表,便于后续扩展字段。
categorize外键字段,文章和分类一对多关系(一篇文章只能归于一类)。在关联的主表(分类表)数据删除时,对应的从表(文章表)数据设置为空,这里用级联删除不合理,因为删除分类时会把对应的文章一起删除掉。
blog外键字段,文章和站点一对多,设置级联删除,同样,为了创建表时方便,该字段可以为空,即在没有创建站点表的情况下,可以先创建文章。
点赞点踩表
用来记录哪个用户给哪篇文章点了赞还是点了踩,类似于UserInfo2Article 第三方关系表,绑定的用户与文章的关系,还有一个字段存布尔值(存0/1),用来记录点赞还是点踩。
class UpAndDown(models.Model):
is_up = models.BooleanField()
user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
表结构如下:
id | user_id | article_id | is_up |
1 | 1 | 1 | 1 |
2 | 1 | 2 | 1 |
3 | 1 | 3 | 0 |
4 | 2 | 1 | 1 |
评论表
用来记录哪个用户给哪篇文章写了哪些评论内容,类似UserInfo2Article 第三方关系表,绑定用户与文章的关系,推导如点赞点踩表。
class Comment(models.Model):
content = models.CharField(verbose_name='评论内容', max_length=255)
comment_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True)
user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
parent = models.ForeignKey(to='self', null=True, on_delete=models.CASCADE)
根评论与子评论概念:根评论就是直接评论当前发布的内容的评论,子评论是评论别人的评论(回复),很明显,根评论与子评论是一对多的关系,外键字段建在多的一方,但是根评论和子评论用的是同一张表。
parent外键字段,自关联外键字段,to='Comment',ORM提供的自关联写法,to='self' 语义更明确。
表结构如下:
id | user_id | article_id | parent_id |
1 | 1 | 1 | |
2 | 2 | 1 | 1 |
外键字段相当于建在子评论,关联的是根评论的主键值,意味着,外键字段有值代表它是一条子评论。
如上面的表结构,id为1的评论是根评论,parent_id字段为空,记录的是id为1的用户与id为1的文章的绑定关系。
id为2的评论是一条子评论,parent_id=1,表示它关联的是id为1的根评论。
表创建完成后,最后执行两条数据库迁移命令。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人