BBS数据表表结构设计
在我们分析完一个项目的需求之后,我们着手开发项目所需要做的第一件事就是设计数据库和表结构及其字段。
一般来说,设计一个数据库的表一般要注意以下三点:
- 第一,把项目的需求转化为一个个数据库中的表
- 第二,探寻表与表之间的关联关系
- 第三,牢记以下原则:能用多对多关联关系就尽量不要用一对多关联关系,能用一对多关联关系就尽量别用一对一(原因是为了减少耦合)
一、用户表:UserInfo
| (通过继承AbstractUser类来扩写Auth_user) |
| •phone:用户的联系方式 |
| •bg_img: 用户的主页背景 |
| •province: 用户的省份 |
| •city: 用户的城市 |
| •gender : 用户的性别 |
| •avatar:用户的头像 |
| •blog:用户的博客站点(外键一对一关联博客表Blog) |
二、博客表:Blog
| •title:博客标题 |
| •subtitle: 博客子标题 |
| •style:博客样式 |
三、文章表:Article
| •title:文章标题 |
| •head_img: 头像 |
| •description:文章摘要 |
| •content:文章内容 |
| •create_time:文章的创建时间 |
| •modify_time: 文章的修改时间 |
| •up_num :点赞数 |
| •down_num:点踩数 |
| •comment_num:评论数 |
| •blog:属于哪个博客站点(外键关联博客表Blog) |
| •category:属于哪个分类(外键关联分类表Category) |
四、标签表:Tag
| •name:标签名 |
| •blog:属于哪个博客站点(外键关联博客表Blog) |
| |
五、分类表:Category
| •name:分类名 |
| •blog:属于哪个博客站点(外键关联博客表Blog) |
| |
| •user:评论的用户(外键关联用户表UserInfo) |
| •article:该评论属于哪篇文章(外键关联文章表Article) |
| •content:评论内容 |
| •comment_time: 评论的创建时间 |
| •comment_id:评论的目标id(外键进行自关联) |
七、点赞点踩表:UpAndDown
| •user:来自哪个用户(外键关联用户表UserInfo) |
| •article:属于哪篇文章(外键关联文章表Article) |
| •is_up:点赞还是点踩(根据bool值来判断) |
| •create_time : 点赞或踩的时间 |
八、文章标签表: Tag2Article
| 根据文章与标签的多对多关系手动建立的第三张表 |
| •tag:标签名(外键关联标签表Tag) |
| •article:属于哪篇文章(外键关联文章表Article) |
九、轮播图表 Swiper (拓展)
| •image: 轮播图图片名 |
| •title:轮播图标题 |
| •img_url: 点击轮播图要跳转的url地址 |
十、日志表: Log(拓展)
| •id: 日志id |
| •ip: 访问的ip地址 |
| •time: 访问的时间 |
| •url: 访问的url |
| •device: 访问的浏览器 |
| •platform:访问的操作系统类型 |
十一、创建BBS表模型
我们要在app中的模型层models.py中写入以下orm语句模型来创建表模型
| from django.contrib.auth.models import AbstractUser |
| from django.utils.html import mark_safe |
| from django.db import models |
| from markdown import markdown |
| |
| |
| |
| class Log(models.Model): |
| id = models.AutoField(primary_key=True) |
| ip = models.CharField(max_length=64, verbose_name='访问IP', help_text='访问用户的IP地址') |
| time = models.DateTimeField(auto_now_add=True, verbose_name='访问时间', help_text='该用户的访问时刻') |
| url = models.CharField(max_length=64, verbose_name='访问的URL', help_text='该用户访问的URL地址') |
| device = models.CharField(max_length=256, null=True, verbose_name='访问的浏览器', help_text='该用户是用什么浏览器访问的') |
| platform = models.CharField(max_length=256, null=True, verbose_name='访问的系统', help_text='该用户用的是什么操作系统') |
| |
| def __str__(self): |
| return self.ip |
| |
| class Meta: |
| ordering = ['id'] |
| verbose_name_plural = '日志' |
| |
| |
| |
| class UserInfo(AbstractUser): |
| avatar = models.FileField(upload_to='avatar/', default='avatar/default.png', verbose_name='头像', help_text='该用户的头像') |
| bg_img = models.FileField(upload_to='bg_img/', default='bg_img/default_bg.png', verbose_name='头像', |
| help_text='该用户的主页背景') |
| province = models.CharField(max_length=32, default='', verbose_name='省', help_text='该用户的省') |
| city = models.CharField(max_length=32, default='', verbose_name='城市', help_text='该用户的市') |
| gender = models.IntegerField(choices=((0, '保密'), (1, '男'), (2, '女')), default=0, verbose_name='性别', |
| help_text='该用户的性别') |
| phone = models.CharField(max_length=11, null=True, default='', verbose_name='联系方式', help_text='该用户的联系方式') |
| blog = models.OneToOneField(to='Blog', on_delete=models.CASCADE, null=True, verbose_name='博客', help_text='该用户的博客') |
| |
| def __str__(self): |
| return self.username |
| |
| class Meta: |
| verbose_name_plural = '用户' |
| |
| |
| |
| class Blog(models.Model): |
| title = models.CharField(max_length=32, verbose_name='博主昵称', help_text='博主昵称') |
| subtitle = models.CharField(max_length=32, verbose_name='子标题/公告', help_text='博客的子标题/公告') |
| style = models.CharField(max_length=32, verbose_name='样式', help_text='该博客独有的样式') |
| |
| def __str__(self): |
| return self.title |
| |
| class Meta: |
| verbose_name_plural = '博客站点' |
| |
| |
| |
| class Article(models.Model): |
| title = models.CharField(max_length=32, verbose_name='标题', help_text='文章的标题') |
| head_img = models.FileField(upload_to='article_head_img/', default='article_head_img/default_head.png', |
| verbose_name='头图', |
| help_text='文章的头图') |
| description = models.CharField(max_length=128, verbose_name='摘要', help_text='简要描述该文章') |
| content = models.TextField(verbose_name='内容', help_text='文章的内容') |
| markdown = models.TextField(verbose_name='Markdown内容', default='暂无', help_text='文章的Markdown内容') |
| create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='该文章的创建时间') |
| modify_time = models.DateTimeField(auto_now=True, verbose_name='修改时间', help_text='该文章的最后修改时间') |
| up_num = models.IntegerField(default=0, verbose_name='点赞数', help_text='该文章的点赞数') |
| down_num = models.IntegerField(default=0, verbose_name='点踩数', help_text='该文章的点踩数') |
| comment_num = models.IntegerField(default=0, verbose_name='评论数', help_text='该文章的评论数') |
| blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE, null=True, blank=True, verbose_name='博客', |
| help_text='该文章属于哪个博客页面') |
| category = models.ForeignKey(to='Category', on_delete=models.CASCADE, null=True, blank=True, verbose_name='分类', |
| help_text='该文章属于哪个分类') |
| tag = models.ManyToManyField(to='Tag', through='Tag2Article', |
| through_fields=('article', 'tag'), verbose_name='标签', |
| help_text='该文章有哪些标签') |
| |
| def get_text_md(self): |
| return mark_safe(markdown(self.content)) |
| |
| def __str__(self): |
| return self.title |
| |
| class Meta: |
| verbose_name_plural = '文章' |
| ordering = ['id', ] |
| |
| |
| |
| class Tag(models.Model): |
| name = models.CharField(max_length=32, verbose_name='标签', help_text='标签的名字') |
| blog = models.ForeignKey(to='Blog', on_delete=models.DO_NOTHING, null=True, blank=True, verbose_name='博客', |
| help_text='该标签属于哪个博客页面') |
| |
| def __str__(self): |
| return self.name |
| |
| class Meta: |
| verbose_name_plural = '标签' |
| |
| |
| |
| class Category(models.Model): |
| name = models.CharField(max_length=32, verbose_name='分类', help_text='分类的名称') |
| blog = models.ForeignKey(to='Blog', on_delete=models.DO_NOTHING, null=True, blank=True, verbose_name='博客', |
| help_text='该分类属于哪个博客页面') |
| |
| def __str__(self): |
| return self.name |
| |
| class Meta: |
| verbose_name_plural = '分类' |
| |
| |
| |
| class Comment(models.Model): |
| user = models.ForeignKey(to='UserInfo', on_delete=models.DO_NOTHING, verbose_name='用户', help_text='该评论来自哪个用户') |
| article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True, verbose_name='文章', |
| help_text='评论的对象是哪篇文章') |
| content = models.CharField(max_length=256, verbose_name='内容', help_text='评论的内容') |
| comment_time = models.DateTimeField(auto_now_add=True, verbose_name='时间', help_text='评论的时间') |
| comment_id = models.ForeignKey(to='self', on_delete=models.CASCADE, null=True, verbose_name='评论id', |
| help_text='对哪个id的评论进行评论') |
| |
| def __str__(self): |
| return self.content |
| |
| class Meta: |
| verbose_name_plural = '评论' |
| |
| |
| |
| class UpAndDown(models.Model): |
| user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, verbose_name='用户', help_text='来自哪个用户') |
| article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True, verbose_name='文章', |
| help_text='针对哪篇文章') |
| is_up = models.BooleanField(null=True, verbose_name='点赞点踩', help_text='True为点赞,False为点踩') |
| create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='点赞点踩的时间') |
| |
| def __str__(self): |
| return self.user |
| |
| class Meta: |
| verbose_name_plural = '点赞点踩' |
| |
| |
| |
| class Tag2Article(models.Model): |
| tag = models.ForeignKey(to='Tag', on_delete=models.SET_DEFAULT, default='', verbose_name='标签', help_text='关联的标签') |
| article = models.ForeignKey(to='Article', on_delete=models.CASCADE, default='', verbose_name='文章', |
| help_text='关联的文章') |
| |
| class Meta: |
| verbose_name_plural = '标签关联文章' |
| |
| |
| |
| class Swiper(models.Model): |
| image = models.FileField(upload_to='swiper_img/', default='swiper_img/default.jpg', verbose_name='图片', |
| help_text='轮播图的图片') |
| title = models.CharField(max_length=32, verbose_name='标题', help_text='图片的标题') |
| img_url = models.CharField(max_length=64, verbose_name='URL', help_text='点击图片要跳转的URL地址') |
| |
| def __str__(self): |
| return self.img_url |
| |
| class Meta: |
| verbose_name_plural = '轮播图' |
开发BBS所用的,Django1版本如下
表设计
| from django.db import models |
| |
| |
| """ |
| 先写普通字段 |
| 之后再写外键字段 |
| """ |
| from django.contrib.auth.models import AbstractUser |
| |
| |
| class UserInfo(AbstractUser): |
| phone = models.BigIntegerField(verbose_name='手机号',null=True) |
| |
| avatar = models.FileField(upload_to='avatar/',default='avatar/default.png',verbose_name='用户头像') |
| """ |
| 给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png |
| """ |
| create_time = models.DateField(auto_now_add=True) |
| |
| blog = models.OneToOneField(to='Blog',null=True) |
| |
| |
| class Blog(models.Model): |
| site_name = models.CharField(verbose_name='站点名称',max_length=32) |
| site_title = models.CharField(verbose_name='站点标题',max_length=32) |
| |
| site_theme = models.CharField(verbose_name='站点样式',max_length=64) |
| |
| |
| class Category(models.Model): |
| name = models.CharField(verbose_name='文章分类',max_length=32) |
| blog = models.ForeignKey(to='Blog',null=True) |
| |
| |
| class Tag(models.Model): |
| name = models.CharField(verbose_name='文章标签',max_length=32) |
| blog = models.ForeignKey(to='Blog', null=True) |
| |
| |
| 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(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) |
| |
| |
| blog = models.ForeignKey(to='Blog', null=True) |
| category = models.ForeignKey(to='Category',null=True) |
| tags = models.ManyToManyField(to='Tag', |
| through='Article2Tag', |
| through_fields=('article','tag') |
| ) |
| |
| |
| class Article2Tag(models.Model): |
| article = models.ForeignKey(to='Article') |
| tag = models.ForeignKey(to='Tag') |
| |
| |
| class UpAndDown(models.Model): |
| user = models.ForeignKey(to='UserInfo') |
| article = models.ForeignKey(to='Article') |
| is_up = models.BooleanField() |
| |
| |
| class Comment(models.Model): |
| user = models.ForeignKey(to='UserInfo') |
| article = models.ForeignKey(to='Article') |
| content = models.CharField(verbose_name='评论内容',max_length=255) |
| comment_time = models.DateTimeField(verbose_name='评论时间',auto_now_add=True) |
| |
| parent = models.ForeignKey(to='self',null=True) |
开发BBS所用的,Django2版本如下
主要针对on_delete字段建立外键关系时,需要加上。而Django1不需要
| |
| from django.db import models |
| |
| |
| """ |
| 先写普通字段 |
| 之后再写外键字段 |
| """ |
| from django.contrib.auth.models import AbstractUser |
| |
| |
| class UserInfo(AbstractUser): |
| phone = models.BigIntegerField(verbose_name='手机号', null=True) |
| |
| avatar = models.FileField(upload_to='avatar/', default='avatar/default.png', verbose_name='用户头像') |
| """ |
| 给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png |
| """ |
| create_time = models.DateField(auto_now_add=True) |
| |
| blog = models.OneToOneField(to='Blog', null=True,on_delete=models.CASCADE) |
| |
| |
| class Blog(models.Model): |
| site_name = models.CharField(verbose_name='站点名称', max_length=32) |
| site_title = models.CharField(verbose_name='站点标题', max_length=32) |
| |
| site_theme = models.CharField(verbose_name='站点样式', max_length=64) |
| |
| |
| class Category(models.Model): |
| name = models.CharField(verbose_name='文章分类', max_length=32) |
| blog = models.ForeignKey(to='Blog', null=True,on_delete=models.CASCADE) |
| |
| |
| class Tag(models.Model): |
| name = models.CharField(verbose_name='文章标签', max_length=32) |
| blog = models.ForeignKey(to='Blog', null=True,on_delete=models.CASCADE) |
| |
| |
| 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(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) |
| |
| |
| blog = models.ForeignKey(to='Blog', null=True,on_delete=models.CASCADE) |
| category = models.ForeignKey(to='Category', null=True,on_delete=models.CASCADE) |
| tags = models.ManyToManyField(to='Tag', |
| through='Article2Tag', |
| through_fields=('article', 'tag') |
| ) |
| |
| |
| class Article2Tag(models.Model): |
| article = models.ForeignKey(to='Article',on_delete=models.CASCADE) |
| tag = models.ForeignKey(to='Tag',on_delete=models.CASCADE) |
| |
| |
| class UpAndDown(models.Model): |
| user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE) |
| article = models.ForeignKey(to='Article',on_delete=models.CASCADE) |
| is_up = models.BooleanField() |
| |
| |
| class Comment(models.Model): |
| user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE) |
| article = models.ForeignKey(to='Article',on_delete=models.CASCADE) |
| content = models.CharField(verbose_name='评论内容', max_length=255) |
| comment_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True) |
| |
| parent = models.ForeignKey(to='self', null=True,on_delete=models.CASCADE) |
十二、改配置文件
由于衍生了auth_user表中的字段,所以要在我们自定义表迁移之前,在配置文件settings中加上
一定要在配置文件中声明替换关系
| AUTH_USER_MODEL = 'app01.UserInfo' |
十三、数据库配置以及迁移
在这个项目中,我们使用pycharm自带的sqlite3,因此无需额外在设置中配置数据库,但是每当我们对models.py中的内容进行修改时,仍要进行数据库迁移
在pycharm左下角的终端terminal中输入以下两条命令完成迁移
生成迁移文件
| python3 manage.py makemigrations |
数据库迁移
| python3 manage.py migrate |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步