一、博客系统分析
数据库的构建
首先,我们分析一个博客系统的功能:
- 一个博客可以有多个标签(多对多)
- 一个博客可以有多条评论(一对多)
- 一个博客只可以有一个类别(多对一)
接下来,我们分析关系的属性:
博客:标题,作者,内容,发布时间,分类(外键),标签(多对多)等 标签:标签名 类别:分类名 评论:作者,博客(外键),邮箱,内容,发布时间等。有8张表,表关系如下:
图中箭头开始的英文字母表示关联字段
按照箭头方向查询,表示正向查询,否则为反向查询
新建项目cnblog,应用名为blog
修改models.py,必须导入模块
from django.contrib.auth.models import AbstractUser
因为有一个表userinfo需要继承它。django自带的auth_user表也是继承AbstractUser
表模型如下:
from django.db import models# Create your models here.from django.db import models# Create your models here.from django.contrib.auth.models import AbstractUserclass 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 = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE) def __str__(self): return self.usernameclass Blog(models.Model): """ 博客信息 """ nid = models.AutoField(primary_key=True) 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.titleclass 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) def __str__(self): return self.titleclass Tag(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) def __str__(self): return self.titleclass 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) content = models.TextField() 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) category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) tags = models.ManyToManyField( to="Tag", #through参数可以指定用作中介的中间模型 through='Article2Tag', ) def __str__(self): return self.titleclass Article2Tag(models.Model): 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 vclass ArticleUpDown(models.Model): """ 点赞表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE) article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE) is_up = models.BooleanField(default=True) class Meta: # 组合唯一约束 unique_together = [ ('article', 'user'), ]class Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) content = models.CharField(verbose_name='评论内容', max_length=255) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) parent_comment = models.ForeignKey('Comment', null=True, on_delete=models.CASCADE) def __str__(self): return self.content
相关参数解释:
through 表示orm,不要创建关系表。而制定一个表,这个表自己来创建!为什么呢?因为orm创建多对多关系表时,只有3个字段。那么需要关系表需要扩充字段时,就不行了!
所以设置through 字段,是为了方便添加额外的字段。使用through,那么这个表,称之为中间模型。
在Comment模型表中,有一个字段parent_comment。它关联的是本身表中的主键nid,它是一个父级评论id,用来展示谁评论谁!to='Comment'等同于to='self'
修改settings.py配置文件,覆盖默认的User模型。最后一行添加,否则执行创建表命令时会报错!
AUTH_USER_MODEL="blog.UserInfo"
Django允许你通过修改setting.py文件中的 AUTH_USER_MODEL 设置覆盖默认的User模型,其值引用一个自定义的模型。
bolg,是应用名。
使用下面2个命令,生成表
python manage.py makemigrationspython manage.py migrate
生成的表如下:
注意:django自带的auth_user表没有了,取而代之的是blog_userinfo表。
查看表字段
它在auth_user表的基础上,增加了5个字段!
登录验证
添加超级用户,密码必须是8位或者以上!
python manage.py createsuperuser
效果如下:
再创建2个用户,zhang和lisi。用来测试多个用户登录!
lisi用户
修改urls.py,增加路径
from blog import viewsurlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), path('login/', views.login),]
修改views.py,增加视图函数
from django.shortcuts import render,HttpResponse,redirectfrom django.contrib import authfrom blog.models import Article,UserInfo# Create your views here.def login(request): if request.method=="POST": user=request.POST.get("user") pwd=request.POST.get("pwd") # 用户验证成功,返回user对象,否则返回None user=auth.authenticate(username=user,password=pwd) if user: # 登录,注册session # 全局变量 request.user=当前登陆对象(session中) auth.login(request,user) return redirect("/index/") return render(request,"login.html")def index(request): return render(request,"index.html")
修改login.html
Title
修改index.html
Title INDEX
访问登录页面
跳转到首页
首页修饰
修改settings.py,设置静态文件目录。最后一行添加:
STATICFILES_DIRS=[ os.path.join(BASE_DIR,"static")]
下载bootsrtap,将压缩的包内容放到satic目录
img和js是新建的
目录如下:
修改urls.py,增加路径
urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), path('', views.index),]
修改views.py,增加注销
def logout(request): # 注销 auth.logout(request) return redirect("/index/")
修改index.html
Title Panel title
Panel contentPanel title
Panel content222Panel title
Panel contentPanel title
Panel contentPanel title
Panel content
重新登录,效果如下:
添加内容
使用django自带的admin后台,快速添加数据。
访问后台页面,这里必须是超级用户。上面已经创建了超级用户xiao
默认是空的
操作表,必须要注册
修改views.py同级目录下的admin.py
注册所有的模型表
from django.contrib import admin# Register your models here.from blog import modelsadmin.site.register(models.UserInfo)admin.site.register(models.Blog)admin.site.register(models.Category)admin.site.register(models.Tag)admin.site.register(models.Article2Tag)admin.site.register(models.Article)admin.site.register(models.ArticleUpDown)admin.site.register(models.Comment)
再次刷新页面
点击Articles,添加文章
从http://www.py3study.com 上面copy一篇博客
注意内容都是html代码
添加分类
添加站点
保存
点击保存
添加成功
多添加几篇博客
首页文章展示
修改settings.py,更改时区
TIME_ZONE = 'Asia/Shanghai'
修改index视图函数
def index(request): article_list=Article.objects.all() return render(request,"index.html",{ "article_list":article_list})
修改index.html
Title Panel title
Panel contentPanel title
Panel content{ % for article in article_list %}{ { article.title }}
{ { article.user.username }} 发布于 { { article.create_time|date:'Y-m-d H:i' }} 评论({ { article.comment_count }}) 点赞({ { article.up_count }})
{ % endfor %}Panel title
Panel contentPanel title
Panel contentPanel title
Panel content
访问首页,效果如下:
个人站点展示
比如博客园范围个人站点是域名加用户名,就可以了,比如:
如果用户不存在,会提示404页面
增加404页面
修改urls.py,增加路径。注意导入re_path
from django.contrib import adminfrom django.urls import path,re_pathfrom blog import viewsurlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), path('', views.index), re_path('(?P\w+)', views.homesite),]
修改views.py,增加视图函数
def homesite(request,username): """ 查询 :param request: :param username: :return: """ # 查询当前站点的用户对象 user=UserInfo.objects.filter(username=username).first() if not user: return render(request,"not_found.html") return render(request,"homesite.html",{ "user":user})
在templates目录创建文件not_found.html
Title
创建homesite.html
Title { { user }}
访问网页:http://127.0.0.1:8000/xiao
访问一个不存在的用户
http://127.0.0.1:8000/123
页面提示404
展示文章列表
修改homesite视图函数
def homesite(request,username): """ 查询 :param request: :param username: :return: """ # 查询当前站点的用户对象 user=UserInfo.objects.filter(username=username).first() if not user: return render(request,"not_found.html") # 查询当前站点对象 blog=user.blog # 查询当前用户发布的所有文章 article_list=Article.objects.filter(user__username=username) return render(request,"homesite.html",{ "blog":blog,"article_list":article_list})
修改homesite.html
Title {
{ blog.title }}Panel title
Panel contentPanel title
Panel contentPanel title
Panel content{ % for article in article_list %}{ { article.title }}
{ { article.desc }}发布于 { { article.create_time|date:'Y-m-d H:i' }} 评论({ { article.comment_count }}) 点赞({ { article.up_count }})
{ % endfor %}
访问xiao的个人站点,效果如下:
这个时候发现,左上角没有显示博客标题。那是因为用户认证表的blog_id字段默认为空,需要在admin后台绑定一下
登录到后台,点击用户表,点击指定的用户xiao
拉到最下面,绑定blog,点击保存
再次刷新个人站点,发现出来了!
作业:
查询当前站点每一个分类的名称以及对应的文章数
查询当前站点每一个标签的名称以及对应的文章数
答案:
修改homesite视图函数
def homesite(request,username): """ 查询 :param request: :param username: :return: """ # 查询当前站点的用户对象 user=UserInfo.objects.filter(username=username).first() if not user: return render(request,"not_found.html") # 查询当前站点对象 blog=user.blog # 查询当前用户发布的所有文章 article_list=Article.objects.filter(user__username=username) # 查询当前站点每一个分类的名称以及对应的文章数 cate_list = Category.objects.filter(blog=blog).annotate(c=Count("article__title")).values("title","c") print(cate_list) # 查询当前站点每一个标签的名称以及对应的文章数 tag_list = Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values("title","c") print(tag_list) dict = { "blog":blog, "article_list":article_list, "category_count":cate_list, "tag_count":tag_list, } return render(request,"homesite.html",dict)
修改homesite.html
Title {
{ blog.title }}{ % for foo in category_count %}我的标签
{ { foo.title }}({ { foo.c }}){ % endfor %}{ % for foo in tag_count %}随笔分类
{ { foo.title }}({ { foo.c }}){ % endfor %}随笔档案
Panel content{ % for article in article_list %}{ { article.title }}
{ { article.desc }}发布于 { { article.create_time|date:'Y-m-d H:i' }} 评论({ { article.comment_count }}) 点赞({ { article.up_count }})
{ % endfor %}
进入admin后台,添加一个tag。增加一篇文章
重新访问个人站点网页,效果如下: