django -- 实现ORM登录
前戏
上篇文章写了一个简单的登录页面,那我们可不可以实现一个简单的登录功能呢?如果登录成功,给返回一个页面,失败给出错误的提示呢?
在之前学HTML的时候,我们知道,网页在往服务器提交数据的时候,都是在form表单里,并且要满足下面的几个条件:
1.form标签必须要有action和method属性,如果是文件上传还要加上下面的一句
<form action="www.baidu.com" enctype="multipart/form-data"></form>
2.所有获取用户的标签必须放在form表单中,必须要有name属性
3.必须要有submit按钮
Django必学三件套
Django 基础必会三件套,render,HttpResponse,redirect
from django.shortcuts import HttpResponse, render, redirect
HttpResponse:返回一个指定的字符串
render:返回一个HTML文件
redirect:跳转url,如果是同一个网站,可以省略域名,如果想跳转到其他网站,要写上全部的网址
知道了form表单提交数据的三个要素后,我们修改一下login.html文件
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css"> <link rel="stylesheet" href="/static/font-awesome-4.7.0/css/font-awesome.css"> <style> body { background-color: #eeeeee; } .login-box { margin-top: 50px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4 login-box"> <form class="form-horizontal" action="/login/" method="post"> <div class="col-sm-9"> <h2 class="text-center">请登录</h2> </div> <div class="form-group"> <div class="col-sm-9"> <div class="input-group margin-bottom-sm"> <label for="email" class="hidden">邮箱</label> <span class="input-group-addon"><i class="fa fa-envelope-o fa-fw"></i></span> <input class="form-control" type="text" name="email" id="email" placeholder="您的邮箱地址"> </div> <span class="help-block"></span> </div> </div> <div class="form-group"> <div class="col-sm-9"> <div class="input-group"> <label for="password" class="hidden" >密码</label> <span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span> <input class="form-control" type="password" name="pwd" id="password" placeholder="请输入密码"> </div> <span class="help-block"></span> </div> </div> <div class="form-group"> <div class="col-sm-9"> <div class="checkbox"> <label> <input type="checkbox"> 记住我 </label> </div> </div> </div> <div class="form-group"> <div class="col-sm-9"> <button type="submit" id="b1" class="btn btn-block btn-primary">登录</button> <p style="color: red;text-align: center">{{ error_msg}}</p> </div> </div> </form> </div> </div> </div> <script src="/static/jquery-3.3.1.js"></script> <script> $("#b1").click(function () { $("input:not([type='checkbox'])").each(function () { // 判断值为不为空 if ($(this).val().length === 0) { // 展示错误提示 var errMsgPrefix = $(this).prev().prev().text(); $(this).parent().next().text(errMsgPrefix + "不能为空"); $(this).parent().parent().parent().addClass("has-error"); } }); }); // 给输入框绑定获取焦点的事件 $("input:not([type='checkbox'])").focus(function () { // 清空错误提示 $(this).parent().next().text(""); // 移除父标签的has-error $(this).parent().parent().removeClass("has-error"); }); </script> </body> </html>
注意:action="/login/"要加/,要不然下面的错误时路径会是这样的:login/login
我们在去login函数里打印一下request.GET
def login(request): print(request.GET) return render(request,'login.html')
结果:
[02/Jul/2019 20:46:35] "GET /login/login?email=%40163.com&pwd=123 HTTP/1.1" 200 3532 <QueryDict: {'email': ['@163.com'], 'pwd': ['123']}>
可以看到,我们在登录页面输入的邮箱和密码都以字典的方式传给了后台,其中字典的key就是我们在html文件里对应标签的name属性,如果没有name属性,则为空
我们提交数据的时候,通常都是以post方法来提交的,get通常是用于获取数据的,那我们把login.html文件里的method=‘get’改为post,在来试一下
<form class="form-horizontal" action="login" method="post">
上面的错误信息告诉了我们:CSRF验证失败。请求中止。我们只需要把setting.py里的CSRF注释掉就可以了
'django.middleware.csrf.CsrfViewMiddleware' #注释掉这行
先去把login函数里的GET换成POST
print(request.POST),然后就可以正常访问了
那我们登录之后能不能给浏览器返回一个“登录成功”的提示呢?
index.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="page-header"> <h1>信息收集卡 <small>共三步</small> </h1> </div> <div class="progress"> <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 33%;"> 1/3 </div> </div> <!--面板--> <div class="panel panel-primary"> <div class="panel-heading">基本信息 <span class="glyphicon glyphicon-pushpin pull-right"></span> </div> <div class="panel-body"> <!--表单--> <form class="form-horizontal"> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">姓名</label> <div class="col-sm-4"> <input type="text" class="form-control" id="inputEmail3" placeholder="Email"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label">手机号</label> <div class="col-sm-4"> <input type="text" class="form-control" id="inputPassword3" placeholder="Password"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label">邮箱</label> <div class="col-sm-4"> <input type="email" class="form-control" id="inputPassword" placeholder="Password"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label">密码</label> <div class="col-sm-4"> <input type="password" class="form-control" id="inputPassword4" placeholder="Password"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label">头像</label> <div class="col-sm-4"> <input type="file" id="inputPassword5" placeholder="Password"> <span class="help-block">只支持jpg,png,gif格式</span> </div> </div> <hr> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label">属性</label> <div class="col-sm-10"> <div class="radio"> <label> <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked> Option one is this and that—be sure to include why it's great </label> </div> <div class="radio"> <label> <input type="radio" name="optionsRadios" id="optionsRadios2" value="option2"> Option two can be something else and selecting it will deselect option one </label> </div> <div class="radio disabled"> <label> <input type="radio" name="optionsRadios" id="optionsRadios3" value="option3" disabled> Option three is disabled </label> </div> </div> </div> </form> </div> </div> <!--下一步按钮--> <button class="btn btn-success pull-right">下一步</button> </div> </div> </div> </body> </html>
先在view里创建一个index的函数
def index(request):
return render(request,"index.html")
这里我们需要导入HttpResponse,然后再url.py新增index的路径
from . import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^index/', views.index), ]
在login函数里写如下代码
def login(request): if request.method == 'POST': email = request.POST.get('email') pwd = request.POST.get('pwd') if email == 'zouzou' and pwd == '123': return redirect('/index/') # return HttpResponse('ok') else: return render(request,'login.html')
这样我们如果输入的是zouzou和123,则会跳转到index页面,那如果我们需要错误的时候给我们提示怎么办呢?django提供了我们模版语言;{{变量名}},我们在login.html的button按钮下面加入如下代码
<button type="submit" id="b1" class="btn btn-block btn-primary">登录</button> <p style="color: red;text-align: center">{{ error_msg}}</p>
在去修改login函数里的代码
def login(request): error_msg='' if request.method == 'POST': #如果是post,表示提交数据 email = request.POST.get('email') pwd = request.POST.get('pwd') if email == 'zouzou' and pwd == '123': #登录成功跳转到index页面 return redirect('/index/') # return HttpResponse('ok') else: #登录失败,提示用户名密码错误 error_msg = '邮箱或密码错误' #如果是GET,表示要获取这个页面 return render(request,'login.html',{"error_msg":error_msg})
这样,我们输入正确的邮箱和密码就会跳转到index页面,错误的会提示
app
上面我们把login函数和index函数都放到了views.py文件里面。试想一下,如果我们的项目有成百上千个函数呢?放到一个py文件里是不是很麻烦。
python给我们提供了app的功能,我们可以把一个模块放到一个app里,可以有多个app,不同的功能放到不同的app里面。
创建app:
如下我们创建了一个叫“appTest01”的项目
python manage.py startapp appTest01
创建完app后,我们要告诉Django我们创建了一个叫“appTest01”的app,去setting.py里加上下面的代码。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'appTest01.apps.Apptest01Config', # 告诉django我们创建了一个叫appTest01的app # 'appTest01', # 上面代码的简写方式 ]
上面的设置好了之后,我们把之前在view.py里写的代码放到刚创建的appTest01下的view.py里面,然后去url.py里把导入的view.py改一下。启动我们的项目,访问登录页面,能正常访问就表示我们的设置是没有问题的
ORM
前面我们的邮箱和密码是写死的。我们可不可以从数据库里面读取数据做一个判断呢?这时我们就要用到ORM了,那什么是ORM呢?
ORM就是一群写代码的,懒的写sql语句,写了一个工具,自动生成sql语句
ORM:Object Relational Mapping(关系对象映射)
类名对应------------》数据库中的表名
类属性对应---------》数据库里的字段
类实例对应---------》数据库表里的一行数据
ORM的优势
Django的orm操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite....,如果数据库移只需要更换Django的数据库引擎即可
ORM的缺点就是执行效率低
1.创建数据库
由于ORM没有没有创建数据库的方法,所以我们手动创建一个数据库:
create database mysite;
2.告诉Django连接哪个数据库
因为Django默认的数据库是sqlite3,我们要使用mysql,所以要告诉mysql,去setting.py里修改如下代码
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 连接数据库的类型 'NAME': 'mysite', # 数据库名 'HOST': '127.0.0.1', # 数据库主机地址 'PORT': 3306, # 数据库的端口 'USER': 'root', # 登录数据库的用户名 'PASSWORD': '123456', # 登录数据库的密码 } }
3.用什么连接数据库
上面我们已经告诉了django连接哪个数据库,那用什么连?利用第三方的包,比如第三方包:pymysql和MySQLdb,因为MySQLdb不支持python3,所以我们使用pymysql来连接数据库,之前也写过pymysql连接mysql的文章
去和setting.py同级的__init__.py里告诉Django用pymysql模块代替默认的MySQLdb去连接MySQL数据库,写上如下代码
import pymysql pymysql.install_as_MySQLdb()
4.创建数据表
数据库已经配置好了,也告诉了用什么连,然后我们使用ORM去创建一个数据表。
在appTest01/models.py的文件中创建类(只能在这里面创建),类必须继承models.Model
from django.db import models # Create your models here. class User(models.Model): id = models.AutoField(primary_key=True) # 创建一个自增的id作为主键 email = models.CharField(max_length=32) # varchar(32) pwd = models.CharField(max_length=32) # varchar(32)
代码解释:
我们创建了一个User的类,上面也说过,类名对应的数据库表,所以会给我们创建一个User的数据表。类下面的就是ORM的语法,创建了三个字段,分别是id(主键)和长度为varchar(32)的email和pwd字段
5.生成数据表
现在我们已经万事具备,只欠东风了,只要生成数据表就可以了,ORM提供了我们两条命令来生成数据表
1.
python manage.py makemigrations #根据app下的migrations目录中的记录,检测当前model层代码是否发生变化
结果:
Migrations for 'appTest01': appTest01\migrations\0001_initial.py - Create model User
2.执行完下面的代码如果都显示OK就表示数据表创建成功了
python manage.py migrate #把orm代码转换成sql语句去数据库执行
上面是对所有的 app 进行迁移,也可以针对某些单独的app进行迁移
# python manage.py makemigrations <appname> # python manage.py migrate <appname>
使用pycharm连接mysql数据库
点击右边的DataBase
使用ORM查询数据
上面的apptest01_user是我们创建的表,其他的都是django默认帮我们创建的。我们往数据表里插几条数据
这样,数据我们有了,那我们怎么获取到数据库里的数据和用户输入的数据对比呢?ORM提供了我们一种User.objects.filter(email='', pwd='') 的方法来查询数据
注意:前面的Uer是我们的数据表名,email和pwd是我们数据表里的字段,那我们是不是可以修改一下登录函数了呢
from django.shortcuts import render,HttpResponse,redirect from appTest01.models import User def login(request): error_msg='' if request.method == 'POST': #如果是post,表示提交数据 ema = request.POST.get('email') password = request.POST.get('pwd') res = User.objects.filter(email=ema,pwd=password) print(res) if res: #登录成功跳转到index页面 return redirect('/index/') # return HttpResponse('ok') else: #登录失败,提示用户名密码错误 error_msg = '邮箱或密码错误' #如果是GET,表示要获取这个页面 return render(request,'login.html',{"error_msg":error_msg}) def index(request): return render(request,"index.html")
如果查询到res的值就是
<QuerySet [<User: User object>]>
查询不到res的值就是
<QuerySet []>
如果要获取对应的值,可以用下面的方法
print(res[0].id,res[0].email,res[0].pwd) # 获取到数据库里对应的字段
对象.属性《-----》数据表里具体字段的值
总结:
1. form表单提交数据的三个要素
1.1. form标签必须要有action和method属性
1.2. 所有获取用户输入的标签必须放在form表单中,必须要有name属性
1.3. 必须要有submit按钮
2. Django 基础必会三件套
from django.shortcuts import HttpResponse, render, redirect
2.1. HttpResponse 返回一个指定的字符串时
2.2. render 返回一个HTML文件
2.3. redirect 跳转
3. request相关的属性
3.1. request.method --> 返回的是请求的方法(全大写):GET/POST ...
3.2. request.GET --> 取得是URL里面的参数,类似于字典的数据结构
3.3. request.POST --> post提交的数据,类似于字典的数据结构
4. Django的模板语言
{{ 变量名 }}
5. Django项目app --> 项目中又分了一级Python包,不同的功能放到不同的包里面
5.1. 创建app
python manage.py startapp app01
5.2. 告诉Django创建了一个app
在settings.py找那个的INSTALLED_APPS中添加新创建的app
6. Django中ORM的使用
6.1. 用处
6.1.1. 操作数据表
6.2.2. 操作数据行
6.2. 使用
6.2.1. 手动创建一个数据库
-> create database mysite;
6.2.2. 告诉Django连哪个数据库
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 连接数据库的类型 'NAME': 'mysite', # 数据库名 'HOST': '127.0.0.1', # 数据库主机地址 'PORT': 3306, # 数据库的端口 'USER': 'root', 'PASSWORD': '123456', } }
6.3. 用什么连数据库?
利用第三方的包,比如第三方包:pymysql和MySQLdb
告诉Django用pymysql模块代替默认的MySQLdb去连接MySQL数据库
和settings.py同级的__init__.py文件,写上:
import pymysql pymysql.install_as_MySQLdb()
6.4. 在app/models.py的文件中创建类,只能在这里面创建
类必须继承models.Model
6.5. 两个命令
6.5.1. python manage.py makemigrations --> 把models.py的变更记录一下,注意要保证数据库里的和ORM变更记录一样,否则会报错
6.5.2. python manage.py migrate --> 把上面的变更记录翻译成SQL语句,去数据库执行
6.5.3. ORM查询
User.objects.filter(email='', pwd='')
6.5.4.数据表名或结构变了都要重新执行一下6.5.1和6.5.2