昨日内容回顾
1. socket 创建服务器2. http 协议:请求协议请求首行 请求方式 url?a=1&b=2 协议请求头 key:value请求体 a=1&b=2(只有 post 请求才有请求体)响应协议响应首行 协议 状态码 文本响应头 key:value响应体 html 字符串3. wsgiref 模块(基于 wsgi 协议)功能:1. 按着 http 协议请求格式解析请求数据----envision:{}2. 按着 http 协议响应格式封装响应数据----response4 基于 wsgiref 实现了一个简单 web 框架1. urls : 存放路由关系2 views: 存放视图函数3 templates: 存放 html 文件4 wsgi-sever:启动文件
Django 简介
知识预览
- MVC 与 MTV 模型
- Django 的下载与基本命令
- 基于 Django 实现的一个简单示例
MVC 与 MTV 模型
MVC
Web 服务器开发领域里著名的 MVC 模式,所谓 MVC 就是把 Web 应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:
mvc 主要用于 web 框架,常用的开发语言,有 java,php,node.js 等等。
web 框架应用最广泛就是 PHP 了,它只能做 web 开发,而且开发效率很快。
MTV
Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是值:
M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
T 代表模板 (Template):负责如何把页面展示给用户(html)。
V 代表视图(View): 负责业务逻辑,并在适当时候调用 Model 和 Template。
除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:
一般是用户通过浏览器向我们的服务器发起一个请求(request),这个请求回去访问视图函数,(如果不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户),视图函数调用模型,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
这里面最难的部分就是 model,后面会慢慢讲到。
django 有一个 ORM,它是专门来操作数据库的。这套语法,需要大量练习才能掌握。
MVC 和 MTV 模型的区别:
MVC:M : model (与数据库打交道)V : views (存放html文件)C : Controller(逻辑控制部分)MTVM : model (与数据库打交道)T : templates (存放html文件)V : views (逻辑处理)+路由控制层(分发哪一个路径由哪一个视图函数处理),它没有单独的分层。它作为URL分发器,将url请求分发给不同的view处理
Django 的下载与基本命令
1、下载 Django:
pip3 install django
2、创建一个 django project
windows 用户,以管理员身份打开一个 cmd 窗口。进入一个空目录,运行以下命令:
E:\python_script\django 框架\day2>django-admin startproject mysite
当前目录下会生成 mysite 的工程,目录结构如下:
mysite/├── manage.py└── mysite├── __init__.py├── settings.py├── urls.py└── wsgi.py
manage.py ——- Django 项目里面的工具,通过它可以调用 django shell 和数据库等。
settings.py —— 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
urls.py ——- 负责把 URL 模式映射到应用程序。
manage.py : 它不关是启动文件,它还是与 Django 交互的文件。比如:
python manage.py runserver : 运行项目python manage.py startapp : 创建应用
如果运行项目时,不指定端口,默认监听本机的 8000 端口。
3、在 mysite 目录下创建应用
进入 mysite 目录
E:\python_script\django 框架\day2>cd mysite
创建应用 blog
目录结构如下:
E:\python_script\django 框架\day2\mysite>python manage.py startapp blog
mysite/├── blog│ ├── admin.py│ ├── apps.py│ ├── __init__.py│ ├── migrations│ │ └── __init__.py│ ├── models.py│ ├── tests.py│ └── views.py├── manage.py└── mysite├── __init__.py├── settings.py├── urls.py└── wsgi.py
views.py—-存放视图函数
models—与数据库打交道
还有一个目录 templates,它是用来存放 html 文件的,下面会讲到。
从上面的目录结构可以看出,mysite 目录下有一个 blog。那么顶层的 mysite,叫做 项目。底层的 blog 叫做应用。
比如微信是一个项目。聊天,朋友圈,支付…都是应用。
项目是必须包含应用的,项目可以包含多个应用。
mysite 下的 mysite,是全局文件,它有 2 个全局配置文件,一个是 settings.py(项目配置文件),一个是 urls.py(路由控制文件)。
wsgi.py 是封装 socket,用来接收和响应请求的。这个文件,从来都不需要动。
4、启动 django 项目
E:\python_script\django 框架\day2\mysite>python manage.py runserver 8080Performing system checks...System check identified no issues (0 silenced).You have 14 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.June 21, 2018 - 19:33:29Django version 2.0.6, using settings 'mysite.settings'Starting development server at http://127.0.0.1:8080/Quit the server with CTRL-BREAK.
这样我们的 django 就启动起来了!当我们访问: http://127.0.0.1:8080/时就可以看到:
基于 Django 实现的一个简单示例
url 控制器
修改 mysite 目录下的 urls.py,增加 index 路径
注意:index 后面不要加括号。直接 views.index 即可
必须导入 blog 应用的 views 模块,否则它找不到对应的视图函数
from django.contrib import adminfrom django.urls import pathfrom blog import viewsurlpatterns = [path('admin/', admin.site.urls),path('index/',views.index),]
视图
修改 blog 目录下的 views.py,增加 index 视图函数
from django.shortcuts import renderimport datetime# Create your views here.def index(request):now=datetime.datetime.now()ctime=now.strftime("%Y-%m-%d %X")return render(request,"index.html",{"ctime":ctime})
request,它是一个对象。存储了请求信息,比如请求路径,请求方式,GET 数据,POST 数据…等等。
request 参数必须要有,不管你用不用它。
模板
新建文件夹 templates,在此目录创建 index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h4>当前时间:{{ ctime }}</h4></body></html>
修改 mysite 目录下的 settings.py,指定模板目录为 templates,修改部分如下:
TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [os.path.join(BASE_DIR, 'templates')],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},]
访问网页,效果如下:
django 有一个好处,代码更改之后,它会自动加载代码。而不需要重启 django 项目,网页就能更新了!
增加登录页面
修改 mysite 目录下的 urls.py,新增一个 login
urlpatterns = [path('admin/', admin.site.urls),path('index/',views.index),path('login/',views.login),]
在 templates 目录下创建文件 login.html
注意:form 表单的标签名是 form,不是 from。from 是 MySQL 的关键字,不要弄混淆了。否则点击提交按钮,是没有反应的。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><form action=""><lable>用户名</lable><input type="text" name="user"/><lable>用户名</lable><input type="password" name="pwd"/><input type="submit"></form></body></html>
修改 blog 目录下的 views.py,增加 login 视图函数
from django.shortcuts import renderimport datetime# Create your views here.def index(request):now=datetime.datetime.now()ctime=now.strftime("%Y-%m-%d %X")return render(request,"index.html",{"ctime":ctime})def login(request):return render(request,"login.html")
访问登录页面,效果如下:
为什么 render 能找到 login.html 文件呢?
因为 setting.py 文件里面定义了 template 路径。render 方法,是用来渲染模板的,它会从 TEMPLATES 配置的路径中去寻找 html 文件。
如果修改 DIRS 里面的文件名,比如改为 abc
'DIRS': [os.path.join(BASE_DIR, 'abc')],
访问页面,会报错
重新修改回来,再次访问,就正常了。
修改 urls.py,增加 auth 路径,用来做验证的。
urlpatterns = [path('admin/', admin.site.urls),path('index/',views.index),path('login/',views.login),path('auth/',views.auth),]
修改 login.html 文件,改为 post 请求
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><form action="/auth/" method="post"><lable>用户名</lable><input type="text" name="user"/><lable>用户名</lable><input type="password" name="pwd"/><input type="submit"></form></body></html>
修改 views.py 文件,增加 auth 视图函数
from django.shortcuts import render,HttpResponseimport datetime# Create your views here.def index(request):now=datetime.datetime.now()ctime=now.strftime("%Y-%m-%d %X")return render(request,"index.html",{"ctime":ctime})def login(request):return render(request,"login.html")def auth(request):print(request.path) # 路径print(request.method) # 请求方式print(request.GET) # GET数据print(request.POST) # POST数据return HttpResponse("OK")
访问登录页面,输入数据,点击提交
页面输出 403,被 CSRF 拦截了。
CSRF:跨站请求伪造,常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。
后面的课程会讲到,如何避免 CSRF。修改 settings.py 里面的 MIDDLEWARE 配置项,关闭 CSRF
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',# 'django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',]
访问方式
访问方式有 2 种,GET 和 POST
在地址栏中,只有 GET 请求。
在 form 表单中,有 GET 和 POST。它是根据 method 属性决定的!一般表单使用 POST
再次访问 url,输入表单信息,点击提交。
输出 ok,表示正常。注意:这里还没有做登录认证,下面会讲到!
查看 cmd 窗口输出信息:
/auth/POST<QueryDict: {}><QueryDict: {'user': ['xiao'], 'pwd': ['123']}>
可以看到:
路径:/auth/。请求方式:POST。GET 数据为空。POST 数据是一个字典。
地址栏直接输入:
http://127.0.0.1:8080/auth/?u=xiao,sex=m
查看 cmd 窗口输出信息:
/auth/GET<QueryDict: {'u': ['xiao,sex=m']}><QueryDict: {}>
登录认证
正常网站,用户名和密码是保存在数据库中。由于还没有学到 django 连接数据库,所以这里将用户名和密码写死,模拟登录行为。
修改 views.py,获取用户和密码,进行判断。
from django.shortcuts import render,HttpResponseimport datetime# Create your views here.def index(request):now=datetime.datetime.now()ctime=now.strftime("%Y-%m-%d %X")return render(request,"index.html",{"ctime":ctime})def login(request):return render(request,"login.html")def auth(request):user = request.POST.get('user') # 获取用户名pwd = request.POST.get('pwd') # 获取密码print(user,pwd)#判断用户名和密码if user == 'xiao' and pwd == '123':return HttpResponse("登录成功") # 返回响应体给浏览器,显示"登录成功"文字else:return render(request,"login.html") # 返回响应体-->login.html文件内容
重新访问登录页面,输入正确的用户名和密码
页面提示,成功。
访问过程分析
访问登录页面时,经历 3 个步骤
(1) http://127.0.0.1:8000/login/ get 请求 无数据(2) path('login/',views.login), 调转视图函数 login(request)(3) login 执行视图函数,响应了一个 login.html 页面
解释:
首先是用户在浏览器输入 url: http://127.0.0.1:8000/login/
django 接收到请求之后,根据 URL 控制器匹配视图函数
执行视图函数 login,响应请求给浏览器一个 login.html 页面。
查看 views.py 文件的 login 视图函数
render(request,"login.html")
上面的代码就是响应体。那么浏览器如何得到 response 信息的呢?封装 response 信息是由 wsgi 来完成的。
点击提交按钮的操作,也经历 3 个步骤
(1) http://127.0.0.1:8000/auth/ post 请求,数据为 user=xiao&pwd=123(2) path('auth/',views.auth), 调取视图函数 auth(request)(3) auth 执行视图函数, if 登陆成功:响应一个字符串登陆成功。else: 响应了一个登陆页面
解释:
- 虽然 form 的 action 属性值为”/auth/“,但是执行提交动作时,浏览器会查看 action 属性,如果为相对路径。那么会获取当前 url 的域名/IP 加端口。和 action 属性拼接,得到完整的 url,比如: http://127.0.0.1:8000/auth/ 。将表单数据以 POST 方式发送给此 url。
注意:推荐使用这种写法。如果 action 写成完整的 url(比如:** [http://127.0.0.1:8000/auth/)**](http://127.0.0.1:8000/auth/))** **,遇到服务器迁移时。那么涉及到的 html 文件,都需要更改,非常耗时耗力!
如果采用相对路径方式,那么不需要改动代码,它会自动拼接,完美解决这个问题。
比如写/auth/,会自动拼接为** [http://127.0.0.1:8000/auth/**](http://127.0.0.1:8000/auth/)
如果 action 为””,也就是空,它会拼接当前的完整 ur。
比如访问登录页面,那么 action 的属性值为 当前 url,比如:** [http://127.0.0.1:8000/login/**](http://127.0.0.1:8000/login/)
django 接收到请求之后,根据 URL 控制器匹配视图函数 auth
执行视图函数,如果用户名和密码正确,页面显示登录成功。否则,页面还是显示登录页面。
上面提到的 2 个场景,它们之间,是没有任何关系的。
每一个请求,对于服务器而言,都是一个新的请求。
思考一个问题,能够将 login 和 auth 视图函数合并?
答案是可以的。
更改 login.html,将 action 属性设置为空(参考上面的步骤 1 解释)
<form action="" method="post">
更改 views.py,删除 auth 视图函数代码,修改 login 视图函数,完整代码如下:
from django.shortcuts import render,HttpResponseimport datetime# Create your views here.def index(request):now=datetime.datetime.now()ctime=now.strftime("%Y-%m-%d %X")return render(request,"index.html",{"ctime":ctime})def login(request):#判断请求是否为POST,必须为大写if request.method == "POST":user = request.POST.get('user') # 获取用户名pwd = request.POST.get('pwd') # 获取密码print(user, pwd)# 判断用户名和密码if user == 'xiao' and pwd == '123':return HttpResponse("登录成功") # 返回响应体给浏览器,显示"登录成功"文字else:return render(request, "login.html") # 返回响应体-->login.html文件内容return render(request,"login.html") # 默认输出登录页面
修改 urls.py,删除 auth 路径
urlpatterns = [path('admin/', admin.site.urls),path('index/',views.index),path('login/',views.login),]
重新访问登录页面,输入正确的用户和密码,点击提交。页面输出:
这就用到了 if 分支。
能尽量合成视图函数的,推荐合成。如果逻辑简单,可以合成。
逻辑比较复杂的,还是建议分开。
视图函数,必须返回一个 HttpResponse 对象。HttpResponse 是一个对象,对象里面,放字符串。
HttpResponse 会自动将字符串转换为字节
django 要求视图函数,必须返回一个 HttpResponse 对象。
模拟 render 操作
修改 login 函数,else 部分是重点
def login(request):#判断请求是否为POST,必须为大写if request.method == "POST":user = request.POST.get('user') # 获取用户名pwd = request.POST.get('pwd') # 获取密码print(user, pwd)# 判断用户名和密码if user == 'xiao' and pwd == '123':return HttpResponse("登录成功") # 返回响应体给浏览器,显示"登录成功"文字else:from mysite import settings # 导入settings模块import os# 拼接login.html的绝对路径path = os.path.join(settings.BASE_DIR,"templates","login.html")with open(path,encoding="utf-8") as f:data = f.read() # 读取文件所有内容print("data",data+'aaaaa')#返回给浏览器并加上一段话return HttpResponse(data+'用户名和密码错误')# return render(request, "login.html") # 返回响应体-->login.html文件内容return render(request,"login.html") # 默认输出登录页面
访问 url: http://127.0.0.1:8000/login/
输入一个错误的密码,点击提交
页面输出,用户名和密码错误
那么,render 就是干了这些事情。
总结:
对于 Django 而言,一次请求必须返回一个 HttpResponse(字符串)
request 对象,存放了请求路径,请求方式,请求数据,比如 GET 和 POST
所以对于视图函数而言,最关心的部分就是 request 和 HttpResponse
一次请求,必有一次响应。如果没有响应,就会报错
范围 url: http://127.0.0.1:8000/index/
在视图函数中,render 是渲染的意思。那么它是如何工作的呢?
1 按着 settings-TEMPLATES-DIRS 路径找指定文件2 读取文件所有字符串3 渲染: 检查字符串中是否有{{变量}} ,if 没有找到:HttpResponse(文件字符串)else找到 {{变量}},用 render 第三个参数中的对应值进行相应替换(如果没有找到对应值,{{变量}}替换为空)HttpResponse(替换后的文件字符串)
那么渲染的过程,是在后端完成的。不是前端完成的。
看 html 代码,就知道了。浏览器根本不认识{{变量}},它只能识别 html,css,js
注意:如果模板里面,写了{{变量}} 。但是 render 没传,那么页面中{{变量}} 会被替换为空。
如果模板里面,写了{{ }} 。变量名没写,那么页面报错
如果 render 传了变量,但是模板里{{变量}} ,变量名写错了,页面中{{变量}} 也会被替换为空。
思考:如何点击时间的时候,变成红色?
直接加行内样式?不对,它是点击的时候,才变成红色。
需要引入 jquery 来做,修改 index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script></head><body><h4>当前时间: <span class="time">{{ ctime }}</span></h4><script type="application/javascript">$(function(){$('.time').click(function () {$(this).css("color","red")})});</script></body></html>
访问 url: http://127.0.0.1:8000/index/
点击时间,就会变红

但是,线上服务器不能这么干?为什么呢?因为如果一旦 jquery 访问链接失效。那么整个网站就崩溃了!
所以这种文件,还是需要放到自己的服务器上,才行!
那好办呀,将 jquery.min.js 放到 templates 目录。
编辑 index.html,直接引入 jquery.min.js 文件。
<script src="jquery.min.js"></script>
再次访问页面,怎么点击都没效果,查看控制台,点击网络部分,发现它是 404 了!

不要以为 templates 下的文件,可以随便访问。太天真了!
浏览器是不能直接访问 templates 下的文件,需要 Django 找到静态文件才行!
在根目录,创建 static 目录,它是专门存放静态文件的。
将 js 文件进去。项目目录结构如下:
mysite/├── blog│ ├── admin.py│ ├── apps.py│ ├── __init__.py│ ├── models.py│ ├── tests.py│ └── views.py├── manage.py├── mysite│ ├── __init__.py│ ├── settings.py│ ├── urls.py│ └── wsgi.py├── static│ └── jquery.min.js└── templates├── index.html└── login.html
修改 settings.py,最后一行添加,注意:STATIC_URL 和它是成对使用的。
STATIC_URL = '/static/'STATICFILES_DIRS = (os.path.join(BASE_DIR,"static"),)
STATIC_URL 参数,表示别名。
STATICFILES_DIRS 表示物理路径。
STATIC_URL 代指 STATICFILES_DIRS 定义的路径。
修改 index.html,更改 src 属性
<script src="/static/jquery.min.js"></script>
注意:这里面的/static/ 是别名,它代指的是物理路径
重新访问页面,再次点击,就会变红。

因为 diango 利用前缀 STATIC_URL 的具体内容,来映射 STATICFILES_DIRS, 那么它就可以找到具体的文件。
比如前台页面的静态资源路径,一般都是写死了,可能涉及到几百个网页。网站在运营过程中,难免后台服务器,需要做迁移工作,可能和之前的存储路径不一样的。这个时候,让前端去改几百个网页,是一个很繁杂的工作。现在只需要修改 STATIC_URL,就可以完美解决这个问题!!!
未完待续…
