第一节: 项目描述
第二节: 设计用户模模型类,并初步完成用户注册功能
create_time:是创建时间,我们要知道网站每天新增了多少用户,公司可能每天都要看这个数据,因为这通常会关系到项目之后的融资。参数auto_now_add=True表示我们在创建这个字段时Django可以自动帮我们自动添加当前时间 。
update_time:是更新时间,其参数auto_now=True表示一旦这个表中的某一条数据有更新,它就会自动把更新时的时间写到该数据的“更新时间”字段中。用来记录这条数据最近的一次更新是什么时候。这个也很有用,因为有时我们会根据这个分析一下用户的行为,比如这人什么时候干嘛了,更新了某个表中的某个数据。。。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Register</title>
</head>
<body>
<form action="/user/register/" method="post">
<p>
User Name: <input type="text" name="username">
</p>
<p>
Enter Password: <input type="password" name="password_1">
</p>
<p>
Re-enter Password: <input type="password" name="password_2">
</p>
<input type="submit" value="register">
</form>
</body>
</html>
from django.http import HttpResponse
from django.shortcuts import render
from .models import User
# Create your views here.
def register_view(request):
# register
# get:
if request.method == 'GET':
# return the web page
return render(request, 'user/register.html')
# post:
elif request.method == 'POST':
# get the entered data from web page
username = request.POST.get('username')
password_1 = request.POST.get('password_1')
password_2 = request.POST.get('password_2')
# 1. if two times of input password are same
if password_1 != password_2:
return HttpResponse("the two entered passwords are inconsistent")
# 2. if the username has been registered (check if username available)
old_users = User.objects.filter(username=username)
if old_users:
return HttpResponse("User name has been existed.")
# 3. insert data (we currently use plaintext password)
User.objects.create(username=username, password=password_1)
return HttpResponse("account registered successfully.")
第三节: 用户注册功能的优化
1、为什么做密码加密处理?
答:因为一旦数据库外泄,用户的密码也将会泄露
注:密码的加密处理使用哈希算法
2、插入问题
假设一个场景:我们有三四台设备都跑着这个项目代码,恰好同一时间一波人同时来注册同一个用户名,那么大家都请求可能会分散在这几台机器上,并且同一时间他们都走到了如下代码,并且大家拿到的结果都是当前用户名还没被注册,那么if语句判断之后,大家就继续走到了下面第三行代码User.objects.create(username=username, password=password)这里,那么谁的的sql语句最先操作了mysql,用户明就归谁了,那其他人就会收到报错信息,因为用户名是unique的(见下面第二张图)。
if old_users:
return HttpResponse("User name has been existed.")
User.objects.create(username=username, password=password)
return HttpResponse("account registered successfully.")
上面这个问题,是一个并发写入的问题。如何解决呢?
首先,这个错误报出来是一个正常的现象,我们只要用try把错误隐藏起来就可以了 (当然,前提是你要保证你的正常插入数据都没问题,套一个try,如果有错误,一定是数据库唯一索引这块报错了)
代码如下:
3、新用户注册后可以免登录一天
user/views.py中:
cloud_note/cloud_note/settings.py中:在代码的最后一行,为session设置一天的生命周期(默认是14天)
第四节:用户登录功能
勾选“记住用户名”则有3天有效期,如果不勾选,则只有1天有效期。有效期结束后,就需要重新登录。
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Login</title>
</head>
<body>
<form action="/user/login/" method="post">
<p>
User Name: <input type="text" name="username">
</p>
<p>
Enter Password: <input type="password" name="password">
</p>
<p>
<input type="checkbox" name="remember"> remember me
</p>
<p>
<input type="submit" value="login">
</p>
</form>
</body>
</html>
def login_view(request):
if request.method == 'GET':
return render(request, 'user/login.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
try:
user = User.objects.get(username=username)
except Exception as e:
print('--login user error %s'%(e))
return HttpResponse('Username or password incorrect.')
m = hashlib.md5()
m.update(password.encode())
password = m.hexdigest()
if password != user.password:
return HttpResponse('Username or password incorrect.')
# record session status(将会话状态信息存到相应的session里,因为用户登录后会在session中保持一天时间的登录状态
request.session['username'] = username
request.session['uid'] = user.id
return HttpResponse('Login successful.')
注意:如果用户输入的密码错误或用户名不存在,都提示‘用户明或密码有误’,不要给出太明确的提示信息,以免一些别有用心的人搞事情(比如暴力的去试),加大了账户密码的破解难度,提高了安全性。
接下来,我们考虑勾选remember的问题:如果remember勾选,则有3天免登录,否则只有1天。但是我们之前已经在settings.py中设置了session只有1天的生命周期,那么该如何把会话状态的时间拉长?这里我们就要用到cookie了。
众所周知,cookie是把会话状态数据存储在浏览器中,这种方式在一定程度上减轻了服务器的压力。试想一下,假设勾选remember可以有一个月的免登录,并且会话信息存储在session中的话,那服务器就要存储会话信息至少一个月。如果有很多用户都勾选了,那服务器的存储压力就会比较大了。解决的方法是:如果用户勾选remember,则在cookie也存储一份该用户的会话信息,这样,并且把cookie的生命周期设置为我们希望的remember免登录的天数,而session只存储1天,这样服务器的压力就减轻了。
如果没有勾选remember,就只提交username和password,不提交remember,如下图所示:
如果勾选了remember,则会提交username,password和remember,如下图所示:
所以我们判断用户是否勾选remember me,只要判断提交的内容是否有remember就可以了
代码如下:
def login_view(request):
if request.method == 'GET':
return render(request, 'user/login.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
try:
user = User.objects.get(username=username)
except Exception as e:
print('--login user error %s'%(e))
return HttpResponse('Username or password incorrect.')
m = hashlib.md5()
m.update(password.encode())
password = m.hexdigest()
if password != user.password:
return HttpResponse('Username or password incorrect.')
# record session status
request.session['username'] = username
request.session['uid'] = user.id
# check if 'remember me' was selected
resp = HttpResponse('Login successful.')
if 'remember' in request.POST:
# cookie store username and uid for 3 days if "remember me" is selected
resp.set_cookie(key='username', value=username, max_age=3600 * 24 * 3)
resp.set_cookie(key='uid', value=user.id, max_age=3600 * 24 * 3)
return resp
第五节: 登录校验功能与网站首页
1、新增“登录校验”功能
在登录这块,要校验一下当前浏览器有没有登录的用户,如果之前登录过并且还在有效期内,那用户就不用再看到登录界面了,直接跳转到已登录。如果没登录过或过了有效期,则显示登录界面。即:要在user/login这个页面做登录状态的检查。
代码如下:
2、网站首页
index/templates/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# {{ request.session }}#}
{# {{ request.COOKIES }}#}
{% if request.session.username and request.session.uid %}
<p>
Welcome {{ request.session.username }}
</p>
<p>
<a href="">Logout</a>
</p>
<p>
<a href="">My notes</a>
</p>
{% elif request.COOKIES.username and request.COOKIES.uid %}
<p>
Welcome {{ request.COOKIES.username }}
</p>
<p>
<a href="">Logout</a>
</p>
<p>
<a href="">My notes</a>
</p>
{% else %}
<a href="/user/login/">Login</a>
<a href="/user/register/">Register</a>
{% endif %}
</body>
</html>
index/views.py:
from django.shortcuts import render
# Create your views here.
def index_view(request):
return render(request, 'index/index.html')
3、小作业:退出登录功能
4、小结
第六节:实现笔记功能
1、创建笔记模型类
2、笔记模块——列表页
独立完成。
需要注意的细节:用户能看到该页面的前提是已经登录了
3、笔记模块——新增笔记功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add Note</title>
</head>
<body>
<form action="/note/add_note/" method="post">
<p>
Title: <input type="text" name="title">
</p>
<p>
<textarea name="content" cols="30" rows="10"></textarea>
<input type="submit" value="save">
</p>
</form>
</body>
</html>