(1) 设计表结构
设计表结构(使用mysql):
BBS-Demp/settings.py
1 LANGUAGE_CODE = 'zh-hans' # admin显示中文 3 TIME_ZONE = 'Asia/Shanghai' # 时区,默认UTC 5 USE_I18N = True 7 USE_L10N = True 9 USE_TZ = False # 时区,默认True 10 12 DATABASES = { 13 'default': { 14 'ENGINE': 'django.db.backends.mysql', 15 'NAME': 'cnblog', # 数据库名 16 'USER': 'root', # 用户名 17 'PASSWORD': 'root', #密码 18 'HOST': '127.0.0.1', #主机ip 19 'PORT': 3306 # 端口 20 } 21 }
22 AUTH_USER_MODEL = 'app01.UserInfo'
BBS_Demo/__init__.py导入pymysql
1 import pymysql 2 3 pymysql.install_as_MySQLdb()
app01/models.py
1 from django.db import models 2 from django.contrib.auth.models import AbstractUser 3 4 # Create your models here. 5 class UserInfo(AbstractUser): 6 """ 7 用户信息 8 """ 9 telephone = models.CharField(max_length=11, null=True, unique=True, verbose_name='手机号') 10 avatar = models.FileField(upload_to='avatars/', default='/avatars/default.png', verbose_name='头像') 11 create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') 12 13 blog = models.OneToOneField(to='Blog', null=True,on_delete=models.CASCADE) 14 15 def __str__(self): 16 return self.username 17 18 19 class Blog(models.Model): 20 """ 21 博客信息表 22 """ 23 title = models.CharField(max_length=64, verbose_name='个人博客标题') 24 site_name = models.CharField(max_length=64, verbose_name='站点名称') 25 theme = models.CharField(max_length=32, verbose_name='博客主题',null=True,blank=True) 26 27 def __str__(self): 28 return self.title 29 30 31 class Category(models.Model): 32 """ 33 博主个人文章分类表 34 """ 35 title = models.CharField(max_length=32, verbose_name="分类标题") 36 blog = models.ForeignKey(to='Blog', verbose_name='所属博客',on_delete=models.CASCADE) 37 38 def __str__(self): 39 return self.title 40 41 42 class Tag(models.Model): 43 """ 44 博主个人文章标签表 45 """ 46 title = models.CharField(max_length=32, verbose_name='标签名称') 47 blog = models.ForeignKey(to='Blog', verbose_name='所属博客',on_delete=models.CASCADE) 48 49 def __str__(self): 50 return self.title 51 52 53 class Article(models.Model): 54 """ 55 博主个人文章表 56 """ 57 title = models.CharField(max_length=64, verbose_name='文章标题') 58 desc = models.CharField(max_length=256, verbose_name='文章摘要') 59 create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') 60 content = models.TextField(verbose_name='文章内容') 61 62 comment_count = models.IntegerField(default=0,verbose_name='文章评论数') 63 up_count = models.IntegerField(default=0,verbose_name='推荐数') 64 down_count = models.IntegerField(default=0,verbose_name='反对数') 65 66 user = models.ForeignKey(to='UserInfo', verbose_name='作者',on_delete=models.CASCADE) 67 category = models.ForeignKey(to='Category', null=True, verbose_name='文章分类',on_delete=models.SET_NULL) 68 # 自己手动创建第三张表 69 tags = models.ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag'), 70 verbose_name='文章标签') 71 72 def __str__(self): 73 return self.title 74 75 76 class Article2Tag(models.Model): 77 article = models.ForeignKey(to='Article', verbose_name='文章',on_delete=models.CASCADE) 78 tag = models.ForeignKey(to='Tag', verbose_name='标签',on_delete=models.CASCADE) 79 80 class Meta: 81 # 联合唯一 82 unique_together = [('article', 'tag'), ] 83 84 def __str__(self): 85 return self.article.title + '---' + self.tag.title 86 87 88 class ArticleUpDown(models.Model): 89 """ 90 点赞表 91 """ 92 user = models.ForeignKey('UserInfo', null=True,on_delete=models.CASCADE) 93 article = models.ForeignKey('Article', null=True,on_delete=models.CASCADE) 94 is_up = models.BooleanField(default=True) 95 96 class Meta: 97 unique_together = [ 98 ('article', 'user'), 99 ] 100 101 102 class Comment(models.Model): 103 """ 104 评论表:对文章的评论、对评论的评论 105 """ 106 article = models.ForeignKey('Article', verbose_name='评论文章',on_delete=models.CASCADE) 107 user = models.ForeignKey('UserInfo', verbose_name='评论者',on_delete=models.CASCADE) 108 create_time = models.DateTimeField(auto_now_add=True, verbose_name='评论时间') 109 content = models.CharField(max_length=256, verbose_name='评论内容') 110 # 父评论 自关联 111 parent_comment = models.ForeignKey(to='self', null=True,on_delete=models.CASCADE) 112 113 def __str__(self): 114 return self.content
表结构流程图
说明:
- USE_TZ = False 表示使用无时区的时间。TIME_ZONE = 'Asia/Shanghai' 表示使用本地时区。当True是,表示不管time_zone怎么设置,都不生效,所以我们一般设置为Flase。
-
LANGUAGE_CODE = 'zh-hans' 表示设置为中国区域,使用中文。
- 用户信息表继承的是Django内置的AbstractUser类,而不是User类。from django. contrib.auth.models import AbstractUser,这样继承有什么好处?虽然User本质也是继承AbstractUser,但是为了我们扩展更多的自定义字段,就继承AbstractUser。但是别忘了settings.py加 AUTH_USER_MODEL = 'app01.UserInfo' 表示使用app01下的UserInfo模型类,生成的表也是app01_userinfo为名。
- 一个分类对应多篇文章,而分类删除的时候,文章不能删除,所以设置了
1 class Article(models.Model):
pass 2 category = models.ForeignKey(to='Category', null=True,verbose_name='文章分类',on_delete=models.SET_NULL) 3 # 文章也可以没有分类,所以可以为null。 - 一个标签对应多个文章,一个文章对应多个分类。是多对多关系,但是这里没有使用Django自动创建第三张表,而是写了个 Article2Tag 类,用来记录article_id 和tag_id的关系。
- 对于点赞、反对、评论三个功能。在Article类中增加三个字段记录数目就行了。增加点赞表、反对表、评论表,与用户直接建立关系,表示那个用户对哪个文章进行了什么操作。
- 这里这里最难得属评论表了,评论分两种,一个是对文章的评论,一个是对评论的评论。
其实这里是利用了自关联,
1 # 父评论 自关联 2 parent_comment = models.ForeignKey(to='self', null=True,on_delete=models.CASCADE)
上面的表格的评论关系就一目了然了。
- UserInfo模型类中还有一个头像字段,用来保存用户头像的。这里要细说一下。
当用户上传头像的时候,能拿到一个文件对象file_obj。但是我们怎么保存到我们服务器呢?
avatar = models.FileField(upload_to='avatars/', default='/avatars/default.png', verbose_name='头像') 这里必须接收一个文件对象,但数据库保存的只是一个文件相对路径字符串,所以这肯定要配置,Django会帮我们把文件对象保存到我们upload=path的path路径,如果没有这个文件夹,将自动生成,前面我们都知道setting.py文件配置过
1 # 别名,用于url访问 2 STATIC_URL = '/static/' 3 4 # 静态文件存放路径 5 STATICFILES_DIRS = [ 6 os.path.join(BASE_DIR, 'static') 7 ]
Django对静态文件的区分有两种
- /static/ js、css、image、fonts等
- /media/ 用户上传的文件
media和static配置类似:
1 # 绝对路径 用来保存用户上传的文件 2 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 3 # 让用户url可以访问 但要配置urls.py 4 MEDIA_URL = '/media/'
5 # 会将文件下载到media文件夹中 以后文件都会保存到media文件夹下,耦合性非常棒,做到了统一
但这还不够,static虽然在根目录,但是用户可以访问,那些是无关紧要的,看了就看了,其他文件不能看,因为static是django已经帮我们配好路由,我们的media并没有,用户访问不了,所以还要在urls.py
1 from django.views.static import serve 2 from . import settings 3 from django.urls import path, re_path 4 5 urlpatterns = [ 6 # media配置 7 re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}), 8 ]
别问为什么,就是这样做,通过上面两部,用户上传的头像或文件会自动保存到/media/avatars/文件夹下,用户url也可以访问。