上一节我们走完了用django开发的基本流程,现在我们可以逐步求精,彻底搞懂django怎么用了。
0x00 Web框架职责
- 充当socket服务端
- 根据URL不同返回不同的内容
- 返回给用户符合HTTP协议的字符串
所有的web框架都要做到这几个职责的一点或者多点。
django做到了2和3。django没有自己的socket server, 用的python的wsgiref模块。
我们的B/S架构到底是个什么玩意儿?
broswer充当socket客户端,server充当socket服务端,仅此而已。
所谓的broswer,只不过是功能非常强大的socket客户端,他的本质就是socket客户端。
0x01 Django的安装和使用
公共步骤:
pip3 install django
说句题外话,python的安装目录下有个叫Scripts的目录。
这个目录被加到了环境变量之中。所以我们才能直接在命令行用这个目录下的程序。
我们来看看这个目录下都有啥:
题外话结束。------
命令行方式创建django工程:
django-admin startproject mysite
pycharm方式创建django工程(需要pycharm专业版):
file—> New Project —>
手动启动django项目(在manage.py的同级目录下执行):
python manage.py runserver 127.0.0.1:8080
pycharm启动django项目:
选中之后,按绿色播放键即可。
0x02 django项目的目录结构
$ tree mysite
mysite
├── db.sqlite3 # 数据库文件
├── manage.py # 对当前django项目的所有操作都可以用manage.py
├── mysite # 存放django项目主配置、主路由的目录
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-37.pyc
│ │ ├── settings.cpython-37.pyc
│ │ ├── urls.cpython-37.pyc
│ │ └── wsgi.cpython-37.pyc
│ ├── asgi.py
│ ├── settings.py # django项目配置文件
│ ├── urls.py # 路由系统 url--->业务逻辑
│ └── wsgi.py # django用的socket server. 调用的wsgiref接口,开发用, 性能略差。以后可以用uwsgi, ngnix等
└── templates
其中我们无需关注pyc结尾的。因为这相当于是编译过的。
0x03 django初体验
我们来写第一个业务逻辑。(只是示例用,工作中把业务逻辑写在路由里会被人当场打死)
首先说一下版本差异:
django1.x 用的是:
from django.conf.urls import url
而django2.x用的是:
from django.urls import path
有啥区别呢?
Django1的url支持正则匹配:
'article-(\d+).html'
:使用正则表达式的分组匹配来获取URL中的参数,并以位置参数形式传递给视图article。'article-(?P<article_id>\d+).html'
:使用正则表达式的分组命名匹配来获取URL中的参数,并以关键字参数的形式传递给视图article。- 分组命名正则表达式组的语法是:
(?P<name>pattern)
,其中name是组的名称(视图中的关键字参数必须跟组名一致),pattern是正则表达式。
django1的url示例:
from django.conf.urls import url
# 使用url关键字
urlpatterns = [
url('article-(\d+).html',views.article),
url('article-(?P<article_id>\d+).html',views.article)
]
# url请求地址为:http://127.0.0.1:8000/article-1.html
Django2的path:
- path写的是绝对字符串,请求地址必须与路由地址完全匹配
- 使用尖括号
<>
从url中获取参数值 - 可以使用转换器指定参数类型,例如:
<int:age>
捕获一个整数参数age,若果没有转化器,将匹配任何字符串,也包括路径分隔符/
path拥有5个转换器:
str
:匹配除路径分隔符/
外的字符串int
:匹配自然数slug
:匹配字母,数字,横杠及下划线组成的字符串uuid
:匹配uuid形式的数据path
:匹配任何字符串,包括路径分隔符/
总而言之,别管是django2还是django1的路由系统,要做的事情就是将url与业务逻辑代码匹配上,只是实现的方式略有不同。
django2更简单一些。
django2示例:
from django.urls import path
# 使用path关键字
urlpatterns = [
path('article-<int:article_id>.html',views.article),
]
# url请求地址为:http://127.0.0.1:8000/article-1.html
OK, 来写一个例子:
# urls.py
from django.urls import path
from django.shortcuts import HttpResponse
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return HttpResponse('vth')
urlpatterns = [
path('login/', login)
]
其中,HttpResponse是一个类。我们返回时要返回它的对象。
我们对对象进行构造的时候,只需要把内容放到构造器里即可。就如上面代码所示。
0x04 发现问题—难道要这样写前端页面?
学会了上述知识,我们开心的去写返回给前端的页面了。
#urls.py
from django.urls import path
from django.shortcuts import HttpResponse
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return HttpResponse('<h1>vth的标题</h1>')
urlpatterns = [
path('login/', login)
]
我们这次返回的是h1标签。
然而我们发现,要是写很复杂很复杂的页面,那咋整???
我们成功的由后端开发工程师转型成全栈了???
4.1 使用模板
没有没有,django怎么会这么low。我们可以直接返回html文件。
我们想返回html文件只需要三步:
urls.py
中引入把index.html
渲染成HttpResponse
的函数- 在
templates
下新建index.html
- 在
urls.py
中返回render
函数渲染的index.html
urls.py
#urls.py
from django.urls import path
from django.shortcuts import HttpResponse, render
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
# return HttpResponse('<h1>vth的标题</h1>')
return render(requests, 'index.html')
urlpatterns = [
path('login/', login)
]
templates/index.html
<!--templates/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
</head>
<body>
<h1>vth的标题</h1>
</body>
</html>
看下效果:
完全ok。新技能get.
but, 心虚不??为啥在templates目录下新建, django就能找到这个文件?
我们觉得templates难听,就要用xxxxx, 行不行?
解决django在哪找文件,属于django的配置问题,所以我们应该去settings.py找。
这里和存放templates的目录是一样的。也就是说是从这里配置的。
4.2 使用CSS
OK,这个问题解决了。我们又提出了新的需求。
前端代码,HTML负责骨架,CSS负责美容。那么,我们想写CSS怎么写?众所周知我们有3大引入方式。为了代码的可读性,我们打算用文件引入的方式来写CSS。
首先我们应该得给CSS单独弄个目录吧,以后好管理。取名叫style吧。
弄好后的目录结构是这样的:
$ tree mysite
mysite
├── db.sqlite3
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-37.pyc
│ │ ├── settings.cpython-37.pyc
│ │ ├── urls.cpython-37.pyc
│ │ └── wsgi.cpython-37.pyc
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── style
│ └── index.css
└── templates
└── index.html
CSS的内容如下:
h1{
color: red;
}
然后我们修改index.html,让它引入这个css文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>vth的标题</h1>
</body>
</html>
我们的href中写的是/static/index.css
, 先不用care这点。
然后我们刷新下页面,发现没变。意思是我们写的css并没有生效。那咋整? 代表着django找不到我们写的css。所以这还是个配置问题。如法炮制,去settings.py改。
拉到最下面,改成这样:
我们新加了STATICFILES_DIRS
。 它是一个元组。元素为字符串。
第二个参数style就是我们存放静态文件CSS的目录了。
加好后我们刷新页面,发现CSS已经能发挥作用了。
咦?还记得为啥我们的index.html中要写成:/static/index.css
么?
解释一下,STATIC_URL
是一个静态的URL地址,我们在引入文件时,它应当充当前缀。
我们虽然写的是static
, 但是由于我们配置了STATICFILES_DIRS
, 所以django
明白实际上应该去style
找CSS
文件。
0x05 写个登录示例
开干吧。
首先我们修改index.html, 改造成表单。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>vth的标题</h1>
<form method="POST" action="/login/">
<input type="text" name="user">
<input type="password" name="pwd">
<input type="submit" value="登录">
</form>
</body>
</html>
注意,action
后面的/login/
的/
不能省。不然会报错。
然后我们修改我们的业务逻辑。
因为在打开一个页面的时候用的是get方法,所以我们可以认为页面使用的是get方法时为未登录状态,返回给它登录页面。
如果是post方法,就验证帐号密码对不对,如果对,重定向到百度。如果不对,则返回登录页面。
#urls.py
from django.contrib import admin
from django.urls import path
from django.shortcuts import HttpResponse, render, redirect
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
if requests.method == 'GET':
# get请求
return render(requests, 'index.html')
elif requests.method == 'POST':
print(requests.POST)
if requests.POST.get('user') == 'vth' and requests.POST.get('pwd') == '123':
# 登录成功
return redirect('http://www.baidu.com')
else:
# 帐号密码错误
return render(requests, 'index.html')
urlpatterns = [
path('login/', login)
]
5.1 requests中的属性总结
method: 标识着请求用的是http的什么方法
POST: 去请求体中拿数据,请求体中的数据是KV对,所以这里面也是KV对。用什么就用POST的get方法拿什么
GET: 从请求的url中拿数据,也是KV对,取值方式同上。
5.2 可返回的值的类型总结
HttpResponse('str')
这里返回的是一个HttpResponse对象,我们只需要把字符串丢进去即可。
render(requests, 'xxx.html', {'name':'test'})
这里返回的是被第三个参数渲染的第二个参数的模板文件。
redirect('url')
这里返回的是一个重定向的url.重定向到谁就写谁。
0x06 jinja2 模板渲染规则
6.1 普通字符串渲染
模板中用{{name}}
, python代码render
的时候,传一个字典,字典要包含name
这个key
, 字典中name这个key对应的value类型为字符串。
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>{{ name }}</h1>
</body>
</html>
# urls.py
from django.urls import path
from django.shortcuts import HttpResponse, render, redirect
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return render(requests, 'index.html', {'name':'vth'})
urlpatterns = [
path('login/', login)
]
效果:
6.2 value为列表的渲染
模板中写{{name.index}}
, python代码中写 {'name':['a','b']}
.
我们来修改一下代码:
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>{{ name.0 }}</h1>
<h1>{{ name.1 }}</h1>
</body>
</html>
# urls.py
from django.urls import path
from django.shortcuts import HttpResponse, render, redirect
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return render(requests, 'index.html', {'name':[
'vth',
'ygm'
]})
urlpatterns = [
path('login/', login)
]
6.3 value为字典的渲染
说一个概念。在前端的世界里,数组的下标和字典的Key,都叫Index. 所以在jinja2中的使用方式跟数组是一样的。
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>{{ name.age }}</h1>
<h1>{{ name.sex }}</h1>
</body>
</html>
from django.urls import path
from django.shortcuts import HttpResponse, render, redirect
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return render(requests, 'index.html', {'name':{
'age': 18,
'sex': '男'
}})
urlpatterns = [
path('login/', login)
]
诶?等等??难不成只能这么low???不能遍历吗? 当然可以!
6.4 遍历列表的渲染
首先来说一下遍历的语法:
{% for item in name %}
{{ item }}
{% endfor %}
其中,for
in
endfor
是关键字。
来看一下:
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div>
{% for item in name %}
<h1>{{ item }}</h1>
{% endfor %}
</div>
</body>
</html>
# urls.py
from django.urls import path
from django.shortcuts import HttpResponse, render, redirect
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return render(requests, 'index.html', {'name':[
'vth','ygm'
]})
urlpatterns = [
path('login/', login)
]
6.5 遍历字典的渲染
规则:
{% for key, value in row.items %}
{{ key }} {{ value }}
{% endfor %}
示例:
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vth的小酒馆</title>
<link href="/static/index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div>
{% for key,value in name.items %}
<h1>{{ key }} ----- {{ value }}</h1>
{% endfor %}
</div>
</body>
</html>
# urls.py
from django.urls import path
from django.shortcuts import HttpResponse, render, redirect
def login(requests):
'''
处理用户请求,并返回内容
:param requests: 用户请求相关的所有信息, object
:return:
'''
return render(requests, 'index.html', {'name':{
'age': 18,
'sex': '男'
}})
urlpatterns = [
path('login/', login)
]
6.6 jinja2的最牛B之处
jinja2最牛B的地方就在于它可以跟html混搭。如6.5所示,我们可以在html标签里写jinja2语法,jinja2的语法之中也可以有html标签。
6.7 特别提醒
jinja2的for循环边界,{%
、%}
应该紧挨着,不能有空格,不然就SB了。