Django框架 用户登录和注册
备注:用户的登录、注册
models.py
Django默认的认证系统auth_user字段是固定的,如果想存储用户的联系方式,就需要通过继承内置的AbstractUser类,来定义一个Model类。
在settings.py中告诉Django,现在使用新定义的UserInfo表来做用户认证
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): phone = models.CharField(max_length=11,verbose_name='手机号') avatar = models.FileField(upload_to='avatar/',default='avatar/avatar.png')
settings.py
LANGUAGE_CODE = 'zh-hans' #页面显示中文 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static') ] #使用自建的UserInfo表 AUTH_USER_MODEL = 'fault_reporting.UserInfo'
登录
路由 urls.py
from fault_reporting import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/',views.LoginView.as_view()), url(r'^register/',views.RegisterView.as_view()), url(r'^index/',views.index), ]
视图 views.py
auth模块:
authenticate() 提供用户认证功能,需要username和password参数
login() 该函数接收一个HttpRequest对象,以及一个经过认证的user对象。实现用户登录功能,本质上会在后端为用户生成相关session数据
from django.shortcuts import render,HttpResponse,redirect from django.contrib import auth # 登录 class LoginView(views.View): def get(self,request): return render(request,'login.html') def post(self,request): username = request.POST.get("username") pwd = request.POST.get("password") user_obj = auth.authenticate(username=username,password=pwd) if user_obj: auth.login(request,user_obj) return redirect('/index/')
from django.shortcuts import render,HttpResponse,redirect from django import views from django.contrib import auth # 登录 class LoginView(views.View): def get(self,request): return render(request,'login.html') def post(self,request): next_url = request.GET.get("next",'/index/') username = request.POST.get("username") pwd = request.POST.get("password") vcode = request.POST.get("vcode",'').upper() # 判断验证码是否正确 if vcode == request.session.get("v_code"): user_obj = auth.authenticate(username=username,password=pwd) if user_obj: auth.login(request,user_obj) return redirect('/index/') else: return render(request,'login.html',{"error_msg":"用户名或密码错误"}) else: return render(request,'login.html',{"error_msg":"验证码错误"})
from django.shortcuts import render,HttpResponse,redirect from django.core.validators import RegexValidator from django.core.exceptions import ValidationError from fault_reporting import models import random import re # 专门返回验证码图片的视图 def vcode(request): from PIL import Image,ImageDraw,ImageFont def vcode_col(): return random.randint(0,255),random.randint(0,255),random.randint(0,255) # 创建一个图片对象 image_obj = Image.new( 'RGB', (250,35), (192,192,192) ) # 在该图片对象上生成一个画笔对象 draw_obj = ImageDraw.Draw(image_obj) # 加载一个字体对象 font_obj = ImageFont.truetype('static/font/kumo.ttf',34) # 生成验证码 tmp = [] for i in range(5): l = chr(random.randint(97,122)) u = chr(random.randint(65,90)) n = str(random.randint(0,9)) r = random.choice([l,u,n]) # 将选中的字符写在图片上 # draw_obj.text((40*i+20,0),r,fill=vcode_col(),font=font_obj) draw_obj.text((40*i+20,0),r,fill=(0,0,0),font=font_obj) tmp.append(r) v_code = "".join(tmp).upper() # 将生成的验证码保存 request.session["v_code"] = v_code #直接在内存中保存图片,替代io操作 from io import BytesIO f1 = BytesIO() image_obj.save(f1,format="PNG") img_data = f1.getvalue() return HttpResponse(img_data,content_type="image/png")
login.html
登录页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="md-col-6 md-col-offset-3"> <form action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="id_username">用户名</label> <input type="text" class="form-control" id="id_username" name="username" placeholder="用户名"> </div> <div class="form-group"> <label for="id_password">密码</label> <input type="password" class="form-control" id="id_password" name="password" placeholder="密码"> </div> <button type="submit" class="btn-info btn" id="login-btn">登录</button> <a href="/register/" class="btn btn-info" id="register-btn">注册</a> </form> </div> </div> </div> </body> </html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="id_username">用户名</label>
<input type="text" class="form-control" id="id_username" name="username" placeholder="用户名">
</div>
<div class="form-group">
<label for="id_password">密码</label>
<input type="password" class="form-control" id="id_password" name="password" placeholder="密码">
</div>
<div class="form-group">
<label for="id_vcode" style="display: block">验证码</label>
<input type="text" class="form-control" id="id_vcode" name="vcode" placeholder="验证码"
style="width: 250px;display: inline-block">
<img src="/vcode/" style="width: 250px;height: 35px; display: inline-block; float: right">
</div>
<p style="color: red ; font-size: 14px">{{ error_msg }}</p>
<button type="submit" class="btn-info btn" id="login-btn">登录</button>
<a href="/register/" class="btn btn-info" id="register-btn">注册</a>
</form>
</div>
</div>
</div>
</body>
注销功能
# urls.py url(r'^logout/$',views.logout), # views.py def logout(request): auth.logout(request) return redirect('/login/')
注册
forms.py
手动创建一个form 类,
from django import forms from django.core.validators import RegexValidator # 正则 from django.core.exceptions import ValidationError # 抛出异常 from fault_reporting import models import re # 自定义正则函数 def check_email(value): ret = re.match(r'^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$', value) if not ret: raise ValidationError("邮箱格式不正确") else: return value # 注册的form类 class RegisterForm(forms.Form): username = forms.CharField(max_length=12,label='用户名') password = forms.CharField( label='密码', min_length=6, widget=forms.widgets.PasswordInput() ) re_password = forms.CharField( label='确认密码', min_length=6, widget=forms.widgets.PasswordInput() ) phone = forms.CharField( label='手机号', min_length=11, max_length=11, validators=[RegexValidator(r'^1[3-9]\d{9}$',"手机号码格式不正确")] ) email = forms.CharField( label='邮箱', validators=[check_email,] ) # 局部钩子,判断用户名是否被注册 def clean_username(self): # 做用户名不能重复的校验 username = self.cleaned_data.get("username") # 去数据库查重 is_exist = models.UserInfo.objects.filter(username=username) if is_exist: raise ValidationError("该用户已被注册") else: return username # 全局钩子,将多个字段进行比较 def clean(self): password = self.cleaned_data.get("password") re_password = self.cleaned_data.get("re_password") if password != re_password: self.add_error("re_password","密码不一致") raise ValidationError("密码不一致") def __init__(self,*args,**kwargs): super(RegisterForm,self).__init__(*args,**kwargs) # 循环给每个字段加 class: form-control for field in iter(self.fields): self.fields[field].widget.attrs.update({ 'class': 'form-control' })
views.py
from django.shortcuts import render,HttpResponse,redirect from fault_reporting import models from fault_reporting import forms from django.contrib import auth from django.http import JsonResponse from django import views # 注册功能 class RegisterView(views.View): def get(self,request): # 实例化一个form对象 form_obj = forms.RegisterForm() return render(request,'register.html',locals()) def post(self,request): # 创建一个空字典,用于判断当数据有问题时的情况 res = {"code":0} # 给用户提交过来的数据做有效性校验 form_obj = forms.RegisterForm(request.POST) # 调用 is_valid() 方法,就是替我们做数据有效性校验 if form_obj.is_valid(): # 数据没有问题 # 去数据库创建一条记录 # 所有经过校验的数据,forms里没有头像字段 # 创建用户 # 从form表单中把 re_password 数据移除,因为UserInfo表不需要该字段 form_obj.cleaned_data.pop("re_password") # 头像数据 avatar_obj = request.FILES.get("avatar") # models.py 中avatar对应的是FileField, models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_obj) res["url"] = '/login/' else: res["code"] = 1 res["error"] = form_obj.errors # res 传送到 register.html 中 return JsonResponse(res)
register.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4"> <form action="" class="register-form"> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="help-block"></span> </div> {% endfor %} <div class="form-group"> <label for="id_avatar" >头像 <img src="/static/images/default.jpg" id="avatar-img" style="width: 60px; height: 60px"> </label> <!-- accept="image/*" 默认只能选择图片格式 --> <input type="file" id="id_avatar" accept="image/*" class="form-control" style="display: none"> </div> <!-- 使用ajax提交数据时,不要写成 submit类型--> <!-- <button type="submit" class="btn btn-info">注册</button> --> <button type="button" class="btn btn-info" id="submit-btn">注册</button> </form> </div> </div> </div> <script src="/static/jquery-3.3.1.min.js"></script> <script> $("#submit-btn").click(function () { {# var username = $("#id_username").val();#} {# var password = $("#id_password").val();#} {# var re_password = $("#id_re_password").val();#} {# var phone = $("#id_phone").val();#} {# var email = $("#id_email").val();#} {# var csrfToken = $("[name='csrfmiddlewaretoken']").val();#} // 因为注册功能有头像文件,所以用FormData对象提交数据 var fd = new FormData(); fd.append("username",$("#id_username").val()); fd.append("password",$("#id_password").val()); fd.append("re_password",$("#id_re_password").val()); fd.append("phone",$("#id_phone").val()); fd.append("email",$("#id_email").val()); fd.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val()); // avatar 头像 fd.append("avatar",$("#id_avatar")[0].files[0]); $.ajax({ url: '/register/', type: 'post', contentType: false, // 不让jQuery去设置提交数据的类型 processData: false, // 不让jQuery去处理提交的数据 data: fd, success:function (res) { console.log(res); if (res["code"] === 1){ // 说明有字段错误,使用each 循环遍历出来 。 // $.each('要遍历的对象', function(k, v)) $.each(res.error , function (k, v) { console.log(k , v[0]); $("#id_" + k).next().text(v[0]).parent().addClass("has-error"); }) }else { // 没有错误,默认跳转到登录界面 location.href = res.url } } }); }); // 给input 标签绑定获取标点就删除错误提示的动作 $(".register-form input").focus(function () { $(this).next().text("").parent().removeClass("has-error"); }); // 头像预览功能 $("#id_avatar").change(function () { // 取到用户选中的头像文件 var fileObj = this.files[0]; // 新建一个FileReader 对象,从本地磁盘加载文件数据 var fr = new FileReader(); fr.readAsDataURL(fileObj); // 读取文件 fr.onload = function () { // 找到头像预览的 img 标签,把它的src 属性设置成读取的用户选中的图片 $("#avatar-img").attr("src",fr.result) }; }); </script> </body> </html>
settings.py
# 所有用户上传的文件 # 告诉Django项目将用户上传的文件保存在服务端那个目录下 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 告诉Django 用户通过什么前缀来访问刚才定义的目录 MEDIA_URL = '/media/'
urls.py
url(r'^register/',views.RegisterView.as_view()), url(r'^vcode/',views.vcode), # 给用户上传的文件做一个对应关系 url(r'^media/(?P<path>.*)',serve,{"document_root":settings.MEDIA_ROOT}),

浙公网安备 33010602011771号