本项目采用django自带的数据库
项目文件

models.py

1 from django.db import models 2 from django.contrib.auth.models import AbstractUser 3 # Create your models here. 4 5 class Userinfo(AbstractUser): 6 email = models.EmailField()
settings.py

1 AUTH_USER_MODEL='app01.Userinfo'#自定义认证表 2 LOGIN_URL='/login/'#指定认证失败的跳转页面 3 4 STATIC_URL = '/static/' 5 STATICFILES_DIRS=[ 6 os.path.join(BASE_DIR,'static_files') 7 ]
views.py

1 from django.shortcuts import render, HttpResponse, redirect
2 from django import forms#django的form组件
3 from django.core.validators import RegexValidator#form组件中的validators自带校验器
4 from django.core.exceptions import ValidationError#错误
5 import re
6 from app01 import models
7 from django.http import JsonResponse#Json响应数据类型
8 from django.urls import reverse#url反向解析
9 from django.contrib import auth#django内置认证系统
10 from django.contrib.auth.decorators import login_required#认证装饰器
11
12
13 # Create your views here.
14 #自定义校验函数
15 def name_valid(value):
16 name_re = re.compile(r'^[\d]+')
17 ret = name_re.match(value)
18 if ret:
19 raise ValidationError('用户名不能以数字开头!')
20
21 #注册form组件
22 class RegisterForm(forms.Form):
23 name = forms.CharField(
24 required=True,
25 label='用户名:',
26 min_length=6,
27 max_length=32,
28 help_text='只能有字母数字下划线组成,且不能以数字开头,长度6到32位!',
29 # initial='admin123_',
30 error_messages={
31 'required': '用户名不能为空!',
32 'min_length': '长度不能少于6位!',
33 'max_length': '长度不能超过32位!',
34 },
35 validators=[RegexValidator(r'^[a-zA-Z0-9_]+$', '用户名只能包含字母数字下划线!'), name_valid],
36 )
37 password = forms.CharField(
38 required=True,
39 label='密码:',
40 min_length=6,
41 max_length=32,
42 help_text='长度6到32位!',
43 initial='',
44 error_messages={
45 'required': '密码不能为空!',
46 'min_length': '长度不能少于6位!',
47 'max_length': '长度不能超过32位!',
48 },
49 widget=forms.PasswordInput(render_value=True),
50 )
51 r_password = forms.CharField(
52 required=True,
53 label='确认密码:',
54 min_length=6,
55 max_length=32,
56 help_text='长度6到32位!',
57 initial='',
58 error_messages={
59 'required': '密码不能为空!',
60 'min_length': '长度不能少于6位!',
61 'max_length': '长度不能超过32位!',
62 },
63 widget=forms.PasswordInput(render_value=True),
64 )
65 email = forms.EmailField(
66 required=True,
67 label='邮箱',
68 error_messages={
69 'required': '邮箱不能为空!',
70 'invalid':'邮箱格式不正确!'
71 }
72 )
73 #全部字段添加样式
74 def __init__(self, *args, **kwargs):
75 super(RegisterForm, self).__init__(*args, **kwargs)
76 for field in self.fields:
77 self.fields[field].widget.attrs.update({'class': 'form-control'})
78 #局部钩子
79 def clean_name(self):
80 pass
81 return self.cleaned_data.get('name')
82
83 def clean_password(self):
84 pass
85 return self.cleaned_data.get('password')
86
87 def clean_r_password(self):
88 pass
89 return self.cleaned_data.get('r_password')
90
91 def clean_email(self):
92 pass
93 return self.cleaned_data.get('email')
94 #全局钩子
95 def clean(self):
96 password = self.cleaned_data.get('password')
97 r_password = self.cleaned_data.get('r_password')
98 if password != r_password:
99 self.add_error('r_password', '两次密码不一致!')
100 raise ValidationError('两次密码不一致!')
101 else:
102 return self.cleaned_data
103
104
105 # 注册
106 def register(request):
107 if request.method == 'GET':
108 register_obj = RegisterForm()
109 return render(request, 'register.html', {'register_obj': register_obj})
110 elif request.method == 'POST':
111 data = request.POST
112 # print(data)
113 register_obj = RegisterForm(data)
114 if register_obj.is_valid():
115 user_obj = register_obj.cleaned_data
116 print(user_obj)
117 username = user_obj.get('name')
118 password = user_obj.get('password')
119 email = user_obj.get('email')
120
121 if not models.Userinfo.objects.filter(username=username).exists():
122 new_obj = models.Userinfo.objects.create_user(username=username, password=password, email=email)
123 print(f'新用户{username}注册成功!')
124 return redirect('login')
125 else:
126 register_obj.add_error('name', '用户名已存在!')
127 return render(request, 'register.html', {'register_obj': register_obj})
128
129 else:
130 return render(request, 'register.html', {'register_obj': register_obj})
131
132 #登录页面的ModelForm组件(本次未使用)
133 '''
134 # class LoginForm(forms.ModelForm):
135 # class Meta:
136 # model = models.Userinfo # 指定类
137 # # fields='__all__'
138 # # exclude=[] #排除字段
139 # fields = ['username', 'password'] # 设置的字段
140 # labels = { # 标签名
141 # 'username': '用户名:',
142 # 'password': '密码:'}
143 # error_messages = { # 错误信息
144 # 'username': {'required': '用户名不能为空!'},
145 # 'password': {'required': '密码不能为空!'},
146 # }
147 # widgets = { # 插件
148 # 'username': forms.TextInput({"class": "form-control"}),
149 # 'password': forms.TextInput({"class": "form-control"}),
150 #
151 # }
152 # # def clean_username(self):
153 # # pass
154 # # return self.cleaned_data.get('username')
155 # # def clean(self):
156 # # pass
157 # # return self.cleaned_data
158 # # def __init__(self,*args,**kwargs):
159 # # super().__init__(*args,**kwargs)
160 # # for field in self.fields:
161 # # self.fields[field].widget.attrs.update({'class':'form-control'})
162
163 '''
164
165
166
167
168
169 #随机验证码
170 def get_cverification_code(request):
171 import os
172 from crmtest import settings
173 import random
174 def get_random_color():
175 '''
176 随机颜色
177 :return: rgb颜色
178 '''
179 return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
180
181 from PIL import Image, ImageDraw, ImageFont#要先安装pillow模块:pip install pillow
182 img_obj = Image.new('RGB', (120, 40), get_random_color())#实例化图片对象
183 draw_obj = ImageDraw.Draw(img_obj)#创建图片
184
185 font_path = os.path.join(settings.BASE_DIR, r'static_files\fonts\BRUX.otf')#字体路径(字体自己下载)
186 print('>>>>', font_path)
187 # font_obj = ImageFont.truetype(font_path, 26)#路径拼接注意不能有中文,否则报错
188 font_obj = ImageFont.truetype(r'static_files/fonts/BRUX.otf', 26) #相对路径r'static_files/fonts/BRUX.otf'
189 # font_obj = ImageFont.load_default().font#系统默认字体
190 sum_str = ''
191 for i in range(6):#生成随机的字母数字组合
192 a = random.choice([str(random.randint(0, 9)), chr(random.randint(97, 122)),
193 chr(random.randint(65, 90))]) # 4 a 5 D 6 S
194 sum_str += a
195 print(sum_str)
196 draw_obj.text((12, 2), sum_str, fill=get_random_color(), font=font_obj)
197
198 width = 120
199 height = 40
200 # 添加噪线
201 for i in range(5):#循环一次就是一条线:两点确定一条
202 x1 = random.randint(0, width)
203 x2 = random.randint(0, width)
204 y1 = random.randint(0, height)
205 y2 = random.randint(0, height)
206 draw_obj.line((x1, y1, x2, y2), fill=get_random_color())
207 # # 添加噪点
208 for i in range(10):
209 # 这是添加点,50个点
210 draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
211 # 下面是添加很小的弧线,看上去类似于一个点,50个小弧线
212 x = random.randint(0, width)
213 y = random.randint(0, height)
214 draw_obj.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
215
216 from io import BytesIO#生成的图片格式指定和存在位置(缓存)
217 f = BytesIO()
218 img_obj.save(f, 'png')
219 data = f.getvalue()
220
221 # 验证码对应的数据保存到session里面
222 request.session['valid_str'] = sum_str
223
224 return HttpResponse(data)
225
226 # 登录
227 def login(request):
228 if request.method == 'GET':
229 return render(request, 'login.html')
230 elif request.method == 'POST':
231 # print(request.POST)
232 username = request.POST.get('username')
233 password = request.POST.get('password')
234 cverification_code=request.POST.get('cverification_code')
235
236 if cverification_code.upper()==request.session.get('valid_str').upper():
237
238 user_obj = auth.authenticate(username=username, password=password)
239 print(user_obj)
240 if user_obj:
241 auth.login(request, user_obj)
242 return JsonResponse({'status': 1, 'url': reverse('index')})
243 else:
244 return JsonResponse({'status': 0, 'url': '账号或密码有误!'})
245 else:
246 return JsonResponse({'status': 0, 'url': '验证码输入有误!'})
247
248
249 #状态认证的首页访问(使用装饰器认证状态失败,会自动跳转一个路径,可以在settings中配置指定LOGIN_URL='/login/')
250 #同时在页面的请求路径会自动加上'?next=/index/'(当前页面路径),
251 # 借此可以在前端通过location.search获取后slice切边获取路径,登录成功之后在success回调函数location.href指向该路径,自动跳转访问的页面
252 @login_required
253 def index(request):
254 # if request.user.is_authenticated:
255 print(request.user)
256 if request.method == 'GET':
257 return render(request, 'index.html')
258 # else:
259 # return redirect('login')
260
261
262 #不加装饰器的方法判断状态
263 '''
264 def index(request):
265 if request.user.is_authenticated:
266 print(request.user)
267 if request.method == 'GET':
268 return render(request, 'index.html')
269 else:
270 return redirect('login')
271 '''
272
273
274 #注销
275 def logout(request):
276 auth.logout(request)
277 return redirect('login')
278
279 #修改密码
280 def reset_psd(request):
281 if request.user.is_authenticated:
282 if request.method == 'GET':
283 return render(request, 'reset_psd.html')
284 elif request.method == 'POST':
285 old_password = request.POST.get('old_password')
286 new_password = request.POST.get('new_password')
287 r_new_password = request.POST.get('r_new_password')
288 # ret=request.user.check_password(old_password)
289 # print(ret)
290 if request.user.check_password(old_password):
291 if new_password == r_new_password:
292 request.user.set_password(new_password)
293 request.user.save()
294 return JsonResponse({'status': True, 'info': '操作成功!', 'url': reverse('index')})
295 else:
296 return JsonResponse({'status': False, 'info': '两次新密码不一致!', 'url': ''})
297 else:
298 return JsonResponse({'status': False, 'info': '操作失败:原密码输入有误!', 'url': ''})
299 return JsonResponse({'status': False, 'info': '操作失败!', 'url': ''})
300
301 else:
302 return redirect('login')
urls.py

1 from django.conf.urls import url 2 from django.contrib import admin 3 from app01 import views 4 5 urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^register/', views.register, name='register'), 8 url(r'^login/', views.login, name='login'), 9 url(r'^index/', views.index, name='index'), 10 url(r'^logout/', views.logout, name='logout'), 11 url(r'^reset_psd/', views.reset_psd, name='reset_psd'), 12 url(r'^get_cverification_code/', views.get_cverification_code, name='get_cverification_code'),#随机验证码 13 14 ]
templates
register.html

1 {% load static %}
2 <!DOCTYPE html>
3 <html lang="en">
4 <head>
5 <meta charset="UTF-8">
6 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
7
8 <title>用户注册</title>
9 </head>
10 <body style="background-image: url('{% static 'images/register_bg.gif' %}');background-size: cover">
11 <div class="container">
12 <div class="row">
13 <div class="col-xs-6 col-xs-offset-3" style="margin-top: 12%;">
14 <div class="container-fluid" style="background-color: rgba(255,255,255,0.2);border-radius: 5%">
15 <div class="row">
16 <h2 class="text-left col-xs-8 text-primary">新用户注册:</h2>
17 </div>
18 <div class="row" >
19 <form action="{% url 'register' %}" method="post" novalidate class="form-horizontal">
20 {% csrf_token %}
21 {% for field in register_obj %}
22 <div class="form-group" >
23 <label for="{{ field.id_for_label }}"
24 class="col-xs-3 control-label" >{{ field.label }}</label>
25 <div class="col-xs-7">
26 {{ field }}
27 <div style="height: 10px;" class="text-danger">{{ field.errors.0 }}</div>
28 </div>
29 </div>
30 {% endfor %}
31 <div class="form-group">
32 <div class="col-sm-7 col-xs-offset-3">
33 <span class="col-xs-10 text-success text-center"
34 style="line-height: 200%">已有账号,请<a href="{% url 'login' %}">登录</a>!</span>
35 <input type="submit" class="btn btn-success btn-sm col-xs-2 pull-right">
36
37 </div>
38 </div>
39 </form>
40 </div>
41 </div>
42 </div>
43 </div>
44 </div>
45
46 </body>
47 <script src="{% static 'jquery-3.4.1.js' %}"></script>
48 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
49 </html>
login.html

1 {% load static %}
2 <!DOCTYPE html>
3 <html lang="en">
4 <head>
5 <meta charset="UTF-8">
6 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
7 <title>用户登录</title>
8 </head>
9 <body style="background-image: url('{% static "images/login_bg.jpg" %}');background-size: cover">
10 <div>
11 <div class="container">
12 <div class="row">
13 <div class='col-xs-4 col-xs-offset-4'>
14 <div class="row" style="margin-top: 50%;background-color: rgba(255,255,255,0.2 );border-radius: 3%">
15 <div class="row c1">
16 <h2 class=" col-xs-6 text-primary" style="margin-bottom: 30px">用户登录</h2>
17 </div>
18 <div class="row">
19 <div class="form-group" style="height: 60px;">
20 <label for="username" class="col-xs-3 control-label text-right">用户名:</label>
21 <div class="col-xs-8">
22 <input type="text" class="form-control" id="username">
23 <div class="text-danger"></div>
24 </div>
25 </div>
26 <div class="form-group" style="height: 60px;">
27 <label for="password" class="col-xs-3 control-label text-right">密码:</label>
28 <div class="col-xs-8">
29 <input type="password" class="form-control" id="password">
30 <div class="text-danger"></div>
31 </div>
32 </div>
33
34 <div class="form-group" style="height: 60px;">
35 <label for="code" class="col-xs-3 control-label text-right">验证码:</label>
36 <div class="col-xs-4">
37 <input type="text" class="form-control" id="code">
38 <div class="text-danger"></div>
39 </div>
40 <div class="col-xs-5" style="padding-left: 0"><img src="{% url 'get_cverification_code' %}"
41 alt="" id="cverification_code"></div>
42 </div>
43
44 <div class="form-group" style="height: 60px;">
45 {% csrf_token %}
46 <div class="col-xs-8 col-xs-offset-3">
47 <a href="{% url 'register' %}">
48 <button class="btn btn-primary col-xs-offset-2" id="register">注册</button>
49 </a>
50 <button class="btn btn-success col-xs-offset-2" id="submit">登录</button>
51 <div class=" text-danger"></div>
52 </div>
53 </div>
54 </div>
55 </div>
56 </div>
57 </div>
58 </div>
59 </div>
60
61 </div>
62 </div>
63 </body>
64 <script src="{% static 'jquery-3.4.1.js' %}"></script>
65 <script src="{% static 'jquery-cookie-1.4.1.js' %}"></script>
66 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
67 <script>
68 $(function () {
69
70 $('#cverification_code').on('click',function () {
71 var src='{% url "get_cverification_code" %}?temp='+Math.random();
72 console.log(src);
73 $('#cverification_code').attr('src',src);
74 });
75
76
77
78 $('#username').blur(function () {
79 if (username !== '') {
80 $('#username').next().text('');
81 }
82 });
83 $('#password').blur(function () {
84 if (username !== '') {
85 $('#password').next().text('');
86 }
87 });
88 $('#code').blur(function () {
89 if (username !== '') {
90 $('#code').next().text('');
91 }
92 });
93 $('#username').focus(function () {
94 if (username !== '') {
95 $('#submit').next().text('');
96 }
97 });
98 $('#password').focus(function () {
99 if (username !== '') {
100 $('#submit').next().text('');
101 }
102 });
103 $('#code').focus(function () {
104 if (username !== '') {
105 $('#code').next().text('');
106 }
107 });
108
109
110 $('#submit').click(function () {
111
112 var username = $('#username').val().trim();
113 var password = $('#password').val().trim();
114 var cverification_code = $('#code').val().trim();
115 console.log(cverification_code);
116 console.log(cverification_code.length);
117
118
119 if (username === '' || password === '' || cverification_code.length !== 6) {
120 if (username === '') {
121 $('#username').next().text('用户名不能为空!');
122 }
123 ;
124 if (password === '') {
125 $('#password').next().text('密码不能为空!')
126 }
127 ;
128 if (cverification_code.length !== 6) {
129 $('#code').next().text('验证码为6位!');
130 }
131 ;
132 console.log(cverification_code.length);
133 if (cverification_code.length !== 6) {
134 $('#code').next().text('验证码为6位!');
135 return false
136 }
137 ;
138
139 } else {
140 $.ajax({
141 url: '{% url 'login' %}',
142 type: 'POST',
143 headers: {'X-CSRFToken': $.cookie('csrftoken')},
144 data: {
145 'username': username,
146 'password': password,
147 'cverification_code': cverification_code,
148
149 },
150 success: function (request) {
151 console.log(request);
152 if (request.status === 1) {
153 location.href = request.url;
154 } else {
155 $('#submit').next().text(request.url);
156 }
157 }
158 })
159 }
160
161 })
162 })
163 </script>
164 </html>
index. Html

1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>首页</title>
6 </head>
7 <body>
8 <h1>index</h1>
9 <a href="{% url 'logout' %}"><h6>注销</h6></a>
10 <a href="{% url 'reset_psd' %}">修改密码</a>
11 </body>
12 </html>
reset_psd.html

1 {% load static %}
2 <!DOCTYPE html>
3 <html lang="en">
4 <head>
5 <meta charset="UTF-8">
6 <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
7 <title>修改密码</title>
8 </head>
9 <body>
10 <div><h3>当前用户:{{ username }}</h3>
11
12 <p>请输入原密码:<input type="password" id="old_password"></p>
13 <P>请输入新密码:<input type="password" id="new_password"></P>
14 <p>请确认新密码:<input type="password" id="r_new_password"></p>
15 {% csrf_token %}
16 <p><button class="btn btn-success" id="submit">保存</button></p>
17
18 </div>
19 </body>
20 <script src="{% static 'jquery-3.4.1.js' %}"></script>
21 <script src="{% static 'jquery-cookie-1.4.1.js' %}"></script>
22 <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
23 <script>
24 $(function () {
25 $('#submit').click(function () {
26 var old_password=$('#old_password').val().trim();
27 var new_password=$('#new_password').val().trim();
28 var r_new_password=$('#r_new_password').val().trim();
29
30 if(old_password===''||new_password==='' || r_new_password===''){
31 alert('不允许有空!');
32 return false
33 };
34
35 if(new_password!==r_new_password){
36 alert('两次输入的新密码不一致,请重新确认输入!');
37 return false
38 };
39
40
41 $.ajax({
42 url:'{% url "reset_psd" %}',
43 type:'post',
44 headers:{'X-CSRFToken':$.cookie('csrftoken')},
45 data:{
46 old_password:old_password,
47 new_password:new_password,
48 r_new_password:r_new_password,
49 },
50 success:function (response) {
51 if(response.status===true){
52 alert(response.info);
53 location.href=response.url;
54 }
55 else {
56 alert(response.info)
57 }
58 }
59
60 }
61 )
62 })
63 })
64 </script>
65 </html>
来源:https://www.cnblogs.com/open-yang/p/11223418.html
