昨日内容回顾
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 协议响应格式封装响应数据----response
4 基于 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(逻辑控制部分)
MTV
M : 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 8080
Performing 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:29
Django 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 admin
from django.urls import path
from blog import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',views.index),
]
视图
修改 blog 目录下的 views.py,增加 index 视图函数
from django.shortcuts import render
import 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 render
import 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,HttpResponse
import 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,HttpResponse
import 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,HttpResponse
import 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,就可以完美解决这个问题!!!
未完待续…