项目流程:
1 搞清楚需求(产品经理)
(1) 基于用户认证组件和Ajax实现登录验证(图片验证码)
1. 一次请求伴随多次请求
2. PIL [https://pillow.readthedocs.io/en/stable/]
3. session存储
4. 验证码刷新
(2) 基于Ajax和forms组件实现注册功能(表项检查,是否符合密码长度等规则)
(3) 设计系统首页,完成文章列表渲染
(4) 设计个人站点
(5) 文章详情页(点赞)
(6) 文章的评论---对评论的评论
(7) 后台管理页面(富文本编辑框,防止XSS攻击)
2 设计表结构
1. 需要一张用户表【状态内容访问隔离---cookies seesion---用户认证组件【用户表必须使用auth_user用户表(扩展字段)】】
使用方式:用户表继承AbstractUser(user继承了它)
2. 表关系
1. 分类与站点表
1. 站点表和用户个人信息表为一对一的关系【可以通过任意一方查找另外一方,需要查找资料,一对一关系有哪些限制代码】
2. 分类与用户表的关系,一个用户可以创建多个分类,一个分类只能属于一个人
3. 分类与站点的关系:站点可以有多个分类,一个分类只能属于一个站点,站点和用户是一对于的关系,那么用户和分类的关系等价于站点与分类的关系,一对于的继承,引入跨表查询,站点可以通过用户查找分类
4. 分类的多,绑定给博客表而非用户表【查询逻辑:用户表--->博客--->分类表】
2. 分类与标签表
1. 一个用户可以创建多个标签,一个标签只能属于一个用户【将标签理解为斜杠青年,一个年轻有为的人的多重身份】
2. 文章与分类是多对多或者一对多,博客园默认多对多的关系,标签与文章是多对多的关系,分类与文章构成一对一的关系
3. 标签和分类可以自行创建
4. 一个分类可以对应多个文章,一篇文章只能属于一个分类
5. 文章的关键词就是标签,一个文章可以有多个标签,一个标签可以属于多篇文章[多对多]
3. 评论表
1. 分清根评论与(对文章的评论)子评论(针对评论的评论 )
4. 分类表和用户表的关系
1. 一个人可以创建多个分类,一个分类只能属于一个人[一对多]
5. 站点与用户表的关系
1. 一个站点对应一个用户,一个用户也只能拥有一个站点[一对一,其他表与用户表绑定的关系可以等同于它与站点表的关系]
6. 用户与分类表的关系
1. 一个用户对应多个分类表,一个分类表只能属于一个用户[一对多的关系]
7. 标签与站点表
1. 一个站点可以拥有多个标签,但一个标签只能属于一个站点[一对多的关系]
8. 文章表与分类表
1. 一个分类下面可以有多篇文章,但一篇文章只能属于一个分类[一对多]
2. 标签和文章是多对多的关系,为了区分,让文章和分类保持一对多的关系
9. 用户表与文章表的关系
1. 一个用户可以发布多篇文章,但一篇文章只能属于一个用户[一对多]
3. 表功能设计
1. 分类与标签表
1. 一个分类对应多篇文章,标签就是文章的关键词
2. 一个文章可以多个标签,一个标签可以对应多个文章
3. 标签与站点绑定一对多的关系
2. 文章表
1. Tag使用了中间模型概念,在tag与article之间使用article2构建关联
2. 文章表与用户绑定多对一的关系
3. 一个文章表包含那些信息?标题,摘要,作者,创建时间
3. 点赞表
1. 存储点赞信息,哪一个用户对那一篇文章点赞或者踩灭
2. user, article , bool判断点赞或者踩灭
4. 评论表
1. 哪一个用户,对那一篇文章,在什么时候,做了什么评论?
2. 添加字段描述父子关系,Django默认为外键设置自增减ID
3. 为了避免跨表查询,每生成一个评论,做一个自加一
4. 查询业务需求大于存储和修改,所以用空间换时间
5. 1219 --- 07 0
4. 查询逻辑
1. user表与blog表为一对一关系,可以通过user找到blog,通过blog与category的一对多关系,再找到分类表,总得来说,blog与category的关系可以迁移到category与user的关系
2.
表结构的二次理解
1. 用户表的功能:登录验证和用户注册
2. 区分登录和未登录状态:cookie和session
3. Django系统表关系: User对应的是
站点表的修改
from django.db import models
from ckeditor.fields import RichTextField
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
"""用户信息"""
nid = models.AutoField(primary_key=True)
telephone = models.CharField(max_length=11, null=True, unique=True)
# 存储用户头像文件
avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")
# 在生成字段时就以当前时间存储
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
# 业务分离,用户名下blog删除,则站点blog不能被删除
blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.PROTECT,
related_name="UserInfo_blog")
def __str__(self):
return self.username
class Blog(models.Model):
"""
博客信息表---站点表
1. 直接继承自UserInfo的nid,同时拒绝删除外键,设置自增减
"""
nid = models.OneToOneField(UserInfo, on_delete=models.PROTECT, parent_link=True, primary_key=True, related_name='Blog_nid')
title = models.CharField(verbose_name='个人博客标题', max_length=64)
site_name = models.CharField(verbose_name='站点名称', max_length=64)
theme = models.CharField(verbose_name='博客主题', max_length=32)
def __str__(self):
return self.title
class Category(models.Model):
"""博主个人文章分类表"""
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='分类标题', max_length=32)
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE,
related_name='Category_blog', db_column='blog')
def __str__(self):
return self.title
class Tag(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='标签名称', max_length=32)
# on_delete=models.PROTECT 标签被删除,外键关联的blog不能被删除
# to_field='nid' 关联对象的字段名称,该字段必须是unique=True
# related_name 指定用于反向查询的对象, 如果两个对象继承自同一个类,重命名之后则不再因为反向查询而冲突
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.PROTECT,
related_name='Article', db_column='blog')
def __str__(self):
return self.title
class Article(models.Model):
"""
文章表
"""
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=50, verbose_name='文章标题')
desc = models.CharField(max_length=255, verbose_name='文章描述')
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
comment_count = models.IntegerField(default=0)
up_count = models.IntegerField(default=0)
down_count = models.IntegerField(default=0)
user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE,
related_name='Article_user', db_column='user')
category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE,
related_name='Article_category', db_column='category')
tags = models.ManyToManyField(
to="Tag", # to 参数用来指定与当前model类关联的model类
through='Article2Tag',
through_fields=('article', 'tag')
)
content = models.TextField()
class Meta:
db_table = "文章表"
def __str__(self):
return self.title
class Article2Tag(models.Model):
"""
文章表与标签表的中间表
仅仅添加了一个新的ID,用于给两个表内容进行定位
一旦文章表或者标签表删除,则与他们关联的ID则删除,称为级联删除
"""
nid = models.AutoField(primary_key=True)
# 标签和文章的关联,属于多对多关系,标签可以删除,表不能删除,所以外键关联
article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE)
tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE)
class Meta:
unique_together = [
('article', 'tag'),
]
def __str__(self):
v = self.article.title + "---" + self.tag.title
return v
class ArticleUpDown(models.Model):
"""
点赞表
如果点赞被删除,那么用户表和文件表不会被删除
"""
nid = models.AutoField(primary_key=True)
user = models.ForeignKey('UserInfo', null=True, on_delete=models.PROTECT)
article = models.ForeignKey("Article", null=True, on_delete=models.PROTECT)
is_up = models.BooleanField(default=True)
class Meta:
unique_together = [
('article', 'user')
]
class Comment(models.Model):
"""评论表"""
nid = models.AutoField(primary_key=True)
user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.PROTECT)
article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.PROTECT)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
# 富文本编辑器 ckeditor
body = RichTextField()
content = models.CharField(verbose_name='评论内容', max_length=255)
parent_comment = models.ForeignKey('self', null=True, on_delete=models.PROTECT)
def __str__(self):
return self.content
修改版
from django.db import models
from ckeditor.fields import RichTextField
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
"""用户信息"""
nid = models.AutoField(primary_key=True)
telephone = models.CharField(max_length=11, null=True, unique=True)
# 存储用户头像文件
avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")
# 在生成字段时就以当前时间存储,用于计算园龄
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
# 业务分离,用户名下blog删除,则站点blog同时删除
blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.PROTECT,
related_name="UserInfo_blog")
def __str__(self):
return self.username
class Blog(models.Model):
"""
博客信息表---站点表
1. 直接继承自UserInfo的nid,同时拒绝删除外键,设置自增减
"""
nid = models.OneToOneField(UserInfo, on_delete=models.PROTECT, parent_link=True, primary_key=True, related_name='Blog_nid')
title = models.CharField(verbose_name='个人博客标题', max_length=64)
# https://home.cnblogs.com/u/1915559/,对应这部分[u/1915559/]
site_name = models.CharField(verbose_name='站点名称', max_length=64)
# 一套CSS文件
theme = models.CharField(verbose_name='博客主题', max_length=32)
def __str__(self):
return self.title
class Category(models.Model):
"""博主个人文章分类表"""
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='分类标题', max_length=32)
# 分类表下的blog删除之后,整篇blog也会删除,因此设置级联删除
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE,
related_name='Category_blog', db_column='blog')
def __str__(self):
return self.title
class Tag(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='标签名称', max_length=32)
# on_delete=models.PROTECT 标签被删除,外键关联的blog不能被删除
# to_field='nid' 关联对象的字段名称,该字段必须是unique=True
# related_name 指定用于反向查询的对象, 如果两个对象继承自同一个类,重命名之后则不再因为反向查询而冲突
# 标签表下的blog删除之后,整篇blog也会删除,因此设置级联删除
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE,
related_name='Article', db_column='blog')
def __str__(self):
return self.title
class Article(models.Model):
"""
文章表
"""
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=50, verbose_name='文章标题')
# desc 是description---摘要
desc = models.CharField(max_length=255, verbose_name='文章描述')
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
comment_count = models.IntegerField(default=0)
up_count = models.IntegerField(default=0)
down_count = models.IntegerField(default=0)
# 文章表下的用户删除之后,原来的用户信息也会删除,因为文章表必须要有作者
user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE,
related_name='Article_user', db_column='user')
# 文章下对分类的删除也会对分类主表进行删除
category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE,
related_name='Article_category', db_column='category')
tags = models.ManyToManyField(
to="Tag", # to 参数用来指定与当前model类关联的model类
through='Article2Tag',
through_fields=('article', 'tag')
)
content = models.TextField()
class Meta:
db_table = "文章表"
def __str__(self):
return self.title
class Article2Tag(models.Model):
"""
文章表与标签表的中间表
仅仅添加了一个新的ID,用于给两个表内容进行定位
一旦文章表或者标签表删除,则与他们关联的ID则删除,称为级联删除
"""
nid = models.AutoField(primary_key=True)
# 标签和文章的关联,属于多对多关系,标签可以删除,表不能删除,所以外键关联
# 中间表相当于对两张表的引用,因此删除也需要同步
article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE)
tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE)
class Meta:
# 联合唯一,字段不能重复
unique_together = [
('article', 'tag'),
]
def __str__(self):
v = self.article.title + "---" + self.tag.title
return v
class ArticleUpDown(models.Model):
"""
点赞表
如果点赞被删除,那么用户表和文件表不会被删除
"""
nid = models.AutoField(primary_key=True)
user = models.ForeignKey('UserInfo', null=True, on_delete=models.PROTECT)
article = models.ForeignKey("Article", null=True, on_delete=models.PROTECT)
is_up = models.BooleanField(default=True)
class Meta:
unique_together = [
('article', 'user')
]
class Comment(models.Model):
"""评论表"""
nid = models.AutoField(primary_key=True)
user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.PROTECT)
article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.PROTECT)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
# 富文本编辑器 ckeditor
body = RichTextField()
content = models.CharField(verbose_name='评论内容', max_length=255)
# self == Comment: 数据库语法自关联
parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE)
def __str__(self):
return self.content
3 按照功能进行开发
4 功能测试上线
5 项目部署上线