【BBS】第02回 注册相关功能实现
目录
1. 补充知识
1.1 去IOE
1. 它是阿里巴巴造出的概念。其本意是,在阿里巴巴的IT架构中,去掉IBM的小型机、Oracle数据库、EMC存储设备,代之以自己在开源软件基础上开发的系统
2、去IOE意味着接下来政府必须将数据安全牢牢掌握在国内企业手中,防止数据丢失造成的一系列严重后果。也是在此背景之下,阿里云与12306以及之前的中国气象总局、药监局展开深度合作才显得水到渠成。
1.2 ForeignKey:on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
models.CASCADE
删除关联数据,与之关联也删除
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
1.3 字段类有很多属性
null 数据库字段是否可以为空
db_column 数据库中字段的列名
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
erbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
db_constraint 是否在数据库中创建外键约束,默认为True。放在ForeignKey,不建立外键关联
1,4 外键
1. OneToOneField: 一对一就是ForeignKey+unique
2. ManyToManyField:多对多
自动创建中间表
手动创建中间表:中间表中除了关联字段外,还有其他字段
写出中间表ManyToManyField的属性:
through:通过哪个中间表做关联
through_fields:设置关联的字段
3. OneToOneField,ForeignKey,ManyToManyField
related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’。
related_query_name:反向查询操作时,使用的连接前缀,用于替换表名
2. 注册forms编写
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from .models import UserInfo
class RegisterFom(forms.Form):
username = forms.CharField(max_length=8, min_length=3, required=True, label='用户名', error_messages={
'max_length': '太长了',
'min_length': '太短了',
'required': '这个必填',
}, widget=widgets.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(max_length=8, min_length=3, required=True, label='密码', error_messages={
'max_length': '太长了',
'min_length': '太短了',
'required': '这个必填',
}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
re_password = forms.CharField(max_length=8, min_length=3, required=True, label='确认密码', error_messages={
'max_length': '太长了',
'min_length': '太短了',
'required': '这个必填',
}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}))
# 局部钩子校验,验证用户名是否重复
# 方案一
def clean_username(self):
username = self.cleaned_data.get('username')
user = UserInfo.objects.filter(username=username).first()
if user:
# 已存在,不合理
raise ValidationError('该用户名已经存在')
else:
return username
# 方式二·
# def clean_username(self):
# username = self.cleaned_data.get('username')
# try:
# UserInfo.objects.get(username=username) # 有且只有一条才正常,否则就抛出异常
# raise ValidationError('该用户名已经存在')
# except Exception:
# return username
# 全局钩子校验,验证两次密码是否一致
def clean(self):
# 比较两个密码是否一致 :cleaned_data存的是校验过后的数据,是个字典
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if password == re_password:
# 合理,返回校验过后的数据
return self.cleaned_data
else:
# 不合理,抛出校验失败的异常
raise ValidationError('两次密码不一致')
3. 注册页面搭建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1 class="text-center">注册功能</h1>
<form id="id_form">
{% for item in form %}
<div class="form-group">
<label for="{{ item.id_for_label }}">{{ item.label }}</label>
{{ item }}
<span class="pull-right text-danger"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="id_file">头像
<img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px" id="id_img">
</label>
<input type="file" id="id_file" accept="image/*" style="display: none">
</div>
<div class="form-group text-center">
{# 如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#}
<input type="button" value="注册" class="btn btn-danger" id="id_submit">
<span class="text-danger"></span>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
from .forms import RegisterFom
def register(request):
if request.method == 'GET':
register_form = RegisterFom()
# context:上下文
return render(request, 'register.html', context={'form': register_form})
3. 头像动态显示
<script>
$("#id_file").change(function () {
// 把当前图片,放到img标签中
// 把图片地址放到img标签上,标签就显示了图片
//$("#id_img")[0].src='https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg'
// 1 id_file这个标签的图片读出来,借助于文件阅读器,js提供的一个类
var reader=new FileReader()
// 2 拿到文件对象,赋值给一个变量
var file =$("#id_file")[0].files[0]
// 3 把文件读到文件阅读器中
reader.readAsDataURL(file)
// 4 等读完后,把文件阅读器的内容写到img标签上
//$("#id_img")[0].src=reader.result
reader.onload=function (){
//$("#id_img")[0].src=reader.result
$('#id_img').attr('src', reader.result)
}
})
</script>
5. 注册功能后端
from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
from .forms import RegisterFom
from .models import UserInfo
def register(request):
if request.method == 'GET':
register_form = RegisterFom()
# context:上下文
return render(request, 'register.html', context={'form': register_form})
else: # post请求的时候
res = {'code':100, 'msg': '注册成功'}
# 取出用户名密码,使用form校验数据,如果校验通过,存到数据库中,如果校验不通过,返回错误信息
register_form = RegisterFom(data=request.POST)
if register_form.is_valid():
# 数据字段自己的规则,局部钩子,全局钩子都校验过后,通过了
# 1 re_password字段,不存在数据库的,剔除
register_data = register_form.cleaned_data
register_data.pop('re_password')
# 2 头像:如果携带了要存,头像是文件,在request.FILES中
my_file = request.FILES.get('my_file')
if my_file:
register_data['avatar'] = my_file
# 3 存到数据库
UserInfo.objects.create_user(**register_data)
# UserInfo.objects.create_user(username=register_data.get('username'),) 等同于上面,但是麻烦
return JsonResponse(res)
else:
res['code'] = 101
res['msg'] = '注册失败'
res['errors'] = register_form.errors
return JsonResponse(res)
6. 注册功能前端
// 当点击注册按钮,发送ajax请求到后端的注册功能
$("#id_submit").click(function (){
// 上传文件,借助于formdata对象
var formdata = new FormData()
// 方式一
{#formdata.append('username',$('#id_username').val())#}
{#formdata.append('password',$('#id_username').val())#}
{#formdata.append('re_password',$('#id_username').val())#}
{#formdata.append('my_file',$('#id_file')[0].files[0])#}
{#// csrftoken也要加上#}
// 方式二:借助于form表单批量处理
var data = $("#id_form").serializeArray()
console.log(data)
//使用for循环,把data中得到的数据,转存到formdata中 jquery的each循环
$.each(data, function (i, v){
formdata.append(v.name, v.value)
})
// 文件单独载放进去
formdata.append('my_file', $('#id_file')[0].files[0])
// 使用ajax向后端发送请求
// 1 三种编码格式:urlencoded, from-data,json
$.ajax({
url:'/register/',
type:'post',
processData: false,
contentType: false,
data: formdata,
success: function (data){
console.log(data)
if(data.code==100){
// 表示注册成功,跳转到登录页面
location.href='/login/'
}else {
//在前端显示错误信息
console.log(data);
//两次密码不一致的错误渲染
/*
if(data.errors['__all__']){
$(".error").html(data.errors['__all__'][0])
}
*/
// 其他标签的错误渲染
$.each(data.errors,function (k,v) {
if (k=='__all__'){
$(".error").html(v[0])
}else{
// 链式调用,在对应的input后的span中插入错误文字,把父div加入has-error类,整个框变红
$("#id_"+k).next().html(v[0]).parent().addClass('has-error')
}
})
// 起一个定时任务,把错误信息去掉
setTimeout(function (){
// 把所有span的文字去掉,把父div中得has-error类去掉
$('.text-danger').html("").parent().removeClass('has-error')
},3000)
}
}
})
})
7. 注册错误信息渲染
// 当点击注册按钮,发送ajax请求到后端的注册功能
$("#id_submit").click(function (){
// 上传文件,借助于formdata对象
var formdata = new FormData()
// 方式一
{#formdata.append('username',$('#id_username').val())#}
{#formdata.append('password',$('#id_username').val())#}
{#formdata.append('re_password',$('#id_username').val())#}
{#formdata.append('my_file',$('#id_file')[0].files[0])#}
{#// csrftoken也要加上#}
// 方式二:借助于form表单批量处理
var data = $("#id_form").serializeArray()
console.log(data)
//使用for循环,把data中得到的数据,转存到formdata中 jquery的each循环
$.each(data, function (i, v){
formdata.append(v.name, v.value)
})
// 文件单独载放进去
formdata.append('my_file', $('#id_file')[0].files[0])
// 使用ajax向后端发送请求
// 1 三种编码格式:urlencoded, from-data,json
$.ajax({
url:'/register/',
type:'post',
processData: false,
contentType: false,
data: formdata,
success: function (data){
console.log(data)
if(data.code==100){
// 表示注册成功,跳转到登录页面
location.href='/login/'
}else {
//在前端显示错误信息
console.log(data);
//两次密码不一致的错误渲染
/*
if(data.errors['__all__']){
$(".error").html(data.errors['__all__'][0])
}
*/
// 其他标签的错误渲染
$.each(data.errors,function (k,v) {
if (k=='__all__'){
$(".error").html(v[0])
}else{
// 链式调用,在对应的input后的span中插入错误文字,把父div加入has-error类,整个框变红
$("#id_"+k).next().html(v[0]).parent().addClass('has-error')
}
})
// 起一个定时任务,把错误信息去掉
setTimeout(function (){
// 把所有span的文字去掉,把父div中得has-error类去掉
$('.text-danger').html("").parent().removeClass('has-error')
},3000)
}
}
})
})
8. 用户存在校验功能
def check_username(request):
res = {'code': 100, 'msg': '用户存在'}
username = request.GET.get('username')
user = UserInfo.objects.filter(username=username).first()
if user:
# 用户存在
return JsonResponse(res)
else:
# 用户不存在
res['code'] = 101
res['msg'] = '用户不存在'
return JsonResponse(res)
// username输入框,失去焦点,触发ajax执行
$("#id_username").blur(function (){
$.ajax({
url:'/check_username/?username=' + $(this).val(),
type: 'grt',
success:function (data){
if (data.code == 100){
// 在span的中插入错误信息
{# alert(data.msg) #}
$("#id_username").next().html(data.msg)
}
}
})
})
流程
-注册功能
-1 forms表单的使用:字段类,字段属性
- 作用:数据校验,渲染前端页面,渲染前端页面错误信息
-2 for循环form对象渲染页面
-3 头像上传:头像动态显示
-4 ajax提交注册信息
-ajax上传文件:FormData form表单上传文件:指定编码
-form表单中:var data = $("#id_form").serializeArray()
-jq的循环$.each(data,function(i,v){})
-登录成功: location.href = '/login/'
-错误:渲染再页面上
-定时任务:3s后清空错误信息
-5 后端
-取出前端传进的数据,校验通过后:剔除re_pwd,把头像给avatar
-create_user创建用户
-登录
-布局使用bootstrap实现的
-图片验证码:自己写的
-保存再session中
-前端图片验证码的刷新
-只要src对应的路径发生变化,就会重新加载
-ajax提交登录信息
-用户名,密码和验证码到后端
-登录接口后端
-authenticate 验证用户名密码是否正确(密码是加密的)
-登录成功,调用auth_login(request, user)---》再session中写入数据,cookie中写入
-首页
-布局
-bootstrap的导航条
-左中右的栅格布局
-中间位置上下:上面是轮播图,下面是文章列表
-文章列表:模板语法的for循环 ,media媒体组
-优化字段:点赞,点踩,评论数量
-后端接口
-查询所有文章(缺分页)
-轮播图内容:模板语法写的====》使用ajax加载轮播图
-登录成功显示用户名字,退出
-退出接口
-没有登录显示登录注册
-个人站点
-路由设计:一定要放在最下面
-布局
-base.html:用来做继承
-不同用户顶部颜色不一样,不同人博客样式不一样
-右侧文章:把首页文章复制过来的,最后一行布局推到右侧去了
-左侧:
-标签,分类,随笔档案的渲染---》后端接口查询出
-后端:3个分组查询,TruncMonth后再分组
-前端:通过后端查出来的渲染
-使用inclusion_tag重写了左侧栏的渲染
-左侧的过滤
-3条路由
-汇总成1条路由
-文章详情
-布局:base.html
-文章真正的渲染 |safe 文章内容是html
-点赞点踩的布局:复制粘贴过来的
-点赞点踩的后端
-如果点过了就不能点了
-没登录不能点
-事务
-评论
-render显示根和子评论
-评论ajax提交根评论
-评论的ajax提交子评论
-根评论和子评论的ajax显示
-后台管理
-布局
-文章列表的显示:表格
-删除文章
-新增文章:富文本编辑器,标签,分类的渲染
-处理xss攻击
-修改文章
-修改头像
-修改密码
-django发送邮件
# 可以扩展什么
-首页轮播图ajax加载
-新增标签,分类
-输入密码错误3次,锁定账号,解锁账号
-修改密码:之前用过的密码不能用了
-异地登录,发邮件提醒
-ip地址根之前不同
-文章存到草稿
# 学长的项目
分类:
BBS项目
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!