项目流程:

    1 搞清楚需求(产品经理)

    1. (1) 基于用户认证组件和Ajax实现登录验证(图片验证码)
    2. 1. 一次请求伴随多次请求
    3. 2. PIL [https://pillow.readthedocs.io/en/stable/]
    4. 3. session存储
    5. 4. 验证码刷新
    6. (2) 基于Ajaxforms组件实现注册功能(表项检查,是否符合密码长度等规则)
    7. (3) 设计系统首页,完成文章列表渲染
    8. (4) 设计个人站点
    9. (5) 文章详情页(点赞)
    10. (6) 文章的评论---对评论的评论
    11. (7) 后台管理页面(富文本编辑框,防止XSS攻击)

    2 设计表结构

    1. 1. 需要一张用户表【状态内容访问隔离---cookies seesion---用户认证组件【用户表必须使用auth_user用户表(扩展字段)】】
    2. 使用方式:用户表继承AbstractUseruser继承了它)
    3. 2. 表关系
    4. 1. 分类与站点表
    5. 1. 站点表和用户个人信息表为一对一的关系【可以通过任意一方查找另外一方,需要查找资料,一对一关系有哪些限制代码】
    6. 2. 分类与用户表的关系,一个用户可以创建多个分类,一个分类只能属于一个人
    7. 3. 分类与站点的关系:站点可以有多个分类,一个分类只能属于一个站点,站点和用户是一对于的关系,那么用户和分类的关系等价于站点与分类的关系,一对于的继承,引入跨表查询,站点可以通过用户查找分类
    8. 4. 分类的多,绑定给博客表而非用户表【查询逻辑:用户表--->博客--->分类表】
    9. 2. 分类与标签表
    10. 1. 一个用户可以创建多个标签,一个标签只能属于一个用户【将标签理解为斜杠青年,一个年轻有为的人的多重身份】
    11. 2. 文章与分类是多对多或者一对多,博客园默认多对多的关系,标签与文章是多对多的关系,分类与文章构成一对一的关系
    12. 3. 标签和分类可以自行创建
    13. 4. 一个分类可以对应多个文章,一篇文章只能属于一个分类
    14. 5. 文章的关键词就是标签,一个文章可以有多个标签,一个标签可以属于多篇文章[多对多]
    15. 3. 评论表
    16. 1. 分清根评论与(对文章的评论)子评论(针对评论的评论
    17. 4. 分类表和用户表的关系
    18. 1. 一个人可以创建多个分类,一个分类只能属于一个人[一对多]
    19. 5. 站点与用户表的关系
    20. 1. 一个站点对应一个用户,一个用户也只能拥有一个站点[一对一,其他表与用户表绑定的关系可以等同于它与站点表的关系]
    21. 6. 用户与分类表的关系
    22. 1. 一个用户对应多个分类表,一个分类表只能属于一个用户[一对多的关系]
    23. 7. 标签与站点表
    24. 1. 一个站点可以拥有多个标签,但一个标签只能属于一个站点[一对多的关系]
    25. 8. 文章表与分类表
    26. 1. 一个分类下面可以有多篇文章,但一篇文章只能属于一个分类[一对多]
    27. 2. 标签和文章是多对多的关系,为了区分,让文章和分类保持一对多的关系
    28. 9. 用户表与文章表的关系
    29. 1. 一个用户可以发布多篇文章,但一篇文章只能属于一个用户[一对多]
    30. 3. 表功能设计
    31. 1. 分类与标签表
    32. 1. 一个分类对应多篇文章,标签就是文章的关键词
    33. 2. 一个文章可以多个标签,一个标签可以对应多个文章
    34. 3. 标签与站点绑定一对多的关系
    35. 2. 文章表
    36. 1. Tag使用了中间模型概念,在tagarticle之间使用article2构建关联
    37. 2. 文章表与用户绑定多对一的关系
    38. 3. 一个文章表包含那些信息?标题,摘要,作者,创建时间
    39. 3. 点赞表
    40. 1. 存储点赞信息,哪一个用户对那一篇文章点赞或者踩灭
    41. 2. user, article , bool判断点赞或者踩灭
    42. 4. 评论表
    43. 1. 哪一个用户,对那一篇文章,在什么时候,做了什么评论?
    44. 2. 添加字段描述父子关系,Django默认为外键设置自增减ID
    45. 3. 为了避免跨表查询,每生成一个评论,做一个自加一
    46. 4. 查询业务需求大于存储和修改,所以用空间换时间
    47. 5. 1219 --- 07 0
    48. 4. 查询逻辑
    49. 1. user表与blog表为一对一关系,可以通过user找到blog,通过blogcategory的一对多关系,再找到分类表,总得来说,blogcategory的关系可以迁移到categoryuser的关系
    50. 2.

    表结构的二次理解

    1. 1. 用户表的功能:登录验证和用户注册
    2. 2. 区分登录和未登录状态:cookiesession
    3. 3. Django系统表关系: User对应的是

    站点表的修改

    1. from django.db import models
    2. from ckeditor.fields import RichTextField
    3. # Create your models here.
    4. from django.contrib.auth.models import AbstractUser
    5. class UserInfo(AbstractUser):
    6. """用户信息"""
    7. nid = models.AutoField(primary_key=True)
    8. telephone = models.CharField(max_length=11, null=True, unique=True)
    9. # 存储用户头像文件
    10. avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")
    11. # 在生成字段时就以当前时间存储
    12. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    13. # 业务分离,用户名下blog删除,则站点blog不能被删除
    14. blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.PROTECT,
    15. related_name="UserInfo_blog")
    16. def __str__(self):
    17. return self.username
    18. class Blog(models.Model):
    19. """
    20. 博客信息表---站点表
    21. 1. 直接继承自UserInfo的nid,同时拒绝删除外键,设置自增减
    22. """
    23. nid = models.OneToOneField(UserInfo, on_delete=models.PROTECT, parent_link=True, primary_key=True, related_name='Blog_nid')
    24. title = models.CharField(verbose_name='个人博客标题', max_length=64)
    25. site_name = models.CharField(verbose_name='站点名称', max_length=64)
    26. theme = models.CharField(verbose_name='博客主题', max_length=32)
    27. def __str__(self):
    28. return self.title
    29. class Category(models.Model):
    30. """博主个人文章分类表"""
    31. nid = models.AutoField(primary_key=True)
    32. title = models.CharField(verbose_name='分类标题', max_length=32)
    33. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE,
    34. related_name='Category_blog', db_column='blog')
    35. def __str__(self):
    36. return self.title
    37. class Tag(models.Model):
    38. nid = models.AutoField(primary_key=True)
    39. title = models.CharField(verbose_name='标签名称', max_length=32)
    40. # on_delete=models.PROTECT 标签被删除,外键关联的blog不能被删除
    41. # to_field='nid' 关联对象的字段名称,该字段必须是unique=True
    42. # related_name 指定用于反向查询的对象, 如果两个对象继承自同一个类,重命名之后则不再因为反向查询而冲突
    43. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.PROTECT,
    44. related_name='Article', db_column='blog')
    45. def __str__(self):
    46. return self.title
    47. class Article(models.Model):
    48. """
    49. 文章表
    50. """
    51. nid = models.AutoField(primary_key=True)
    52. title = models.CharField(max_length=50, verbose_name='文章标题')
    53. desc = models.CharField(max_length=255, verbose_name='文章描述')
    54. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    55. comment_count = models.IntegerField(default=0)
    56. up_count = models.IntegerField(default=0)
    57. down_count = models.IntegerField(default=0)
    58. user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE,
    59. related_name='Article_user', db_column='user')
    60. category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE,
    61. related_name='Article_category', db_column='category')
    62. tags = models.ManyToManyField(
    63. to="Tag", # to 参数用来指定与当前model类关联的model类
    64. through='Article2Tag',
    65. through_fields=('article', 'tag')
    66. )
    67. content = models.TextField()
    68. class Meta:
    69. db_table = "文章表"
    70. def __str__(self):
    71. return self.title
    72. class Article2Tag(models.Model):
    73. """
    74. 文章表与标签表的中间表
    75. 仅仅添加了一个新的ID,用于给两个表内容进行定位
    76. 一旦文章表或者标签表删除,则与他们关联的ID则删除,称为级联删除
    77. """
    78. nid = models.AutoField(primary_key=True)
    79. # 标签和文章的关联,属于多对多关系,标签可以删除,表不能删除,所以外键关联
    80. article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE)
    81. tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE)
    82. class Meta:
    83. unique_together = [
    84. ('article', 'tag'),
    85. ]
    86. def __str__(self):
    87. v = self.article.title + "---" + self.tag.title
    88. return v
    89. class ArticleUpDown(models.Model):
    90. """
    91. 点赞表
    92. 如果点赞被删除,那么用户表和文件表不会被删除
    93. """
    94. nid = models.AutoField(primary_key=True)
    95. user = models.ForeignKey('UserInfo', null=True, on_delete=models.PROTECT)
    96. article = models.ForeignKey("Article", null=True, on_delete=models.PROTECT)
    97. is_up = models.BooleanField(default=True)
    98. class Meta:
    99. unique_together = [
    100. ('article', 'user')
    101. ]
    102. class Comment(models.Model):
    103. """评论表"""
    104. nid = models.AutoField(primary_key=True)
    105. user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.PROTECT)
    106. article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.PROTECT)
    107. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    108. # 富文本编辑器 ckeditor
    109. body = RichTextField()
    110. content = models.CharField(verbose_name='评论内容', max_length=255)
    111. parent_comment = models.ForeignKey('self', null=True, on_delete=models.PROTECT)
    112. def __str__(self):
    113. return self.content

    修改版

    1. from django.db import models
    2. from ckeditor.fields import RichTextField
    3. # Create your models here.
    4. from django.contrib.auth.models import AbstractUser
    5. class UserInfo(AbstractUser):
    6. """用户信息"""
    7. nid = models.AutoField(primary_key=True)
    8. telephone = models.CharField(max_length=11, null=True, unique=True)
    9. # 存储用户头像文件
    10. avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")
    11. # 在生成字段时就以当前时间存储,用于计算园龄
    12. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    13. # 业务分离,用户名下blog删除,则站点blog同时删除
    14. blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.PROTECT,
    15. related_name="UserInfo_blog")
    16. def __str__(self):
    17. return self.username
    18. class Blog(models.Model):
    19. """
    20. 博客信息表---站点表
    21. 1. 直接继承自UserInfo的nid,同时拒绝删除外键,设置自增减
    22. """
    23. nid = models.OneToOneField(UserInfo, on_delete=models.PROTECT, parent_link=True, primary_key=True, related_name='Blog_nid')
    24. title = models.CharField(verbose_name='个人博客标题', max_length=64)
    25. # https://home.cnblogs.com/u/1915559/,对应这部分[u/1915559/]
    26. site_name = models.CharField(verbose_name='站点名称', max_length=64)
    27. # 一套CSS文件
    28. theme = models.CharField(verbose_name='博客主题', max_length=32)
    29. def __str__(self):
    30. return self.title
    31. class Category(models.Model):
    32. """博主个人文章分类表"""
    33. nid = models.AutoField(primary_key=True)
    34. title = models.CharField(verbose_name='分类标题', max_length=32)
    35. # 分类表下的blog删除之后,整篇blog也会删除,因此设置级联删除
    36. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE,
    37. related_name='Category_blog', db_column='blog')
    38. def __str__(self):
    39. return self.title
    40. class Tag(models.Model):
    41. nid = models.AutoField(primary_key=True)
    42. title = models.CharField(verbose_name='标签名称', max_length=32)
    43. # on_delete=models.PROTECT 标签被删除,外键关联的blog不能被删除
    44. # to_field='nid' 关联对象的字段名称,该字段必须是unique=True
    45. # related_name 指定用于反向查询的对象, 如果两个对象继承自同一个类,重命名之后则不再因为反向查询而冲突
    46. # 标签表下的blog删除之后,整篇blog也会删除,因此设置级联删除
    47. blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE,
    48. related_name='Article', db_column='blog')
    49. def __str__(self):
    50. return self.title
    51. class Article(models.Model):
    52. """
    53. 文章表
    54. """
    55. nid = models.AutoField(primary_key=True)
    56. title = models.CharField(max_length=50, verbose_name='文章标题')
    57. # desc 是description---摘要
    58. desc = models.CharField(max_length=255, verbose_name='文章描述')
    59. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    60. comment_count = models.IntegerField(default=0)
    61. up_count = models.IntegerField(default=0)
    62. down_count = models.IntegerField(default=0)
    63. # 文章表下的用户删除之后,原来的用户信息也会删除,因为文章表必须要有作者
    64. user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE,
    65. related_name='Article_user', db_column='user')
    66. # 文章下对分类的删除也会对分类主表进行删除
    67. category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE,
    68. related_name='Article_category', db_column='category')
    69. tags = models.ManyToManyField(
    70. to="Tag", # to 参数用来指定与当前model类关联的model类
    71. through='Article2Tag',
    72. through_fields=('article', 'tag')
    73. )
    74. content = models.TextField()
    75. class Meta:
    76. db_table = "文章表"
    77. def __str__(self):
    78. return self.title
    79. class Article2Tag(models.Model):
    80. """
    81. 文章表与标签表的中间表
    82. 仅仅添加了一个新的ID,用于给两个表内容进行定位
    83. 一旦文章表或者标签表删除,则与他们关联的ID则删除,称为级联删除
    84. """
    85. nid = models.AutoField(primary_key=True)
    86. # 标签和文章的关联,属于多对多关系,标签可以删除,表不能删除,所以外键关联
    87. # 中间表相当于对两张表的引用,因此删除也需要同步
    88. article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE)
    89. tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE)
    90. class Meta:
    91. # 联合唯一,字段不能重复
    92. unique_together = [
    93. ('article', 'tag'),
    94. ]
    95. def __str__(self):
    96. v = self.article.title + "---" + self.tag.title
    97. return v
    98. class ArticleUpDown(models.Model):
    99. """
    100. 点赞表
    101. 如果点赞被删除,那么用户表和文件表不会被删除
    102. """
    103. nid = models.AutoField(primary_key=True)
    104. user = models.ForeignKey('UserInfo', null=True, on_delete=models.PROTECT)
    105. article = models.ForeignKey("Article", null=True, on_delete=models.PROTECT)
    106. is_up = models.BooleanField(default=True)
    107. class Meta:
    108. unique_together = [
    109. ('article', 'user')
    110. ]
    111. class Comment(models.Model):
    112. """评论表"""
    113. nid = models.AutoField(primary_key=True)
    114. user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.PROTECT)
    115. article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.PROTECT)
    116. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    117. # 富文本编辑器 ckeditor
    118. body = RichTextField()
    119. content = models.CharField(verbose_name='评论内容', max_length=255)
    120. # self == Comment: 数据库语法自关联
    121. parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE)
    122. def __str__(self):
    123. return self.content

    3 按照功能进行开发

    4 功能测试上线

    5 项目部署上线