初探Django框架
初探Django框架
innodb引擎engine支持事务操作
只能説菜鸟教程是真的好用, 最近学习的perl,LUA都是在菜鸟教程看的(菜鸟看菜鸟hhh), 虽说内容并没有很深入, 但是也能够达到初步了解的效果了。难受的是因为要测试的网站是通过Django+Nginx+uwsgi
搭建的, 直接看源码让我一头雾水。也就不得不来学习一下Django框架, 所以就摘了一些我觉得比较重要的内容敲一遍, 然后把内容贴到这里(再加上我的碎碎念)。相信之前没学习过Django的小伙伴直接看完这篇文章应该也可以初步理解这个框架了。如果说想了解更详细的内容可以参考 Django 教程 和 官方手册, Django中文手册。
MTV模型
首先要知道一点, Django框架使用的并不是我们常见的MVC模型而是MTV模型, 提前了解这点对我们理解后面对各个模型的作用和框架的整体工作流程是非常有帮助的。MVC模型我想就不必介绍了, 下面直接了解一下什么是MTV模型吧。
创建项目
django-admin startproject HelloWorld
$ cd HelloWorld/
$ tree
.
|-- HelloWorld
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
`-- manage.py
目录说明:
HelloWorld: 项目的容器。
manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
HelloWorld/__init__.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
HelloWorld/asgi.py: 一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。
HelloWorld/settings.py: 该 Django 项目的设置/配置。
HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
配置视图和URL
views.py
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello world ! ")
绑定 URL 与视图函数
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.hello),path('hello/', views.hello),
]
得到项目结构:
$ tree
.
|-- HelloWorld
| |-- __init__.py
| |-- __init__.pyc
| |-- settings.py
| |-- settings.pyc
| |-- urls.py # url 配置
| |-- urls.pyc
| |-- views.py # 添加的视图文件
| |-- views.pyc # 编译后的视图文件
| |-- wsgi.py
| `-- wsgi.pyc
`-- manage.py
说明一下设置项目路径的path函数:
path(route, view, kwargs=None, name=None)
Django path() 可以接收四个参数,分别是两个必选参数:route、view 和两个可选参数:kwargs、name。
route: 字符串,表示 URL 规则,与之匹配的 URL 会执行对应的第二个参数 view。
view: 用于执行与正则表达式匹配的 URL 请求。
kwargs: 视图使用的字典类型的参数。
name: 用来反向获取 URL。
Django2. 0中可以使用 re_path() 方法来兼容 1.x 版本中的 url() 方法,一些正则表达式的规则也可以通过 re_path() 来实现
Django 模板
想要使用模板那么我们要先在容器下创建一个模板目录templates
, 然后通过settings.py文件设置模板路径
settings.py
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',
],
},
},
]
然后新增一个使用模板的视图:
views.py
from django.shortcuts import render
def runoob(request):
context = {}
context['hello'] = 'Hello World!'
return render(request, 'runoob.html', context)
在添加一个将内容交给模板视图处理的url
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('runoob/', views.runoob),
]
然后访问可见到输出Hello
模板的一些标签的语法和Flask的差不多, 变量使用{{ x }} 运算使用{% x %}, 而且种类也不是非常的多所以就不一一介绍了。
但是过滤器还是挺有意思的{{ 变量名 | 过滤器:可选参数 }}
, 模板过滤器有不少, 而且还可以叠加使用, 所以这就可以用来绕一些WAF。有一个比较有意思的过滤器是safe , 如果输出的内容经过这个过滤器的话那么Django就不会对其进行转义,可以让该数据语义生效。(默认情况下Django 会自动对 views.py 传到HTML文件中的标签如跳转标签, script标签
语法进行转义,令其语义失效)。{{ views_str|safe }}
include 标签
这个标签还是值得一说的, 因为 {% include %} 标签允许在模板中包含其它的模板的内容。
{% include "1.html" %}
#这个例子都包含了 1.html 模板:
csrf_token
csrf_token 用于form表单中,作用是跨站请求伪造保护。
如果不用{% csrf_token %}标签,在用 form 表单时,要再次跳转页面会报403权限错误。
用了{% csrf_token %}标签,在 form 表单提交数据时,才会成功。
首先,向服务器发送请求,获取登录页面,此时中间件 csrf 会自动生成一个隐藏input标签,该标签里的 value 属性的值是一个随机的字符串,用户获取到登录页面的同时也获取到了这个隐藏的input标签。
然后,等用户需要用到form表单提交数据的时候,会携带这个 input 标签一起提交给中间件 csrf,原因是 form 表单提交数据时,会包括所有的 input 标签,中间件 csrf 接收到数据时,会判断,这个随机字符串是不是第一次它发给用户的那个,如果是,则数据提交成功,如果不是,则返回403权限错误。
自定义标签和过滤器
templatetags 目录(与 templates 目录同级,目录名只能是 templatetags)
HelloWorld/
|-- HelloWorld
| |-- __init__.py
| |-- __init__.pyc
| |-- settings.py
...
|-- manage.py
`-- templatetags
`-- templates
接着在settings.py的OPTIONS的后面加上以下内容:
"libraries":{ 'my_tags':'templatetags.my_tags' }
然后在templatetags目录下创建一个添加标签的文件my_tags.py
from django import template
register = template.Library()
# 利用装饰器 @register.filter 自定义过滤器
@register.filter
def my_filter(v1, v2): # 注意:装饰器的参数最多只能有 2 个。
return v1 * v2
# 利用装饰器 @register.simple_tag 自定义标签。
@register.simple_tag
def my_tag1(v1, v2, v3):
return v1 * v2 * v3
在使用自定义标签和过滤器前,要在 html 文件 body 的最上方先使用{% load my_tags %}
导入该 py 文件。
然后就可以在html模板文件后面使用我们自定义的过滤器和标签了:
{% load my_tags %}
{{ 11|my_filter:22 }}
{% my_tag1 11 22 33 %}
#上面的11,22,33也可以切换为一个变量名就行业务处理
语义化标签
先在该 py 文件中导入 mark_safe
然后在定义标签时,用上 mark_safe 方法,令标签语义化,相当于 jQuery 中的 html() 方法。和前端HTML文件中的过滤器 safe 效果一样。
from django.utils.safestring import mark_safe
@register.simple_tag
def my_html(v1, v2):
temp_html = "<input type='text' id='%s' class='%s' />" %(v1, v2)
return mark_safe(temp_html)
模本加载{% my_html "zzz" "xxx" %}
即可得到一个输入框
配置静态文件
在项目容器根目录下创建 statics 目录。
然后在settings.py中添加属性
STATIC_URL = '/static/' # 别名
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "statics"), ]
在 statics 目录下创建 css 目录,js 目录,images 目录,plugins 目录, 分别放 css文件,js文件,图片,插件。
把 bootstrap 框架放入插件目录 plugins。在 HTML 文件的 head 标签中引入 bootstrap。
注意:此时引用路径中的要用配置文件中的别名 static,而不是目录 statics。
<link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7/dist/css/bootstrap.css">
如果模板想要使用静态文件的话需要先使用{% load static %}
代码加载静态文件资源
然后添加一个将请求交给模板的视图(views.py):
from django.shortcuts import render
def runoob(request):
name ="ADMIN"
return render(request, "runoob.html", {"name": name})
模板文件:
{% load static %}
{{name}}<img src="{% static 'images/runoob-logo.png' %}" alt="runoob-logo">
模板继承
父模板
标签 block...endblock: 父模板中的预留区域,该区域留给子模板填充差异性的内容,不同预留区域名字不能相同。
{% block 名称 %}
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}
子模板
子模板使用标签 extends 继承父模板:
{% extends "父模板路径"%}
子模板如果没有设置父模板预留区域的内容,则使用在父模板设置的默认内容,当然也可以都不设置,就为空。
子模板设置父模板预留区域的内容:
{ % block 名称 % }
内容
{% endblock 名称 %}
Django 模型(Model)
如上图所示, 模型实际上就是和数据库进行交互的一个模块, 它可以帮助用户进行数据库的连接访问, 同时更重要的是可以让用户不是通过写sql语句查询数据, 而是通过类和对象的函数方法完成对数据库的操作。
回顾前面我们有看到Django是MTV结构的可知, 标准流程而言, 模型与数据库的交互操作是根据视图提出的需求完成的, Template模板只是负责页面的渲染显示工作(当然特殊的标签和装饰器另说), 数据库操作是完全交由模型执行的。
所以我们可以将用户的请求分为两类:
a.如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。
b.如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。
模型将视图函数所需要的数据返回到视图函数中, 然后视图函数再把返回的数据填充到模板中空格中,最后返回网页给用户。
Django ORM
Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。Django 模型使用自带的 对象关系映射ORM(Object Relational Mapping )用于实现面向对象编程语言里不同类型系统的数据之间的转换(其实就是数据库系统三级映射模式中的外模式/概念模式映像)。ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。ORM 是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。
ORM 解析过程:
- 1、ORM 会将 Python 代码转成为 SQL 语句。
- 2、SQL 语句通过 pymysql 传送到数据库服务端。
- 3、在数据库中执行 SQL 语句并将结果返回。
数据库配置
创建 MySQL 数据库( ORM 无法操作到数据库级别,只能操作到数据表)语法:
create database 数据库名称 default charset=utf8; # 防止编码问题,指定为 utf8
接下来为了让Django能够连接上我们的数据库, 需要在settings.py
中添加DATABASES
数据:
DATABASES = {
'default':
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'runoob', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
}
}
Django 根据这一设置,与 MySQL 中相应的数据库和用户连接起来。
实际上我们需要明白的一点是, Django的模型和数据库间的交互并不是完全由Django内部的组件完成的, 而是依靠pymysql
这些独立于Django之外,需要另外安装的模块来完成。Django真正做的任务是生成数据库操作的语句, 然后交给pymysql
这些外模块, 让它们去跟数据库进行真正的连接和执行查询语句, 然后返回得到的数据交回到Django, Django再通过内部的方法对结果进行处理分配到具体对象的一些属性当中。
所以我们需要告诉 Django 使用 pymysql 模块连接 mysql 数据库:
# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()
创建 APP
Django 规定,如果要使用模型,必须要创建一个 app。我们使用以下命令创建一个 TestModel 的 app:
django-admin startapp TestModel
然后app生成在容器的根目录下:
HelloWorld
|-- HelloWorld
|-- manage.py
...
|-- TestModel
| |-- __init__.py
| |-- admin.py
| |-- models.py
| |-- tests.py
| `-- views.py
然后对这个生成的模型进行一些设置:
-
我们修改 TestModel/models.py 文件,代码如下:
-
# models.py from django.db import models class Test(models.Model): name = models.CharField(max_length=20)
-
models.py中类的
类名
代表了数据库表名
,且继承了models.Model,类里面的字段
代表数据表中的字段(name)
,数据类型则由选择的对象CharField(相当于varchar)、DateField(相当于datetime)决定, max_length 参数限定长度。
接下来在 settings.py 中找到INSTALLED_APPS这一项,如下:
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'TestModel', # 添加此项
)
最后在命令行中运行:
$ python3 manage.py migrate # 创建表结构
$ python3 manage.py makemigrations TestModel # 让 Django 知道我们在我们的模型有一些变更
$ python3 manage.py migrate TestModel # 创建表结构
看到几行 "Creating table…"
的字样,数据表就创建好了。
表名组成结构为:应用名_类名(如:TestModel_test)。尽管我们没有在 models 给表设置主键,但是 Django 会自动添加一个 id 作为主键。
数据库操作
在 HelloWorld 目录中添加 testdb.py 文件(下面介绍),并修改 urls.py:
from django.urls import path
from . import views,testdb
urlpatterns = [
path('runoob/', views.runoob),
path('testdb/', testdb.testdb),
]
添加数据
添加数据需要先创建对象,然后再执行 save 函数,相当于SQL中的INSERT:
# -*- coding: utf-8 -*-
from django.http import HttpResponse
from TestModel.models import Test
# 数据库操作
def testdb(request):
test1 = Test(name='runoob') # 相当于创建了一个字段
test1.save()
return HttpResponse("<p>数据添加成功!</p>")
访问 http://127.0.0.1:8000/testdb 就可以看到数据添加成功的提示。
获取数据
Django提供了多种方式来获取数据库的内容,如下所示(HelloWorld/HelloWorld/testdb.py):
# -*- coding: utf-8 -*-
from django.http import HttpResponse
from TestModel.models import Test
# 数据库操作
def testdb(request):
# filter相当于SQL中的WHERE,可设置条件过滤结果
response2 = Test.objects.filter(id=1)
# 获取单个对象
response3 = Test.objects.get(id=1)
# 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
Test.objects.order_by('name')[0:2]
#数据排序
Test.objects.order_by("id")
# 上面的方法可以连锁使用
Test.objects.filter(name="runoob").order_by("id")
# 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
list = Test.objects.all()
# 输出所有数据
response,response1 = "",""
for var in list:
response1 += var.name + " "
response = response1
return HttpResponse("<p>" + response + "</p>")
上面的案例代码不难理解, 我们可以知道Test.objects
实际上就是TestModel.models
,我们之前有提到Django只能达到操作表的层级, 并不能对数据库进行操作, 所以其实TestModel.models
一张表。而上面添加数据的安利中的Test(name='runoob')
就是从TestModel.models
这样的一张表继承而来, 所以Test(name='runoob')就是一个字段。
更新数据
修改数据可以使用 save()
或 update()
, 示例代码HelloWorld/HelloWorld/testdb.py
如下:
from django.http import HttpResponse
from TestModel.models import Test
# 数据库操作
def testdb(request):
# 修改其中一个id=1的name字段,再save,相当于SQL中的UPDATE
test1 = Test.objects.get(id=1)
test1.name = 'Google'
test1.save()
# 另外一种方式(filter相当于where)
#Test.objects.filter(id=1).update(name='Google')
# 修改所有的列(all函数获取整张表)
# Test.objects.all().update(name='Google')
删除数据
删除数据库中的对象只需调用该对象的delete()方法即可,示例代码如下:
from django.http import HttpResponse
from TestModel.models import Test
# 数据库操作
def testdb(request):
# 获取id=1的数据
test1 = Test.objects.get(id=1)
# 删除id=1的数据
test1.delete()
# 另外一种方式
# Test.objects.filter(id=1).delete()
# 删除所有数据
# Test.objects.all().delete()
return HttpResponse("<p>删除成功</p>")
所以实际上删除操作就是通过查询操作获得一些元组后使用delete()函数就是删除数据。
Django 表单
GET 方法
我们在之前的项目中创建一个 /HelloWorld/HelloWorld/search.py文件(视图结构),用于接收用户的请求:
from django.http import HttpResponse
from django.shortcuts import render
# 表单
def search_form(request):
return render(request, 'search_form.html')
# 接收请求数据
def search(request):
request.encoding='utf-8'
if 'q' in request.GET and request.GET['q']:
message = '你搜索的内容为: ' + request.GET['q']
else:
message = '你提交了空表单'
return HttpResponse(message)
在模板目录 templates 中添加/HelloWorld/templates/search_form.html表单:
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="搜索">
</form>
</body></html>
然后修改添加一下 urls.py 文件:
from django.conf.urls import url
from . import views,testdb,search
urlpatterns = [
url(r'^hello/$', views.runoob),
url(r'^testdb/$', testdb.testdb),
url(r'^search-form/$', search.search_form),
url(r'^search/$', search.search),
]
POST 方法
嗯....该说不说, 其实POST方法和GET方法并没有太大的区别
我们照样创建两个文件/HelloWorld/templates/post.html
/HelloWorld/HelloWorld/search2.py
一个显示的Template模板和一个负责业务逻辑的视图View文件即可
区别就与POST传输数据要将form
表单中的method方法从get改为post, 同时接收参数的视图通过修改request.GET
变为request.POST
获取数据POST传输过来的数据
Request 对象
每个视图函数的第一个参数是一个 HttpRequest 对象,例如
from django.http import HttpResponse
def runoob(request):
return HttpResponse("Hello world")
HttpRequest对象包含当前请求URL的一些信息:
关于属性的内容在这里就不详细描述太多了, 更多的内容可以看request的手册部分,
- path 请求页面的全路径,不包括域名—例如, "/hello/"。
- method 请求中使用的HTTP方法的字符串表示。全大写表示
- GET, POST 分别获取get传参和post传参的参数,REQUEST则是联合了两者的全部参数。需要注意的是POST不包括file-upload信息。参见FILES属性。
- COOKIES 包含所有cookies的标准Python字典对象。Keys和values都是字符串。
- FILES 包含所有上传文件的类字典对象。FILES中的每个Key都是标签中name属性的值. FILES中的每个value 同时也是一个标准Python字典对象,包含下面三个Keys:
- filename: 上传文件名,用Python字符串表示
- content-type: 上传文件的Content type
- content: 上传文件的原始内容
- 注意:只有在请求方法是POST,并且请求页面中
- META 包含所有可用HTTP头部信息的字典
- user 是一个django.contrib.auth.models.User 对象,代表当前登录的用户。如果访问用户当前没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。
- raw_post_data 原始HTTP POST数据,未解析过。 高级处理时会有用处。
- session 唯一可读写的属性,代表当前会话的字典对象。只有激活Django中的session支持时该属性才可用。
下面是几个常用的方法:
- getitem(key) 返回GET/POST的键值,先取POST,后取GET。如果键不存在抛出 KeyError。
- has_key() 检查request.GET or request.POST中是否包含参数指定的Key。
- get_full_path() 返回包含查询字符串的请求路径。例如, "/music/bands/the_beatles/?print=true"
- is_secure() 如果请求是安全的,返回True,就是说,发出的是HTTPS请求。
在HttpRequest对象中, GET和POST属性是django.http.QueryDict类的实例。QueryDict类似字典的自定义类,用来处理单键对应多值的情况。因为QueryDict不是很突出所以也就不多说了。
Django 视图
一个视图函数,简称视图,是一个简单的 Python 函数,它接受 Web 请求并且返回 Web 响应。每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)。
请求对象: HttpRequest 对象(简称 request 对象)
以下介绍几个常用的 request 属性。
-
GET
数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP GET 的所有参数。
有相同的键,就把所有的值放到对应的列表里。
取值格式:request.get("key_name")。
get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。
def runoob(request): name = request.GET.get("name") return HttpResponse('姓名:{}'.format(name))
-
POST
和GET差不多, get()函數作用也是一样的。
-
body
数据类型是二进制字节流,是原生请求体里的参数内容,在 HTTP 中用于 POST,因为 GET 没有请求体。
在 HTTP 中不常用,而在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML、Json 等。
-
path
获取 URL 中的路径部分,数据类型是字符串。
-
method
获取当前请求的方式。
感觉上来说...........和上面差不多, 这段貌似有点多余了
响应对象:HttpResponse 对象
响应对象主要有三种形式:HttpResponse()、render()、redirect()。
-
HttpResponse(): 返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含有 html 标签,也可以渲染。
def runoob(request): # return HttpResponse("LaLaLa...") return HttpResponse("<a href='https://www.google.com/'>点我没用</a>")
-
render(): 返回文本,第一个参数为 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数:键为页面参数名,值为views参数名)。
def runoob(request): name ="小芳" return render(request,"Look Here",{"name":name})
-
redirect():重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。
def runoob(request): return redirect("/index/")
render 和 redirect 是在 HttpResponse 的基础上进行了封装。render底层返回的也是 HttpResponse 对象, redirect底层继承的是 HttpResponse 对象。
解决post 请求返回 403
导入模块:from django.views.decorators.csrf import csrf_exempt
在函数前面添加修饰器:@csrf_exempt
原因:当采用客户端象 django 的服务器提交 post 请求时,会得到403,权限异常。因为 django 针对提交的请求,有校验。所以会如此。客户端提交的 post 如果不加这段,会出现 403 error
Django 路由
路由简单的来说就是根据用户请求的 URL 链接来判断对应的处理程序,并返回处理结果,也就是 URL 与 Django 的视图建立映射关系。Django 路由在 urls.py 配置,urls.py 中的每一条配置对应相应的处理方法。Django 不同版本 urls.py 配置有点不一样:
-
Django1.1.x 版本
url() 方法:普通路径和正则路径均可使用,需要自己手动添加正则首位限制符号。
-
Django 2.2.x 之后的版本
path:用于普通路径,不需要自己手动添加正则首位限制符号,底层已经添加。
re_path:用于正则路径,需要自己手动添加正则首位限制符号。
Django1.1.x 版本中的 url 和 Django 2.2.x 版本中的 re_path 用法相同。
主要的内容有正则路径中的分组
反向解析
命名空间
这三个内容, 不过如果不是写比较多路由的项目基本都用不上, 所以还是不说了, 感兴趣的可以自己去搜一下。
后面还有一些内容比如Django管理工具Django-Admin
, ORM
, Form
和Auth
组件, 不过感觉这些相当于是拓展了,如果单纯想简单读懂Django的代码现在应该是没问题的了,但如果说想要自己通过Django框架写一个小项目出来现在的知识还是不够的, 所以明天我学习一遍后再捡一些个人感觉露脸率比较高的知识出来吧(后面如果深入学习的话再慢慢补充吧, 毕竟现在还是菜鸟阶段只能C+V慢慢积累hhh)