Python学习之路―2018/7/11

匿名 (未验证) 提交于 2019-12-02 22:56:40

Python学习之路―2018/7/10

login.html

<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1">     <title>博客开发系统</title>     <link rel="stylesheet" href="/static/blog/css/bootstrap.min.css">     <link rel="icon" href="/static/blog/image/favicon.ico">     <style type="text/css">         body {             background: url(../static/blog/image/bk.jpeg) no-repeat;             background-size: 100%;             overflow: hidden;         }         h3 {             padding: 10px;             border-bottom: 1px solid #ddd;         }     </style> </head> <body> <div class="row">     <div class="col-md-4 col-md-offset-4" style="margin-top: 100px">         <form>             {% csrf_token %}             <div class="well">                 <h3>登录</h3>                 <div class="form-group">                     <label for="username">用户名</label>                     <input type="text" class="form-control" id="username" placeholder="用户名" autocomplete="off">                 </div>                 <div class="form-group">                     <label for="password">密码</label>                     <input type="password" class="form-control" id="password" placeholder="密码" autocomplete="off">                 </div>                 <div class="row form-group">                     <div class="col-md-6">                         <label for="password">验证码</label>                         <input type="text" class="form-control" id="verify_code" placeholder="请输入验证码" autocomplete="off">                     </div>                     <div class="col-md-6">                         <label for="picture"></label>                         <img src="/verify_code" style="height: 40px;width: 183px" id="verify_code_img">                     </div>                 </div>                 <div class="form-group">                     <span id="error"></span>                     <input type="button" class="btn btn-success form-control login-btn" value="登录">                 </div>             </div>         </form>     </div> </div> </body> <script type="text/javascript" src="/static/blog/js/jquery-3.3.1.min.js"></script> <script type="text/javascript">     $("#verify_code_img").click(function () {         $(this)[0].src += "?"     });      $(".login-btn").click(function () {         $.ajax({             url: "/login/",             type: "POST",             data: {                 "username": $("#username").val(),                 "password": $("#password").val(),                 "verify_code": $("#verify_code").val(),             },             success:function (data) {                     if(data["user"]){                         location.href = "/index/"                     }                     else {                         $("#error").html(data["msg"]).css({"color": "red"})                     }                 }         })     }) </script> </html>

views.py

from django.shortcuts import render, HttpResponse from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.contrib import auth from blog.utils.verify_code import *   @csrf_exempt def login(request):     if request.method == "GET":         return render(request, "login.html")     elif request.method == ‘POST‘:         username = request.POST.get("username")         password = request.POST.get("password")         verify_codes = request.POST.get("verify_code")         verify_code_str = request.session.get("verify_code_str")         response = {"user": None, "msg": None}          if verify_codes.upper() == verify_code_str.upper():             user = auth.authenticate(username=username, password=password)             if user:                 auth.login(request, user)                 response["user"] = user.username             else:                 response["msg"] = "用户名或者密码错误!"         else:             response["msg"] = "验证码错误!"          return JsonResponse(response)   def verify_code(request):     data = get_verify_code(request)     return HttpResponse(data)   def index(request):     return render(request, "index.html")

verify_code.py

import random from PIL import Image, ImageDraw, ImageFont, ImageFilter from io import BytesIO   def random_color():     color = (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))     return color   def random_color2():     color = (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))     return color   def random_char():     """     随机数/字母     """     random_num = str(random.randint(0, 9))     random_low = chr(random.randint(97, 122))  # a~z     random_upper = chr(random.randint(65, 90))  # A~Z     random_chars = random.choice([random_num, random_low, random_upper])     return random_chars   def get_verify_code(request):     """     动态生成验证码     """     image = Image.new("RGB", (183, 40), (255, 255, 255))     image_font = ImageFont.truetype("static/blog/font/Arial.ttf", 32)     draw = ImageDraw.Draw(image)      # 给每个坐标填充颜色,填充噪点     for x in range(183):         for y in range(40):             draw.point((x, y), fill=random_color())      verify_code_str = ""     for i in range(5):         random_chars = random_char()         verify_code_str += random_chars         draw.text((20+i*30, 0), random_chars, font=image_font, fill=random_color2())     image = image.filter(ImageFilter.BLUR)  # 模糊处理          # 放到磁盘中,但是速度比较慢,推荐放在内存中     # with open("verify_code.png", "wb") as f:     #     image.save(f)     #     # with open("verify_code.png", "rb") as f:     #     data = f.read()          request.session["verify_code_str"] = verify_code_str     f = BytesIO()     image.save(f, "png")     data = f.getvalue()      return data

登录界面效果如下图所示:

  1. 一次请求伴随着多次静态的请求,例如css文件的请求
  2. 利用PIL模块动态制作验证码
  3. 验证码使用session存储(session首先会生成一个sessionid,接着会在cookie中存储sessionid,最后想session表中插入数据,包括sessionid以及session内容,一个浏览器对应一条session数据)
  4. 验证码的刷新,每次点击验证功能图片时,在其src的末尾加上 ?,这样每次点击图片便会刷新验证码
  5. 遵循“高耦合低内聚”原则,将动态生成验证码的功能存放在另一个py文件中

首先需要安装social-auth-app-django模块

pip install social-auth-app-django

接着从https://github.com/GeeTeam/gt-python-sdk/ 下载geetest.py ,将其放入项目中

login.html

<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1">     <title>博客开发系统</title>     <link rel="stylesheet" href="/static/blog/css/bootstrap.min.css">     <link rel="stylesheet" href="/static/blog/css/blog.css">     <link rel="icon" href="/static/blog/image/favicon.ico">     <style type="text/css">         body {             background: url(../static/blog/image/bk.jpeg) no-repeat;             background-size: 100%;             overflow: hidden;         }     </style> </head> <body> <div class="row">     <div class="col-md-4 col-md-offset-4" style="margin-top: 100px">         <form>             {% csrf_token %}             <div class="well">                 <div class="login-head">                     {% block head %}                         <h3 style="display: inline-block">登录</h3>                         <a id="register">立即注册</a>                     {% endblock head %}                 </div>                 {% block content %}}                     <div class="form-group">                         <label for="username">用户名</label>                         <input type="text" class="form-control" id="username" placeholder="用户名" autocomplete="off">                     </div>                     <div class="form-group">                         <label for="password">密码</label>                         <input type="password" class="form-control" id="password" placeholder="密码" autocomplete="off">                     </div>                 {% endblock content %}                 <div class="form-group">                     <span id="error"></span>                     {% block submit %}                         <input type="button" class="btn btn-success form-control login-btn" value="登录">                     {% endblock submit %}                 </div>                 <div id="popup-captcha"></div>             </div>         </form>     </div> </div> </body> <script type="text/javascript" src="/static/blog/js/jquery-3.3.1.min.js"></script> <script src="https://static.geetest.com/static/tools/gt.js"></script> <script src="/static/blog/js/blog.js"></script> </html>

blog.css

a:hover {     cursor: pointer;     text-decoration: none; }  .login-head {     margin-bottom: 10px;     border-bottom: 1px solid #ddd; }  .login-head a {     margin-left: 287px;     color: #5cb85c; }

blog.js

var handlerPopup = function (captchaObj) {     // 成功的回调     captchaObj.onSuccess(function () {         var validate = captchaObj.getValidate();         $.ajax({             url: "/login/", // 进行二次验证             type: "post",             dataType: "json",             data: {                 username: $(‘#username‘).val(),                 password: $(‘#password‘).val(),                 geetest_challenge: validate.geetest_challenge,                 geetest_validate: validate.geetest_validate,                 geetest_seccode: validate.geetest_seccode             },             success: function (data) {                 if(data["user"]){                     location.href = "/index/"                 }                 else {                     $("#error").html(data["msg"]).css({"color": "red"})  //提示错误信息                 }             }         });     });     $(".login-btn").click(function () {         captchaObj.show();     });     // 将验证码加到id为captcha的元素里     captchaObj.appendTo("#popup-captcha");     // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html }; // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback) $.ajax({     url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存     type: "get",     dataType: "json",     success: function (data) {         // 使用initGeetest接口         // 参数1:配置参数         // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件         initGeetest({             gt: data.gt,             challenge: data.challenge,             product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效             offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注             // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config         }, handlerPopup);     } });

views.py

from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from blog.utils.geetest import GeetestLib from django.contrib import auth  @csrf_exempt def login(request):     if request.method == "GET":         return render(request, "login.html")     elif request.method == ‘POST‘:         # 滑动验证码,利用geetest插件         gt = GeetestLib(pc_geetest_id, pc_geetest_key)         status = request.session[gt.GT_STATUS_SESSION_KEY]         username = request.POST.get("username")         password = request.POST.get("password")         response = {"user": None, "msg": None}         if status:             user = auth.authenticate(username=username, password=password)             if user:                 auth.login(request, user)                 response["user"] = user.username             else:                 response["msg"] = "用户名或者密码错误!"         return JsonResponse(response) 

展示效果:

首先在项目中创建media文件夹,在media目录下创建avatars文件夹,这个文件主要存放用户上传的文件

Django有两种静态文件,分别是static以及media,static主要存放js,css,img等服务器自己的文件,而media主要存放用户上传的文件。

我们需要配置settings中的media信息MEDIA_ROOT = os.path.join(BASE_DIR, "media"),配置好以后每次用户上传的文件Django会存放到/media/avatars目录下,如果用户没有创建avatars目录,Django会自动创建该目录

register.html

{% extends "login.html" %} {% block head %}     <h3 style="text-align: center">注册</h3> {% endblock %}  {% block content %}     {% for foo in form %}         <div class="form-group">             <label for="{{ foo.auto_id }}">{{ foo.label }}</label>             {{ foo }}             <span class="error pull-right"></span>         </div>     {% endfor %}     <div class="form-group">         <label for="avatar">             头像             <img src="/static/blog/avatars/default.gif" id="avatar_img">         </label>         <input type="file" id="avatar" style="display: none">     </div> {% endblock content %}  {% block submit %}     <input type="button" class="btn btn-success form-control" value="注册"> {% endblock submit %}

blog.css

#avatar_img {     width: 60px;     height: 60px;     margin-left: 20px; } .error{     color: red; }

blog.js

// input标签内容改变事件,即头像上传 $("#avatar").change(function () {    var file = $(this)[0].files[0];  // 获取图片路径    var reader = new FileReader();  // 文本阅读器    reader.readAsDataURL(file);  // 读取文件的路径    reader.onload = function () {        $("#avatar_img").attr("src", reader.result)  // 将图片的路径写入img标签的src中    } }); // 注册用户 $(".reg-btn").click(function () {     var formdata = new FormData();     var request_list = $("#form").serializeArray();  // 包含form表单中所有标签的name和value     $.each(request_list, function (index, data) {         formdata.append(data.name, data.value)         /*         相当于:             formdata.append("username",$("#id_username").val());             formdata.append("password",$("#id_password").val());             formdata.append("re_password",$("#id_re_password").val());             formdata.append("email",$("#id_email").val());         */     });     formdata.append("avatar", $("#avatar")[0].files[0]);     $.ajax({         url: "/register/",         type: "POST",         contentType: false,  // 必加参数         processData: false,  // 必加参数         data: formdata,         success: function (data) {             $("span.error").html("");  // 移除上一次的错误信息             $(".has-error").removeClass("has-error");  // 移除上一次错误信息框样式             if (data.user) {                 location.href = "/login/"  //注册成功则返回登录界面             }             else {                 var error_list = data.msg;                 $.each(error_list, function (field, error) {                     if (field == "__all__"){                         $("#id_re_password").next().html(error[0]).parent().addClass("has-error")  // 对全局钩子的单独处理                     }                     $("#id_" + field).next().html(error[0]);  // 找寻对应id的input标签后面的span标签,将错误信息写入                     $("#id_" + field).parent().addClass("has-error");  // 给含有错误信息的input标签添加css样式                 })             }         }     }) });

my_forms.py

from django import forms from .models import * from django.core.exceptions import ValidationError  wid_1 = forms.widgets.TextInput(attrs={"class": "form-control", "autocomplete": "off"}) wid_2 = forms.widgets.PasswordInput(attrs={"class": "form-control"})   class UserForm(forms.Form):     username = forms.CharField(max_length=32, label="用户名", widget=wid_1,                                error_messages={"required": "该字段不能为空!"})     password = forms.CharField(min_length=8, label="密码", widget=wid_2,                                error_messages={"min_length": "密码长度至少8位!",                                                "required": "该字段不能为空!"})     re_password = forms.CharField(min_length=8, label="确认密码", widget=wid_2,                                   error_messages={"min_length": "密码长度至少8位!",                                                   "required": "该字段不能为空!"})     email = forms.EmailField(label="邮箱", widget=wid_1,                              error_messages={"invalid": "邮箱格式错误!"})      def clean_username(self):         username = self.cleaned_data.get("username")         user = User.objects.filter(username=username)         if not user:             return username         else:             raise ValidationError("该用户已经被注册!")      def clean(self):         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("两次密码不一致!")

views.py

from blog.my_forms import *  def register(request):     user = UserForm()     if request.method == "GET":         return render(request, "register.html", locals())     elif request.method == "POST":         response = {"user": None, "msg": None}         user = UserForm(request.POST)         if user.is_valid():             username = user.cleaned_data.get("username")             response["user"] = username             password = user.cleaned_data.get("password")             email = user.cleaned_data.get("email")             avatar_obj = request.FILES.get("avatar")             if avatar_obj:  # 如果用户上传了头像,则创建用户时将其传入,没有则传递其他填写的信息                 user_obj = User.objects.create_user(username, email, password, avatar=avatar_obj)             else:                 user_obj = User.objects.create_user(username, email, password)         else:             response["msg"] = user.errors  # 包含了所有的错误信息          return JsonResponse(response)

展示效果:

  1. label标签添加for属性(内容为某个标签的id),点击label相当于点击for属性中的标签。利用此特性,将文件上传input隐藏,添加一个label与其绑定,接着在label中添加img,这样点击图片便可以实现文件上传。
  2. 文件的路径通过$(xx)[0].files[0]来获取
  3. 实现上传头像的预览主要分为三步,第一获取图片的路径,第二步读取图片的路径,第三步将图片路径写入img标签中
  4. 文本阅读器中的readAsDataURL是异步的,所以会出现还没有读取图片路径就写入了,从而产生一个unknow对象,所以需要用到onload方法,onload会在所有资源加载完之后运行。
  5. 上传文件时需要用到FormData,使用FormData时必须加ajax中加contentType: false,processData: false这两个参数
  6. 向FormData中插入数据的时候,可以通过$("xxx").serializeArray()(包含了form表单中所有输入框的name以及value)循环添加,减少代码量
  7. Media的配置,将服务器的文件存放在static中,用户上传的文件存放在media/avatars中

原文:https://www.cnblogs.com/ExBurner/p/9297363.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!