BBS项目(三)
2022-03-14 22:43
507 查看
[toc]
BBS项目(三)
注册forms类编写局部钩子和全局钩子
'''forms校验''' from django import forms from django.forms import widgets from blog import models from django.core.exceptions import ValidationError '''注册校验,写入样式''' class RegisterForm(forms.Form): # 名称校验 username = forms.CharField( max_length=18, min_length=3,label='用户名', error_messages={'required': '该字段必填', 'max_length': '名字过长,不能超过18个字符', 'min_length': '名字过短,不能少于三个字符'}, widget=widgets.TextInput(attrs={'class': 'form-control'}) ) # 密码校验 password = forms.CharField( max_length=18, min_length=3,label='密码', error_messages={'required': '该字段必填', 'max_length': '密码过长,不能超过18个字符', 'min_length': '密码过短,不能少于三个字符'}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}) ) re_password = forms.CharField( max_length=18, min_length=3,label='确认密码', error_messages={'required': '该字段必填', 'max_length': '密码过长,不能超过18个字符', 'min_length': '密码过短,不能少于三个字符'}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}) ) # 邮箱 email = forms.EmailField( error_messages={'required': '该字段必填', 'invalid': '邮箱格式不正确'},label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}) ) # 局部钩子,校验用户是否存在 def clean_username(self): username = self.cleaned_data.get('username') # 拿到数据判断用户在不在表中 user = models.UserInfo.objects.filter(username=username).first() if user: # 用户存在抛异常 raise ValidationError('该用户以存在') else: return username # 全局钩子,校验密码是否一致 def clean(self): password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') if not password == re_password: raise ValidationError('两次密码不一致')
注册功能前端
# 发送ajax请求,使用的Formdata #form标签.serializeArray() # 整体代码 $('#id_submit').click(function () { let formdata = new FormData() formdata.append('myfile', $('#myfile')[0].files[0]) //方案一 /* formdata.append('username',$('#id_username').val()) formdata.append('password',$('#password').val()) formdata.append('re_password',$('#id_re_password').val()) formdata.append('email',$('#id_email').val()) */ //方案二 let form_data = $('#my_form').serializeArray() //console.log(form_data) $.each(form_data, function (index, element) { //console.log(index) //console.log(element) formdata.append(element.name, element.value) }) //console.log(formdata.get('username')) $.ajax({ url: '/register/', method: 'post', contentType: false, processData: false, data: formdata, success: function (data) { console.log(data) if (data.status == 100) { location.href = data.next_url //location.href='/login/' } else { $.each(data.msg, function (key, value) { //console.log('#id_'+key) if (key == '__all__') { $('#id_error').html(value[0]) } else { //取到input标签的下一个 //$('#id_'+key).next().html(value[0]) //链式调用 //$('#id_'+key).parent().addClass('has-error') //链式调用 $('#id_' + key).next().html(value[0]).parent().addClass('has-error') } }) //加了一个定时器,3s以后干某些事 setTimeout(function () { //清除红色框 $('.form-group').removeClass('has-error') //清空所有错误信息 $('.error').html('') }, 3000) } } }) })
注册功能后端
def register(request): if request.method == 'GET': register_form = RegisterForm() return render(request, 'register.html', context={'form': register_form}) elif request.method == 'POST': response = {'status': 100, 'msg': None} register_form = RegisterForm(request.POST) if register_form.is_valid(): # 数据校验通过 # 可能传头像,可能没传头像 clean_data=register_form.cleaned_data print(clean_data) my_file=request.FILES.get('myfile') if my_file: # 传了头像 # FileField字段类型直接接受一个文件对象, # 它会把文件存到upload_to='avatar/',然后把路径存到数据库中 # 相当于with open 打开文件,把文件存到avatar路径下,把路径赋值给avatar这个字段 clean_data['avatar']=my_file clean_data.pop('re_password') models.UserInfo.objects.create_user(**clean_data) response['msg']='恭喜你,注册成功' response['next_url']='/login/' else: response['status']=101 response['msg'] = register_form.errors return JsonResponse(response)
注册功能前端错误渲染
success: function (data) { console.log(data) if (data.status == 100) { location.href = data.next_url } else { $.each(data.msg, function (key, value) { if (key == '__all__') { $('#id_error').html(value[0]) } else { $('#id_' + key).next().html(value[0]).parent().addClass('has-error') } }) setTimeout(function () { //清除红色框 $('.form-group').removeClass('has-error') //清空所有错误信息 $('.error').html('') }, 3000) } }
登录页面搭建
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <script src="/static/jquery-3.3.1.js"></script> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">登录功能</h3> </div> <div class="panel-body"> <form id="my_form"> {% csrf_token %} <div class="form-group"> <label for="">用户名</label> <input type="text" id="id_username" class="form-control"> <span class="danger pull-right error"></span> </div> <div class="form-group"> <label for="">密码</label> <input type="text" id="id_password" class="form-control"> <span class="danger pull-right error"></span> </div> <div class="form-group"> <label for="">验证码</label> <div class="row"> <div class="col-md-6"> <input type="text" id="id_code" class="form-control"> </div> <div class="col-md-6"> <img src="/get_code/" alt="" height="35px" width="300px"> </div> </div> </div> <div class="text-center"> <input type="button" value="登录" class="btn btn-warning" id="id_submit"><span class="danger error" id="id_error" style="margin-left: 10px"></span> </div> </form> </div> </div> </div> </div> </div> </body> </html>
验证码
- 生成验证码的模块:https://www.jb51.net/article/153863.html
- 集成第三方,极验滑动验证,腾讯验证码(sdk)
- 手写验证码
'''手写验证码模板''' def get_random(): return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def get_code(request): # 最终方案 # img = Image.new('RGB', (300, 30), get_random()) img = Image.new('RGB', (250, 30), (250, 250, 250)) # 第一个参数,是文字格式的文件,ttf格式,第二个参数是文字大小 img_font = ImageFont.truetype('./static/font/ss.TTF', 20) # 拿到一个画板对象,把图片放到画板上,指定写的字的字体是什么 img_draw = ImageDraw.Draw(img) # 在画板上写文字 # 随机生成5位 小写字母,大写字母,和数字 code = '' for i in range(5): low_char = chr(random.randint(97, 122)) up_char = chr(random.randint(65, 90)) number_char = str(random.randint(0, 9)) res = random.choice([low_char, up_char, number_char]) code += res img_draw.text((20 + i * 40, 0), res, fill=get_random(), font=img_font) print(code) request.session['code'] = code # 画点和线 # 画线和点圈 width = 250 height = 30 for i in range(5): x1 = random.randint(0, width) x2 = random.randint(0, width) y1 = random.randint(0, height) y2 = random.randint(0, height) # 在图片上画线 img_draw.line((x1, y1, x2, y2), fill=get_random()) for i in range(20): # 画点 img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random()) x = random.randint(0, width) y = random.randint(0, height) # 画弧形 img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random()) bytes_io = BytesIO() img.save(bytes_io, 'png') # 写到内存中,需要传format,图片格式 return HttpResponse(bytes_io.getvalue()) # 把内容读出来
点击刷新验证码
$('#id_img').click(function () { let img_url = $('#id_img')[0].src $('#id_img')[0].src = img_url + '?' })
登录功能前后端
前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> <script src="/static/element/jQuery3.4.js"></script> <script src="/static/element/bootstrap.min.js"></script> <link rel="stylesheet" href="/static/element/bootstrap.min.css"> <style> .danger { color: tomato; } </style> </head> <body> <div class="container"> <div class="row" style="margin-top: 15px"> <div class="col-md-8 col-md-offset-2"> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">登录</h3> </div> <div class="panel-body"> <form action="" id='my_form' method="post"> {% csrf_token %} {# 用户名、密码、验证码 #} <div class="form-group"> {# 后端blog_forms.py获取label #} {# item.auto_id获取当前id自动聚焦 #} <label for="{{ item.auto_id }}">用户名</label> <input type="text" id="id_username" class="form-control"> <span class="danger pull-right error"></span> </div> <div class="form-group"> {# 后端blog_forms.py获取label #} {# item.auto_id获取当前id自动聚焦 #} <label for="{{ item.auto_id }}">密码</label> <input type="password" id="id_password" class="form-control"> <span class="danger pull-right error"></span> </div> <div class="form-group"> <label for="{{ item.auto_id }}">验证码</label> <div class="row"> <div class="col-md-6"> <input type="text" id="id_code" class="form-control"> </div> <div class="col-md-6"> <img src="/get_code/" alt="验证码图片" height="35px" width="300px" id="id_imgcode"> </div> </div> </div> {# 提交,使用form表单,类型要写成button,如果是submit的话会触发表单提交 #} <div class="text-center"> <input type="button" class="btn btn-info" id="id_submit" value="登录"> <a href="/admin/" class="btn btn-warning table-hover">退出</a> <span class="danger error" id="id_error" style="margin-left: 20px"></span> </div> </form> </div> </div> </div> </div> </div> </body> <script> {# 点击变图,发送不同请求 #} $('#id_imgcode').click(function () { var img_url = $('#id_imgcode')[0].src $('#id_imgcode')[0].src = img_url + '?' + Math.floor(Math.random() * 100) }) {#提交数据#} $('#id_submit').click(function () { $.ajax({ url: '/login/', method: 'post', data: { username: $('#id_username').val(), password: $('#id_password').val(), code: $('#id_code').val(), csrfmiddlewaretoken: '{{csrf_token}}', }, success: function (data) { if (data.status == 100) { {#location.href='/index/'#} location.href = data.url } else { $('#id_error').html(data.msg) {#alert($('#id_error').html(data.msg)[0].innerText)#} setTimeout(function (){ $('.error').html('') },3000) } }, }) }) </script> </html>
后端
# 验证码 def get_code(request): # width = 300 # height = 30 # image = Image.new('RGB', (width, height), (255, 255, 255)) # # with open('code.png','wb')as code_f: # image.save(code_f) # with open('./code.png','rb')as f: # res = f.read() # return HttpResponse(res) from PIL import Image, ImageDraw, ImageFont from io import BytesIO # 随机字母: def rndChar(): return chr(random.randint(65, 90)) # 随机颜色1: def rndColor(): return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)) # 随机颜色2: def rndColor2(): return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)) width = 300 height = 50 image = Image.new('RGB', (width, height), (255, 255, 255)) # 创建Font对象: # 创建ttf格式文件 font = ImageFont.truetype(r'E:\BBS\static\code\arial.ttf', 36) # 创建Draw对象: draw = ImageDraw.Draw(image) # 填充每个像素: for x in range(width): for y in range(height): draw.point((x, y), fill=rndColor()) # 输出文字: code = '' for t in range(5): '''如果想使用数字和大小写字母拼接的验证码可以用chr(),用ASCII,然后随机获取''' res = rndChar() code += res draw.text((60 * t + 10, 10), res, font=font, fill=rndColor2()) print(code) '''将验证码写入code,需要注意的是,如果同一个浏览器打开两个页面,那么原来的session_data就会被覆盖掉,如果打开另外一个浏览器就会重新生成新的session_data记录''' request.session['code'] = code # 模糊: # image = image.filter(ImageFilter.BLUR) # 存入硬盘,读出 # image.save('code.jpg', 'jpeg') # with open('./code.jpg', 'rb') as f: # res = f.read() # 存入内存,读出 bytes_io = BytesIO() image.save(bytes_io, 'png') return HttpResponse(bytes_io.getvalue()) # 登录 def login(request): if request.method == 'GET': return render(request, 'login.html') else: response = {'status': 100, 'msg': None} username = request.POST.get('username') password = request.POST.get('password') code = request.POST.get('code') if not code: response['status'] = 101 response['msg'] = '验证码不能为空' if code.lower() == request.session.get('code').lower(): user = authenticate(username=username, password=password) if user: auth.login(request,user) response['msg'] = '登录成功' response['url'] = '/index/' else: response['status'] = 102 response['msg'] = '用户名或密码错误' else: response['status'] = 101 response['msg'] = '验证码错误' return JsonResponse(response)
首页页面搭建
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Blog-index</title> <script src="/static/element/jQuery3.4.js"></script> <script src="/static/element/bootstrap.min.js"></script> <link rel="stylesheet" href="/static/element/bootstrap.min.css"> <style> footer#footer { padding-top: 32px; padding-bottom: 32px; display: flex; flex-direction: column; align-items: center; color: #888; background-color: #f3f3f3; font-size: 13px; font-weight: 400; text-align: center; } footer { display: block; } body { font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-weight: normal; background-color: #f9f9f9; } </style> </head> <body> <div class="container-fluid"> {# 头部 #} <div class="head"> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <li class="navbar-branding"> <a href="/index/" class="navbar-brand" title="开发者的网上家园" role="banner"> 博客园 </a> </li> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="/index/">首页 <span class="sr-only">(current)</span></a></li> <li><a href="https://news.cnblogs.com/">新闻</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">发现 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="https://zzk.cnblogs.com/">找找看</a></li> <li><a href="#">收藏</a></li> <li><a href="https://www.lagou.com/">招聘</a></li> <li role="separator" class="divider"></li> <li><a href="/login/">个人园子</a></li> </ul> </li> </ul> {% if request.user.is_authenticated %} <ul class="nav navbar-nav navbar-right"> <li><a href="#">{{ request.user.username }}</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="/">个人主页</a></li> <li><a href="#">设置</a></li> <li><a href="admin">后台管理</a></li> <li role="separator" class="divider"></li> <li><a href="/logout/">退出</a></li> </ul> </li> </ul> {% else %} <ul class="nav navbar-nav navbar-right"> <li><a href="/login/">登录</a></li> <li><a href="/register/">注册</a></li> </ul> {% endif %} </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> </div> {# 主体 #} <div class="body row"> {# 左侧 #} <div class="col-md-2"> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> {# 中间 #} <div class="col-md-7"> <div class="lunbotu"> <div id="carousel-example-generic" class="carousel slide" data-ride="carousel"> <!-- Indicators --> <ol class="carousel-indicators"> <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li> <li data-target="#carousel-example-generic" data-slide-to="1"></li> <li data-target="#carousel-example-generic" data-slide-to="2"></li> </ol> <!-- Wrapper for slides --> <div class="carousel-inner" role="listbox"> {% for banner in banner_list %} {% if forloop.first %} <div class="item active"> <img src="{{ banner.url }}" alt="首页图"> {# <div class="carousel-caption">#} {# {{ banner.name }}#} {# </div>#} </div> {% else %} <div class="item "> <img src="{{ banner.url }}" alt="首页图"> </div> {% endif %} {% endfor %} </div> <!-- Controls --> <a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev"> <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span> <span class="sr-only">Previous</span> </a> <a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next"> <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> <span class="sr-only">Next</span> </a> </div> </div> {# 文章 #} <div class="article"> {% for article in article_list %} {{ article.title }} {% endfor %} </div> </div> {# 右侧 #} <div class="col-md-3"> <ul class="list-group"> <li class="list-group-item">Cras justo odio</li> <li class="list-group-item">Dapibus ac facilisis in</li> <li class="list-group-item">Morbi leo risus</li> <li class="list-group-item">Porta ac consectetur ac</li> <li class="list-group-item">Vestibulum at eros</li> </ul> </div> </div> {# 页脚 #} <div> <br><br><br><br><br><br><br><br><br><br> <br><br><br><br><br><br><br><br><br><br> </div> <div class="footer text-center" style="background-color: rgb(243,243,243)"> <footer id="footer" class="footer"> <div id="friend_link" class="link-list friend-link"> 友情链接: <a href="//www.aliyun.com" target="_blank">阿里云</a> <a href="//cloud.tencent.com" target="_blank">腾讯云</a> <a href="//www.huaweicloud.com" target="_blank">华为云</a> <a href="//cloud.baidu.com" target="_blank">百度云</a> <a href="//www.jdcloud.com" target="_blank">京东云</a> <a href="http://www.ucancode.com/" target="_blank">工控组态源码</a> <a href="//www.shanhaibi.com/" target="_blank">山海鲸可视化</a> <a href="http://www.gcpowertools.com.cn" target="_blank">葡萄城控件</a><a href="//www.chinaz.com/" target="_blank">站长之家</a><a href="http://dev.yesky.com" target="_blank">天极网</a><a href="//wetest.qq.com/?from=links_cnblogs" target="_blank">腾讯WeTest</a> </div> <div class="footer-splitter"></div> <div id="footer_bottom"> <div class="poweredby">Powered by .NET 6 on Kubernetes</div> <div class="about"><a href="//about.cnblogs.com/">关于博客园</a><a href="//about.cnblogs.com/contact">联系我们</a><a href="//about.cnblogs.com/ad">广告服务</a><a href="//about.cnblogs.com/brandzone">专区合作</a><span>©2004-2022</span> </div> <div class="report-contact">举报电话:0571-88079867,举报邮箱:contact@cnblogs.com <a href="http://www.shjbzx.cn" target="_blank"><img src="/images/jblogo.png?v=20200730" alt=""></a></div> </div> </footer> </div> </div> </body> </html>
相关文章推荐
- BBS 项目(六)
- BBS项目笔记之九:js实现动态的导航
- 尚学堂科技_马士兵_JDBC_MySQL_BBS项目视频教程
- 记录一下写香樟BBS项目时使用githubAPI进行第三方验证登录时的巨坑
- 今天要看完BBS项目视频!NOTIME!!!
- 分享java web 期末项目实验源码20套,BBS论坛,ERP管理系统,OA自动化等等
- 马士兵讲jsp项目--BBS项目分析笔记
- 传智播客bbs项目,在Properties文件中配置BeanFactory
- 最新(Java全栈) java BBS论坛开发项目实战
- 我写马士兵BBS项目时一个哭笑不得的错误
- BBS项目 replyDetail回复之后读秒跳转
- 小型BBS项目1--4月25号学习总结
- python 自动化之路 day 20 Django进阶/BBS项目【一】
- 小型BBS项目--4月26号学习总结
- Django学习笔记(19)——BBS+Blog项目开发(3)细节知识点补充
- 马士兵讲jsp项目--BBS项目分析笔记
- ASP.NET Core 开源论坛项目 NETCoreBBS
- 从BBS项目的开发谈起,回顾毕业后这一年
- Struts的BBS项目总结
- BBS项目笔记之一:Spring+Hibernate实现Dao层