目录
项目开发基本流程
1.需求分析
2.架构设计
3.分组开发
4.测试
5.部署上线
一、BBS表设计
先确定表的数量,再确定表的基础字段,再确定表的外键字段
1.表字段设计
1.用户表
-
继承AbstractUser
-
拓展:
phone 电话号码
avatar 用户头像
create_time 创建时间
-
外键字段:
一对一 ==> 个人站点表
2.个人站点表
- site_name 站点名称
- site_title 站点标题
- site_theme 站点样式
3.文章标签表
-
name 标签名
-
外键字段:
一对多 ==> 个人站点表
4.文章分类表
-
name 分类名
-
外键字段:
一对多 ==> 个人站点表
5.文章表
- title 文章标题
- desc 文章简介
- content 文章内容
- create_time 发布时间
数据库字段优化设计:
虽然下面三个字段可以从其他表里面跨表查询计算得出,但是频繁查询影响效率,那么我们就可以将其设计成普通字段,然后同步操作,那我们获取这些字段的时候,可以减少跨表的次数,提高效率
-
点赞数
-
点踩数
-
评论数
-
外键字段
(1)一对多:个人站点
(2)多对多:文章标签
(3)一对多:文章分类
6.点赞点踩表
用来记录哪个用户给哪片文章点了赞还是点了踩,可以默认为空
user
用户id ==>ForeignKey(to="User")
article
文章id ==>ForeignKey(to="Article")
is_up
是否点赞 ==>BooleanField()
7.文章评论表
用来记录哪个用户给哪片文章文章写了哪些评论内容
-
user_id
用户主键值 ==>ForeignKey(to="User")
-
article_id
文章主键值 ==>ForeignKey(to="Article")
-
content
评论内容 ==>CharField()
-
comment_time
评论时间 ==>DateField()
-
parent(自关联)
子评论 ==>ForeignKey(ro="Comment",null=True)
ORM专门提供的自关联写法,直接写
self
parent
==>ForeignKey(ro="self",null=True)
2.七章表之间的关系
1.用户表和个人站点表之间的关系:
- 一对一
- 外键字段:建立在哪一边都可以,外键字段建立在用户表(查询频率更高)
2.个人站点表和文章标签表:
- 一对多:一个个人站点对应多个文章标签
- 外键字段:建立在多一方
3.个人站点表和文章分类表:
- 一对多:一个个人站点对应多个文章分类
- 外键字段:建立在多一方
4.文章表
(1)与个人站点:
- 一对多:一个个人站点下可以有多篇文章
- 外键字段:建立在多一方
(2)与文章标签:
- 多对多:一个标签对应多个文章,一个文章可以有多个标签
- 外键字段:建立在哪一边都可以,外键字段建立在文章表(查询频率更高)
(3)与文章分类:
- 一对多:一个文章只属于一个分类,一个分类可以对应多篇文章
- 外键字段:建立在多一方
5.根评论与子评论
(1)根评论与子评论的概念:
- 根评论就是直接评论但前发布内容多
- 子评论是评论别人的评论
(2)根评论与子评论是一对多的关系:外键字段建在多的一方,当使用的是一张表的时候:
(3)用parent来建立外键字段自关联,并且该字段一定要设立null=True
,因为该字段为子评论,只有为子评论的时候才有值,所以可以为空
表关系图
二、 业务逻辑之注册功能
1.forms组件:常用于数据的创建
前端页面:
注册页面需要用户名的校验,从而使用forms组件。但是,将forms组件的代码也写到views.py文件中过于代码复杂,为了减少耦合,应该将forms组件代码单独写到一个文件中。
如果项目中只使用了一个forms组件,那么可以建立一个py文件
- myforms.py
如果项目中需要使用多个forms组件,那么可以创建一个文件夹,再根据forms组件的功能不同创建不同的py文件
-- forms文件夹
regform.py
loginform.py
userform.py
orderform.py
...
2.前端页面
(1)普通数据展示
label
标签中的for auto_id
,自动获取forms组件input框的id值
{% for form in form_obj %}
<div class="form-group">
<label for="{{ form.auto_id }}">{{ form.label }}</label>
{{ form }}
<span style="color: red"></span>
</div>
{% endfor %}
(2)头像数据展示
在img标签中src属性中使用默认头像,这样页面上就可以显示出默认头像,在通过修改src的属性,结合文本域变化事件,动态显示用户上传的头像
将img标签放到label标签中,点击图标就可以选择到input框
<label for="myavatar">头像
<img src="/static/img/default.png" alt="" id="myImg" width="100" style="margin-left: 20px">
</label>
<input type="file" id="myavatar" style="display: none">
- 文件阅读器
onload
使得文件阅读器等待io操作完成
(3)ajax请求
- 1.产生formData对象,通过对formData对象添加书籍,发送给后端
- 2.添加普通键值对,通过form标签的序列化的功能
- 3.添加文件数据
- 4.发送ajax请求,并根据后端返回的状态码不同,在前端页面上跳转登录路由或者渲染不同的错误提示信息
// 打包数据,发送ajax请求
$('#id_commit').click(function () {
// 1.产生formData对象,通过对formData对象添加书籍,发送给后端
let formDataObj = new FormData();
// 2.添加普通键值对
$.each($('#myform').serializeArray(), function (index, obj) {
formDataObj.append(obj.name, obj.value)
});
// 3.添加文件数据
formDataObj.append('avatar',$('#myavatar')[0].files[0]);
// 4.发送ajax请求
$.ajax({
url:'',
type:'post',
data:formDataObj,
//发送文件的两个固定配置
contentType:false,
processData:false,
success:function (args) {
if (args.code===10001){
// 成功跳转到登录页面
window.location.href = args.url
}else {
// 展示错误信息
$.each(args.msg,function (key,array){
// 凭借获得input框的id
let inputId = '#id_' + key;
$(inputId).next().text(array[0]).parent().addClass('has-error')
})
}
}
})
})
3.后端
def register_func(request):
# 1.产生forms对象,在前端页面上渲染出来。使用forms组件,则在app01中单独创建一个存放forms代码的文件
form_obj = RegForm()
# 2.ajax发送post请求
if request.method == "POST":
# 2.1建立返回字典
back_dict = {'code': 10000, 'msg': ''}
# 2。2通过forms组件校验数据
form_obj = RegForm(
request.POST) # 这时候request.POST的数据 { username,password,confirm_password, csrfmiddlewaretoken}
# 2.3判断数据是否合法
if form_obj.is_valid(): # 这时候cleaned_data 中的数据 {username,password,confirm_password}
cleaned_data = form_obj.cleaned_data
# 2.4 在校验完的数据中剔除confirm_password
cleaned_data.pop('confirm_password')
# 2.5 获取用户头像文件,并判断用户是否上传文件
"""如果不判断是否用户上传头像文件,
则request.FILES.get('avatar')为None,直接添加到校验过的数据中
存到数据库中该字段则为None,就不会使用默认头像了,
"""
file_obj = request.FILES.get('avatar')
if file_obj:
cleaned_data['avatar'] = file_obj
# 2.6 通过auth模块创建用户数据
models.UserInfo.objects.create_user(**cleaned_data)
# 2.7 修改状态码和增加注册成功跳转的路由
back_dict['code'] = 10001
back_dict['url'] = '/login/'
else:
# 3 添加 状态码和错误信息 ,并把该字典返回给这个ajax的回调函数
back_dict['code'] = 10002
back_dict['msg'] = form_obj.errors
return JsonResponse(back_dict)
return render(request, 'registerPage.html', locals())
-
判断用户是否上传头像
if avatar_obj
不可以不加,因为没值的时候才会使用默认值,如果没有传none的情况下,头像字段的值会变成none就无法使用默认头像
if avatar_obj:
cleaned_data
中的键值要和userinfo表保持一致
三、 业务逻辑之登录功能
1.图片验证码
(1)img标签的src属性三种模式
-
1.图片路径
则会通过该路径获取图片加载到页面上
-
2.路由
会直接向该路由发送get请求,如果结果是图片的二进制数据,那么自动渲染图片
-
3.图片的二进制数据
直接渲染图片
(2)推导步骤2: pillow模块
可以帮助产生图片,处理图片,在图片上写字
- 导入
from PIL import Image,ImageDraw,ImageFont
Image:生成图片
ImageDraw: 在图片上画画
ImageFont:控制字体的样式
+
-
Image.new(模式,尺寸,颜色)
颜色可以使用RGB三基色的数据
IO操作过于频繁,又要存又要取,效率低下
(3)推倒步骤3:IO内存管理器模块
- 导入
from io import BytesIO,StringIO
- BytesIO:临时存储数据,返回的时候数据是二进制格式
- StringIO:临时存储数据,返回的时候数据是字符串