- 用的是pycharm专业版本,并没有使用虚拟环境,使用的是django2.1,python3.6
- 写这个项目需要你了解django基本结构与语法,不然在映射和函数之间,你会晕头转向
- 了解简单的html css JavaScript代码
第一课堂:简单创建项目和app
- 创建项目
- pycharm 人性化,必须专业版
- 命令行,进入此目录,写下如下代码
python3 manage.py startproject 项目名字
# manage.py则是每个Django项目中自动生成的一个用于管理项目的脚本文件,需要在cmd窗口中cd到Django项目的manage.py所在的目录后通过python命令执行。其中的command是Django内置的或者你自定义的命令
- 创建app
命令行进入项目目录
python3 manegy.py startapp app名字
- 由于我们设计的登录和检测,肯定是一个需要记录时间的,所以我们在settings文件里面设置中国时区
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
第二课堂:设计数据模型
- 数据库模型设计
- 明确我们设计的模型需要那些输入:用户名,密码,邮箱地址,创建时间
- 有了目标,在models里面,进行相应的设计,一些属性简单易懂
class User(models.Model):
gender = (
('male', "男"),
('female', "女"),
)
name = models.CharFiel(max_length=128unique=True)
password = models.CharField(max_length=256)
email = models.EmailField(unique=True)
sex = models.CharField(max_length=32choices=gender, default="男")
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Meta:
ordering = ["-c_time"]
verbose_name = "用户"
verbose_name_plural = "用户"
- 连接数据库,这里我有几个问题需要解决
- 把你的数据库编码全部解决,进入你的数据库中
show variables like '%char%';#显示你数据库字符集列表
set character_set_client=utf8;
set character_set_connection=utf8;
set character_set_database=utf8;
set character_set_results=utf8;
set character_set_server=utf8;
set character_set_system=utf8;
set collation_connection=utf8;
set collation_database=utf8;
set collation_server=utf8;
上面代码分行写,分别运行,不要管警告
---------------------
作者:胖虎艾春辉
来源:CSDN
原文:https://blog.csdn.net/weixin_42479155/article/details/85345973
版权声明:本文为博主原创文章,转载请附上博文链接!
- 如果你是使用navicat强烈建议,你重新检查一遍,字符集一定选utf8
- 将你的新建app全部加入settings里面的app注册表里
- 激活模型:
python3 manage.py migrate
第三课堂:创建后台:管理人员,路由和视图
- 在admin里面与你创建的模型进行对接,在admin里面输入
admin.site.register(models.User)
- 创建超级管理员权限
python3 manage.py createsuperuser
- 路由需求:主页,登录,注册,登出
- 搭建映射与函数(在app的views.py)
urls里面的分配:
from django.contrib import admin
from django.urls import path
from login import views
from django.conf.urls import include
from django.urls import re_path
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index, name = 'index'),
path('login/',views.login,name = 'login'),
#path('',views.base,name ='base'),
path('',views.index),
path('register/',views.register,name = 'register'),
path('logout/',views.logout,name = 'logout'),
]
#**这里我需要提醒,为了避免函数调用出现问题,我们给每一个映射都设计一个命名空间**
- 在login/view.py里面构建简单映射
from django.shortcuts import render
from django.shortcuts import redirect
# Create your views here.
def index(request):
pass
return render(request, 'login/index.html')
def login(request):
pass
return render(request, 'login/login.html')
def register(request):
pass
return render(request, 'login/register.html')
def logout(request):
pass
return redirect("/index/")
# 这里我提醒:导入redirect是为了重定向
render函数有两个参数,第一个request准备返回,固定参数,不要随意改变,第二个参数是需要渲染的页面
第四课堂:前端页面设计
- 如果不继承的话是这样的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div style="margin: 15% 40%;">
<h1>欢迎登录!</h1>
<form action="/login/" method="post">
<p>
<label for="id_username">用户名:</label>
<input type="text" id="id_username" name="username" placeholder="用户名" autofocus required />
</p>
<p>
<label for="id_password">密码:</label>
<input type="password" id="id_password" placeholder="密码" name="password" required >
</p>
<input type="submit" value="确定">
</form>
</div>
</body>
</html>
这样的页面非常丑
- 使用bootstrap开源框架
-
这个框架弥补你对前端开发的不足,
-
同时相匹配的jquery下载
(http://www.bootcss.com/) -
对于静态文件的引入,在settings文件中,它有一个STATICFILES_DIRS的模块,所以在最下面一行加入引导路径
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
- 同事,前端页面不可能都各自编写,而是要有复用性,也就是说你这个html文件可以被继承和扩展
- 在bootstap文档中,为我们这些小白提供了一个入门模板:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<h1>你好,世界!</h1>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>
- 最后我们设计的网页基础模板,里面加入了自己改造的导航条:
{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>{% block title %}base{% endblock %}</title>
<!-- Bootstrap -->
<link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
{% block css %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
<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="#my-nav" aria-expanded="false">
<span class="sr-only">切换导航条</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Mysite</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="my-nav">
<ul class="nav navbar-nav">
<li class="active"><a href="/index/">主页</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% block content %}{% endblock %}
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="{% static 'js/jquery-3.2.1.js' %}"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
- 通过页面顶端的{% load staticfiles %}加载后,才可以使用static方法;
- 通过{% block title %}base{% endblock %},设置了一个动态的页面title块;
- 通过{% block css %}{% endblock %},设置了一个动态的css加载块;
- 通过{% block content %}{% endblock %},为具体页面的主体内容留下接口;
- 通过{% static ‘bootstrap-3.3.7-dist/css/bootstrap.min.css’ %}将样式文件指向了我们的实际静态文件,下面的js脚本也是同样的道理
- 修改login.html文件对base文件进行拓展:
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}
{% block content %}
<div class="container">
<div class="col-md-4 col-md-offset-4">
<form class='form-login' action="/login/" method="post">
<h2 class="text-center">欢迎登录</h2>
<div class="form-group">
<label for="id_username">用户名:</label>
<input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
</div>
<div class="form-group">
<label for="id_password">密码:</label>
<input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
</div>
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
</div>
</div> <!-- /container -->
{% endblock %}
- 通过{% extends ‘base.html’ %}继承了‘base.html’模板的内容;
- 通过{% block title %}登录{% endblock %}设置了专门的title;
- 通过block css引入了针对性的login.css样式文件;
- 主体内容定义在block content内部
- 添加了一个重置按钮
- 在static目录下新建一个css文件夹,在里面新建login.css文件,在里面设置css样式,具体样式是这样
body{
background-image: url("8b13632762d0f703a7484cf103fa513d2697c521.jpg");
}
.form-login{
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-login .form-control{
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-login .form-control:focus{
z-index: 2;
}
.form-login input[type = "text"]{
margin-bottom: -1px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.form-login input[type = password]{
margin-bottom: 10px;
border-bottom-left-radius: 0;
border-bottom-left-radius: 0;
}
- 同时,如果你想加入背景的话,可以在body属性的css文件下添加background-image属性 = url(建议你的背景图片放在同一个文件夹下,这样就没有必要去寻找了
第五课堂:登录视图
- 我们希望用户通过login页面填写表单和用户名密码,并且是POST的方式发送到服务器/login/地址,服务器通过视图函数,接受并处理这些请求:
def login(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
print(username, password)
return redirect('/index/')
return render(request, 'login/login.html')
解释:
1. 每一个视图函数都至少接受一个参数,并且是第一位置参数,该参数封装了当前请求的所有数据
2. request.method分装了数据请求大写POST,将执行if语句
3. get('username')的键名字是html表单中input元素中name属性定义的值,所以不能忘记添加name属性
4. 利用redirect方法,将页面重定向到index页
5. 但是你这样也是不成功的,为什么呢?因为django有一部分的内置防御功能,解决问题就是在所有的form里面,添加:{% csrf_token %}
- 数据验证,实现必填字段不能为空,密码用圆点代替隐藏
def login(request):
if request.method == "POST":
username = request.POST.get('username', None)
password = request.POST.get('password', None)
if username and password: # 确保用户名和密码都不为空
username = username.strip()
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
return redirect('/index/')
return render(request, 'login/login.html')
- 连接数据库并且进行验证:
def login(request):
if request.method == "POST":
username = request.POST.get('username', None)
password = request.POST.get('password', None)
if username and password: # 确保用户名和密码都不为空
username = username.strip()
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
try:
user = models.User.objects.get(name=username)
except:
return render(request, 'login/login.html')
if user.password == password:
return redirect('/index/')
return render(request, 'login/login.html')
解释:
1. 导入models模块
2. models.User.objects.get(name=username)是Django提供的最常用的数据查询API,因为我们之前设计好了数据库的表
- 添加提示信息
def login(request):
if request.method == "POST":
username = request.POST.get('username', None)
password = request.POST.get('password', None)
message = "所有字段都必须填写!"
if username and password: # 确保用户名和密码都不为空
username = username.strip()
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
try:
user = models.User.objects.get(name=username)
if user.password == password:
return redirect('/index/')
else:
message = "密码不正确!"
except:
message = "用户名不存在!"
return render(request, 'login/login.html', {"message": message})
return render(request, 'login/login.html')
- 对于登录页面进行修改:
<form class='form-login' action="/login/" method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h2 class="text-center">欢迎登录</h2>
<div class="form-group">
<label for="id_username">用户名:</label>
<input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
</div>
<div class="form-group">
<label for="id_password">密码:</label>
<input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
</div>
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
第6课堂:设计表单
- 原理:django在内部集成了一个表单功能,以面对对象的方式,直接使用python代码生成html表单代码:准备和重构数据用于页面渲染;
为数据创建HTML表单元素;
接收和处理用户从表单发送过来的数据 - 创建表单forms,就是在login这个app下创建forms.py文件
from django import forms
class UserForm(forms.Form):
username = forms.CharField(label="用户名", max_length=128)
password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput)
解释:
1. 导入forms模块
2. 所有的表单都需要继承自forms.Form类
3. 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中<form>内的一个input元素
4. label参数用于设置<label>标签
5. widget=forms.PasswordInput用于指定该字段在form表单里表现为<input type='password' />,也就是密码输入框
- 相应的对映射进行修改
def login(request):
if request.method == "POST":
login_form = forms.UserForm(request.POST)
message = "请检查填写的内容!"
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
try:
user = models.User.objects.get(name=username)
if user.password == password:
return redirect('/index/')
else:
message = "密码不正确!"
except:
message = "用户不存在!"
return render(request, 'login/login.html', locals())
login_form = forms.UserForm()
return render(request, 'login/login.html', locals())
1. 对于请求不是post的返回空的表格
2. 对于post表格,接收表单数据,并验证
3. 采用is_valid进行验证,验证过程有:#先来归纳一下整个流程
#(1)首先is_valid()起手,看seld.errors中是否值,只要有值就是flase
#(2)接着分析errors.里面判断_errors是都为空,如果为空返回self.full_clean(),否则返回self._errors
#(3)现在就要看full_clean(),是何方神圣了,里面设置_errors和cleaned_data这两个字典,一个存错误字段,一个存储正确字段。
#(4)在full_clean最后有一句self._clean_fields(),表示校验字段
#(5)在_clean_fields函数中开始循环校验每个字段,真正校验字段的是field.clean(value),怎么校验的不管
#(6)在_clean_fields中可以看到,会将字段分别添加到_errors和cleaned_data这两个字典中
#(7)结尾部分还设置了钩子,找clean_XX形式的,有就执行。执行错误信息也会添加到_errors中
#(8)整个校验过程完成
4. 验证成功后可以从表单对象的cleaned_data数据字典中获取表单的具体值
5. 此外使用了locals这个函数,它返回当前所有的本地变量字典,直接打包,提高了效率
- 修改html页面:
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}
{% block content %}
<div class="container">
<div class="col-md-4 col-md-offset-4">
<form class='form-login' action="/login/" method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h2 class="text-center">欢迎登录</h2>
{{ login_form }}
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
</div>
</div> <!-- /container -->
{% endblock %}
1. 使用一个{{login_form}}就直接完成了表单内容的生成工作,而这个变量来自在视图函数中生成的form实例的变量名,同时,locals将其传入
2. 使用post的时候,必添加{% csrf_token %}用于处理csrf安全机制
3. 实际上除了通过{{ login_form }}简单地将表单渲染到HTML页面中了,还有下面几种方式:
{{ login_form.as_table }} 将表单渲染成一个表格元素,每个输入框作为一个<tr>标签
{{ login_form.as_p }} 将表单的每个输入框包裹在一个<p>标签内
{{ login_form.as_ul }} 将表单渲染成一个列表元素,每个输入框作为一个<li>标签
- 但其实这个一点也不好看,我有强迫症,所以手动渲染:
<div class="form-group">
{{ login_form.username.label_tag }}
{{ login_form.username}}
</div>
<div class="form-group">
{{ login_form.password.label_tag }}
{{ login_form.password }}
</div>
- 为了还原bootstarp框架一个清白,我们再次修改forms代码:
from django import forms
class UserForm(forms.Form):
username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-contro
第七课堂: 图片验证码
- 为了防止爬虫无限制的爬你的网站,导致你的服务器瘫痪,为此,我们需要提供验证码功能
- 步骤
- 看你的系统,我的系统是ubuntu18.04所以,我使用命令行输入
pip3 install django-simple-captcha
但是我遇到了问题上面显示我的mysqldb不能安装
没办法,只能sudo apt-get install python3-mysql解决了
- 注册app,captcha是一个系统内置的app,记住,不需要你取新建这个名字的app,而是只需要你在app列表进行注册就行
- 同时,在终端下执行下列语句:这句话的意思是将验证码自己制造的表推送到目的地
python3 manage.py migrate
- 使用二级路由,加入内置的路由,这是我个人的设置大家可以仿照一下
from django.contrib import admin
from django.urls import path
from login import views
from django.conf.urls import include
from django.urls import re_path
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index, name = 'index'),
path('login/',views.login,name = 'login'),
#path('',views.base,name ='base'),
path('register/',views.register,name = 'register'),
path('logout/',views.logout,name = 'logout'),
re_path(r'^captcha',include('captcha.urls'))#添加captcha相对应的网址
]
- 在forms里面添加captchafield,这个文件下代码如下
from django import forms
#添加图形验证码
from captcha.fields import CaptchaField
class UserForm(forms.Form):
username = forms.CharField(label="用户名:",max_length=128,widget=forms.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(label="密码:",max_length=256,widget=forms.PasswordInput(attrs={'class': 'form-control'}))
captcha = CaptchaField(label='验证码')
- 修改login.html
<div class="form-group">
{{ login_form.username.label_tag }}
{{ login_form.username }}
</div>
<div class="form-group">
{{ login_form.password.label_tag }}
{{ login_form.password}}
</div>
<div class="form-group">
{{ login_form.captcha.errors }}#指示用户,你的验证码有问题
{{ login_form.captcha.label_tag }}
{{ login_form.captcha }}
</div>
- 虽然页面有点抽,但是框架还是不错的,is_valid()是内置的forms的验证方法
第八课堂: sessions会话
- 因为http协议特性,每一次来自于用户的浏览器请求是无状态的、独立的,也就是说无法保存用户状态,你可以在网页中尝试你把cookies进行关闭,看看你能不能访问天猫淘宝等
- 所有现代网站通常将cookies保存不重要的东西,大部分重要的东西通过sessions会话功能进行保存在服务器端
- django提供了一个通用sessions框架,并且可以使用多种sessions数据保存方式
- 保存在数据库里面
- 保存到缓存
- 保存到文件里
- 保存在cookies内
- django默认session框架,你不要修改就行,大致是这样,在settings文件下
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',#sessions会话窗口,相当于认证你是不是用户还是陌生人
'django.contrib.messages',
'django.contrib.staticfiles',
'login',
'captcha'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',#启用sessions
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- sessions 用法
class backends.base.SessionBase
# 这是所有会话对象的基类,包含标准的字典方法:
__getitem__(key)
Example: fav_color = request.session['fav_color']
__setitem__(key, value)
Example: request.session['fav_color'] = 'blue'
__delitem__(key)
Example: del request.session['fav_color'] # 如果不存在会抛出异常
__contains__(key)
Example: 'fav_color' in request.session
get(key, default=None)
Example: fav_color = request.session.get('fav_color', 'red')
pop(key, default=__not_given)
Example: fav_color = request.session.pop('fav_color', 'blue')
# 类似字典数据类型的内置方法
keys()
items()
setdefault()
clear()
# 它还有下面的方法:
flush()
# 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。
set_test_cookie()
# 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的工作机制,你只有在下次用户请求的时候才可以测试。
test_cookie_worked()
# 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之前先调用set_test_cookie()方法。
delete_test_cookie()
# 删除测试cookie。
set_expiry(value)
# 设置cookie的有效期。可以传递不同类型的参数值:
• 如果值是一个整数,session将在对应的秒数后失效。例如request.session.set_expiry(300) 将在300秒后失效.
• 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效
• 如果为0,在用户关闭浏览器后失效
• 如果为None,则将使用全局会话失效策略
失效时间从上一次会话被修改的时刻开始计时。
get_expiry_age()
# 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE.
# 这个方法接受2个可选的关键字参数
• modification:会话的最后修改时间(datetime对象)。默认是当前时间。
•expiry: 会话失效信息,可以是datetime对象,也可以是int或None
get_expiry_date()
# 和上面的方法类似,只是返回的是日期
get_expire_at_browser_close()
# 返回True或False,根据用户会话是否是浏览器关闭后就结束。
clear_expired()
# 删除已经失效的会话数据。
cycle_key()
# 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login() 会调用这个方法。
- 步骤
- 使用session,修改login视图函数
def login(request):
if request.session.get('is_login',None):
return redirect('/index/')
if request.method == "POST":
login_form = forms.UserForm(request.POST)
message = "请检查填写的内容"
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
#用户名字符合法性验证
#密码长度验证
try :
user = models.User.objects.get(name=username)
if user.password == password:
request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name
return redirect('/index/')
else:
message = "密码不正确"
except:
message = "用户名不存在"
return render(request,'login/login.html',locals())
login_form = forms.UserForm
return render(request, 'login/login.html',locals())
- 修改页面,不要担心request没有加载出来,它是内置的,只是不同步,导致没有提示而已
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="my-nav">
<ul class="nav navbar-nav">
<li class="active"><a href="/index/">主页</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if request.session.is_login %}
<li><a href="#">当前在线:{{ request.session.user_name }}</a></li>
<li><a href="/logout/">登出</a></li>
{% else %}
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
- 修改index内容
{% extends 'base.html' %}
{% block title %}主页{% endblock %}
{% block content %}
{% if request.session.is_login %}
<h1>你好,{{ request.session.user_name }}!欢迎回来!</h1>
{% else %}
<h1>你尚未登录,只能访问公开内容!</h1>
{% endif %}
{% endblock %}
第九课堂: 注册
- 竟然登录都已经写好了登录代码,那么你需要一个注册代码,这样才使得和前后端通信完美
- 步骤
- 我们在login的forms文件中在userform的下面新建一个类,那就是我们的注册类
class RegisterForm(forms.Form):
gender = (
('male',"男"),
('female',"女"),
)
username = forms.CharField(label="用户名",max_length=128,widget=forms.TextInput(attrs={'class':'form-control'}))
password1 = forms.CharField(label="密码",max_length=256,widget=forms.PasswordInput(attrs={'class':'form-control'}))
password2 = forms.CharField(label="确认密码",max_length=256,widget=forms.PasswordInput(attrs={'class':'form-control'}))
email = forms.EmailField(label="邮箱地址",widget=forms.EmailInput(attrs={'class':'form-control'}))
sex = forms.ChoiceField(label='性别',choices=gender)
captcha = CaptchaField(label='验证码')
解释:
1. gender字典里面和user模型中的一样,可以当成常量使用
2. password1和2是为了检验两次输入密码的相同性,防止误输入密码
3. email是一个邮箱输入框
4. sex是一个select下拉框
- 为了与表单相适应,我们在register.html进行对login依样画葫芦
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}注册{% endblock %}
{% block content %}
<div class="container">
<div class="col-md-4 col-md-offset-4">
<form class='form-register' action="/register/" method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h2 class="text-center">欢迎注册</h2>
<div class="form-group">
{{ register_form.username.label_tag }}
{{ register_form.username}}
</div>
<div class="form-group">
{{ register_form.password1.label_tag }}
{{ register_form.password1 }}
</div>
<div class="form-group">
{{ register_form.password2.label_tag }}
{{ register_form.password2 }}
</div>
<div class="form-group">
{{ register_form.email.label_tag }}
{{ register_form.email }}
</div>
<div class="form-group">
{{ register_form.sex.label_tag }}
{{ register_form.sex }}
</div>
<div class="form-group">
{{ register_form.captcha.errors }}
{{ register_form.captcha.label_tag }}
{{ register_form.captcha }}
</div>
<button type="reset" class="btn btn-default pull-left">重置</button>
<button type="submit" class="btn btn-primary pull-right">提交</button>
</form>
</div>
</div> <!-- /container -->
{% endblock %}
- 完善views里面的映射函数,和url构建的想对应
def register(request):
if request.session.get('is_login',None):
#思考一下,登录状态下注册,肯定不可行
return redirect('index/')
if request.method == "POST":
register_form = forms.RegisterForm(request.POST)
message = "请检查填写的内容"
if register_form.is_valid():
username = register_form.cleaned_data['username']
password1 = register_form.cleaned_data['password1']
password2 = register_form.cleaned_data['password2']
email = register_form.cleaned_data['email']
sex = register_form.cleaned_data['sex']
if password1 != password2:
message = "两次输入的密码不同"
return render(request,'login/register.html',locals())
else:
same_name_user = models.User.objects.filter(name=username)
if same_name_user:
message = '用户已经存在,请重新选择用户名'
return render(request,'login/register.html',locals())
same_email_user = models.User.objects.filter(email=email)
if same_email_user:
message = '该邮箱地址已经被注册,请使用别的邮箱'
return render(request,'login/register.html',locals())
#当然在上述都没错误的话,我们开始使用我们的东西了
new_user = models.User()#对于数据库里面表进行实例化,方便使用
new_user.name = username
new_user.password =hash_code(password1)
new_user.email = email
new_user.sex = sex
new_user.save()#进行保存
return redirect('/login/')#重定向到指定的位置
register_form = forms.RegisterForm()
return render(request,'login/register.html',locals())
- 思考一下,想阿里等大公司,你的密码肯定不可能是显示出来的密码,肯定加密了,对此,我们也使用加密
在views中加入一下
import hashlib
def hash_code(s, salt='mysite'):# 加点盐
h = hashlib.sha256()
s += salt
h.update(s.encode()) # update方法只接收bytes类型
return h.hexdigest()
同时对login中password的比较进行修改
if user.password == hash_code(password): # 哈希值和数据库内的值进行比对
new_user.password = hash_code(password1) # 使用加密密码
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190120121651475.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQ3OTE1NQ==,size_16,color_FFFFFF,t_70)
# 使用sha256算法,将输入密码单向转换为字符串储存在数据库中,这样的话,用户隐私得到很大保护
- 对相应的登录和注册进行修改
就是对应的密码储存,或者比对需要进行相应的加密就行
def login(request):
if request.session.get('is_login',None):
return redirect('/index/')
if request.method == "POST":
login_form = forms.UserForm(request.POST)
message = "请检查填写的内容"
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
#用户名字符合法性验证
#密码长度验证
try :
user = models.User.objects.get(name=username)
# 1.
# 导入models模块
# 2.
# models.User.objects.get(name=username)
# 是Django提供的最常用的数据查询API, 因为我们之前设计好了数据库的表
if not user.has_confirmed:#进行邮件确认
message = "用户还未通过邮件确认"
return render(request, 'login/login.html',locals())
# if user.password == password:将密码与数据库里面值进行比对,而不是提取出来,浪费时间
# 好像数据库里面的东西拿出来会自动反转
print(user.password)
if user.password == hash_code(password):
request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name
return redirect('/index/')
else:
message = "密码不正确"
except:
message = "用户名不存在"
# return render(request, 'login/login.html', {"message": message})
return render(request,'login/login.html',locals())#local更为强大,所有的有关内容,均传递
login_form = forms.UserForm
return render(request, 'login/login.html',locals())
def register(request):
if request.session.get('is_login',None):
#思考一下,登录状态下注册,肯定不可行
return redirect('index/')
if request.method == "POST":
register_form = forms.RegisterForm(request.POST)
message = "请检查填写的内容"
if register_form.is_valid():
username = register_form.cleaned_data['username']
password1 = register_form.cleaned_data['password1']
password2 = register_form.cleaned_data['password2']
email = register_form.cleaned_data['email']
sex = register_form.cleaned_data['sex']
if password1 != password2:
message = "两次输入的密码不同"
return render(request,'login/register.html',locals())
else:
same_name_user = models.User.objects.filter(name=username)
if same_name_user:
message = '用户已经存在,请重新选择用户名'
return render(request,'login/register.html',locals())
same_email_user = models.User.objects.filter(email=email)
if same_email_user:
message = '该邮箱地址已经被注册,请使用别的邮箱'
return render(request,'login/register.html',locals())
#当然在上述都没错误的话,我们开始使用我们的东西了
new_user = models.User()#对于数据库里面表进行实例化,方便使用
new_user.name = username
new_user.password =hash_code(password1)
new_user.email = email
new_user.sex = sex
new_user.save()#进行保存
code = make_confirm_string(new_user)
send_email(email,code)
return render(request,'login/confirm.html',locals())#跳转到等待邮件确认页面
register_form = forms.RegisterForm()
return render(request,'login/register.html',locals())
第十课堂: 发送邮件
- 在django中发送邮件需要在settings中配置参数
# 邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
#是否使用SSL加密,qq企业邮箱要求使用,我们又不是企业邮箱,所以不要使用ssl加密
EMAIL_HOST = 'smtp.163.com'
#指定的smtp服务器名称
EMAIL_PORT = 25
EMAIL_HOST_USER = '15530258872@163.com'
EMAIL_HOST_PASSWORD = 'qq1685715822'#授权码
# 注册有效期天数
CONFIRM_DAYS = 7
- 先写一个例子,在django里面发邮件的样式:
在mysite文件夹创建一个send_mail.py
import os
from django.core.mail import EmailMultiAlternatives
from django.core.mail import send_mail
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
if __name__ == '__main__':
# send_mail(
# '测试邮件',
# '邮件验证',
# '15530258872@163.com',
# ['1685715822@qq.com']
# )
# 某些邮件公司可能不开放smtp服务
# 某些公司要求使用ssl安全机制
# 某些smtp服务对主机名格式有要求
subject,from_email,to = '测试邮件','15530258872@163.com','1685715822@qq.com'
text_content = '欢迎'
html_content = '<p>欢迎访问<a href="www.aichunhui.cn" target="_blank">艾春辉的主页</a></p>'
msg = EmailMultiAlternatives(subject,text_content,from_email,[to])
msg.attach_alternative(html_content,"text/html")
msg.send()
第十一课堂:确认邮件注册:
- 我们需要创建一个邮件注册表,用来保存用户的确认码和注册时间
- 在/login/models.py文件中写入
from django.db import models
# Create your models here.
class User(models.Model):
gender = (
('male', "男"),
('female', "女"),
)
name = models.CharField(max_length=128, unique=True)
password = models.CharField(max_length=256)
email = models.EmailField(unique=True)
sex = models.CharField(max_length=32, choices=gender, default="男")
c_time = models.DateTimeField(auto_now_add=True)
has_confirmed = models.BooleanField(default=False)
def __str__(self):
return self.name
class Meta:
ordering = ["-c_time"]
verbose_name = "用户"
verbose_name_plural = "用户"
class ConfirmString(models.Model):
code = models.CharField(max_length=256)
user = models.OneToOneField('User')
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.name + ": " + self.code
class Meta:
ordering = ["-c_time"]
verbose_name = "确认码"
verbose_name_plural = "确认码"
#user模型中增加了确认邮件是否注册的bool型值
#注册模型保存了用户和注册码之间的关系,一对一形式
c_time就是注册时间
- 在终端里执行:python3 manage.py migrate
- 在/login/admin里面将模型注册进入
- 修改注册模型
def register(request):
if request.session.get('is_login', None):
# 登录状态不允许注册。你可以修改这条原则!
return redirect("/index/")
if request.method == "POST":
register_form = forms.RegisterForm(request.POST)
message = "请检查填写的内容!"
if register_form.is_valid(): # 获取数据
username = register_form.cleaned_data['username']
password1 = register_form.cleaned_data['password1']
password2 = register_form.cleaned_data['password2']
email = register_form.cleaned_data['email']
sex = register_form.cleaned_data['sex']
if password1 != password2: # 判断两次密码是否相同
message = "两次输入的密码不同!"
return render(request, 'login/register.html', locals())
else:
same_name_user = models.User.objects.filter(name=username)
if same_name_user: # 用户名唯一
message = '用户已经存在,请重新选择用户名!'
return render(request, 'login/register.html', locals())
same_email_user = models.User.objects.filter(email=email)
if same_email_user: # 邮箱地址唯一
message = '该邮箱地址已被注册,请使用别的邮箱!'
return render(request, 'login/register.html', locals())
# 当一切都OK的情况下,创建新用户
new_user = models.User()
new_user.name = username
new_user.password = hash_code(password1) # 使用加密密码
new_user.email = email
new_user.sex = sex
new_user.save()
code = make_confirm_string(new_user)
send_email(email, code)
message = '请前往注册邮箱,进行邮件确认!'
return render(request, 'login/confirm.html', locals()) # 跳转到等待邮件确认页面。
register_form = forms.RegisterForm()
return render(request, 'login/register.html', locals())
#其实就是多了两行代码
- 在views里面添加make_confirm_string(创建确认码对象)
def make_confirm_string(user):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
code = hash_code(user.name, now)
models.ConfirmString.objects.create(code=code, user=user,)
return code
- 创建一个发送邮件函数:
def send_email(email, code):
from django.core.mail import EmailMultiAlternatives
subject = '来自艾春辉的注册确认邮件'
text_content = '''感谢注册'''
html_content = '''
<p>感谢注册<a href="http://{}/confirm/?code={}" target='_blank';>www.aichunhui.cn</a></p>
<p>请点击站点链接完成注册确认!</p>
<p>此链接有效期为{}天!</p>
'''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)
msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
msg.attach_alternative(html_content, "text/html")
msg.send()
- 发送注册的邮件都解决了,那么我们现在来处理邮件确认请求
def user_confirm(request):
code = request.GET.get('code', None)
message = ''
try:
confirm = models.ConfirmString.objects.get(code=code)
except:
message = '无效的确认请求!'
return render(request, 'login/confirm.html', locals())
c_time = confirm.c_time
now = datetime.datetime.now()
if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
confirm.user.delete()
message = '您的邮件已经过期!请重新注册!'
return render(request, 'login/confirm.html', locals())
else:
confirm.user.has_confirmed = True
confirm.user.save()
confirm.delete()
message = '感谢确认,请使用账户登录!'
return render(request, 'login/confirm.html', locals())
- 在template里面写入一个confirm.html
{% extends 'base.html' %}
{% block title %}注册确认{% endblock %}
{% block content %}
<div class="row">
<h1 class="text-center">{{ message }}</h1>
</div>
<script>
window.setTimeout("window.location = '/login/'",2000)
</script>
{% endblock %}
- 在login中比对密码时候进行比对是否注册:插入以下代码
if not user.has_confirmed:
message = "该用户还未通过邮件确认!"
return render(request, 'login/login.html', locals())
- 最后看我的views里面所有的代码:
from django.shortcuts import render
from django.shortcuts import redirect
from . import models
#导入本地的,不是使用默认的from django import forms
from . import forms
from django.conf import settings
import hashlib
import datetime
from mysite import settings
# Create your views here.
def user_confirm(request):
code = request.GET.get('code', None)
message = ''
try:
confirm = models.ConfirmString.objects.get(code=code)
except:
message = '无效的确认请求!'
return render(request, 'login/confirm.html', locals())
c_time = confirm.c_time
now = datetime.datetime.now()
if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
confirm.user.delete()
message = '您的邮件已经过期!请重新注册!'
return render(request, 'login/confirm.html', locals())
else:
confirm.user.has_confirmed = True
confirm.user.save()
confirm.delete()
message = '感谢确认,请使用账户登录!'
return render(request, 'login/confirm.html', locals())
def send_email(email, code):
from django.core.mail import EmailMultiAlternatives
subject = '来自艾春辉的注册确认邮件'
text_content = '''感谢注册'''
html_content = '''
<p>感谢注册<a href="http://{}/confirm/?code={}" target='_blank';>www.aichunhui.cn</a></p>
<p>请点击站点链接完成注册确认!</p>
<p>此链接有效期为{}天!</p>
'''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)
msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
msg.attach_alternative(html_content, "text/html")
msg.send()
def make_confirm_string(user):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
code = hash_code(user.name, now)
models.ConfirmString.objects.create(code=code, user=user,)
return code
def hash_code(s, salt = 'mysite'):
#使用sha256
h = hashlib.sha256()
s += salt
h.update(s.encode())#update方法只接受bytes流类型
return h.hexdigest()
def index(request):
return render(request, 'login/index.html')
def login(request):
if request.session.get('is_login',None):
return redirect('/index/')
if request.method == "POST":
login_form = forms.UserForm(request.POST)
message = "请检查填写的内容"
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
#用户名字符合法性验证
#密码长度验证
try :
user = models.User.objects.get(name=username)
# 1.
# 导入models模块
# 2.
# models.User.objects.get(name=username)
# 是Django提供的最常用的数据查询API, 因为我们之前设计好了数据库的表
if not user.has_confirmed:#进行邮件确认
message = "用户还未通过邮件确认"
return render(request, 'login/login.html',locals())
# if user.password == password:将密码与数据库里面值进行比对,而不是提取出来,浪费时间
# 好像数据库里面的东西拿出来会自动反转
print(user.password)
if user.password == hash_code(password):
request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name
return redirect('/index/')
else:
message = "密码不正确"
except:
message = "用户名不存在"
# return render(request, 'login/login.html', {"message": message})
return render(request,'login/login.html',locals())#local更为强大,所有的有关内容,均传递
login_form = forms.UserForm
return render(request, 'login/login.html',locals())
def register(request):
if request.session.get('is_login',None):
#思考一下,登录状态下注册,肯定不可行
return redirect('index/')
if request.method == "POST":
register_form = forms.RegisterForm(request.POST)
message = "请检查填写的内容"
if register_form.is_valid():
username = register_form.cleaned_data['username']
password1 = register_form.cleaned_data['password1']
password2 = register_form.cleaned_data['password2']
email = register_form.cleaned_data['email']
sex = register_form.cleaned_data['sex']
if password1 != password2:
message = "两次输入的密码不同"
return render(request,'login/register.html',locals())
else:
same_name_user = models.User.objects.filter(name=username)
if same_name_user:
message = '用户已经存在,请重新选择用户名'
return render(request,'login/register.html',locals())
same_email_user = models.User.objects.filter(email=email)
if same_email_user:
message = '该邮箱地址已经被注册,请使用别的邮箱'
return render(request,'login/register.html',locals())
#当然在上述都没错误的话,我们开始使用我们的东西了
new_user = models.User()#对于数据库里面表进行实例化,方便使用
new_user.name = username
new_user.password =hash_code(password1)
new_user.email = email
new_user.sex = sex
new_user.save()#进行保存
code = make_confirm_string(new_user)
send_email(email,code)
return render(request,'login/confirm.html',locals())#跳转到等待邮件确认页面
register_form = forms.RegisterForm()
return render(request,'login/register.html',locals())
def logout(request):
if not request.session.get('is_login',None):
#如果本来就没有登录,也就没有等出一说
return redirect("/index/")
request.session.flush()
# 或者使用下面的方法
# del request.session['is_login']
# del request.session['user_id']
# del request.session['user_name']
return redirect("/index/")
- url里面代码:
from django.contrib import admin
from django.urls import path
from login import views
from django.conf.urls import include
from django.urls import re_path
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index, name='index'),
path('login/', views.login, name='login'),
# path('',views.base,name ='base'),
path('', views.index),
path('register/', views.register, name='register'),
path('logout/', views.logout, name='logout'),
re_path(r'^captcha/', include('captcha.urls'), name='captchas'), # 添加captcha相对应的网址
re_path(r'^confirm/$', views.user_confirm, name='cofirms')
]