Web框架简介
Web开发是Python语言应用领域的重要部分,也是目前最主要的Web开发语言之一,在其二十多年的历史中出现了数十种Web框架,比如Django、Tornado、Flask、Twisted、Bottle和Web.py等,有的历史悠久,有的发展迅速,还有的已经停止维护.
Django:
发布于2003年,是当前Python世界里最负盛名且最成熟的Web框架,最初被用来制作在线新闻的Web站点,Django的各模板之间结合得比较紧密,所以在功能强大的同时又是一个相对封闭的系统(依然可以自定义的),但是其健全的在线文档和开发社区,使开发者遇到问题能找到解决办法.
Tornado:
一个强大的、支持协程、高效并发且可扩展的Web服务器,发布于2009年9月,应用于FriendFeed、Facebook等社交网站。它的强项在于可以利用异步协程机制实现高并发的服务。
Flask:
Python Web框架家族里比较年轻的一个,发布于2010年,它吸收了其他框架的优点并且把自己的主要领域定义在了微小项目上,以短小精干,简洁明了著称。
Twisted:
一个有着十多年历史的开源事件驱动框架。它不像前三种着眼于Web应用开发,而是适用从传输层到自定义应用协议的所有类型的网络程序的开发,并能在不同的操作系统上提供很高的运行效率。但是,目前对Python3的支持有限,建议使用Python2.7。
MVC和MTV框架
MVC
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示
MTV
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
# M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
# T 代表模板 (Template):负责如何把页面展示给用户(html)。
# V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:
一般是用户通过浏览器向我们的服务器发起一个请求(request),这个请求回去访问视图函数,(如果不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户),视图函数调用模型,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
选择框架的原则
-
选择更主流的框架。因为它们的文档更齐全,技术积累更多,社区更繁盛,能得到更好的帮助和支持。
-
选择更活跃的框架。关注项目在GitHub等环境中的更新频率、Issue和Pull Request的响应情况。如果一个项目长期没有更新,或者有一堆的问题需要解决但是没有得到响应,就不应该是你学习的对象。
-
选择能够满足需求的框架。没有最好的框架,只有更合适的框架。你所选择的Web框架不仅需要满足当前的需求,还要充分考虑项目发展一段时间后的情况,即前瞻性,避免盲目选择而导致将来推倒重来的情况。
-
选择时效性好的框架。在学习和使用框架的时候经常需要查阅和参考各种网络上的文章、博客和教程,但是需要注意他们的发表时间。有些框架的相关文章已经很老了,很久没更新了,应该放弃这种框架;有的框架一直以来都有不断的新文章、新博客出现,就是比较不错的选择。
-
选择入门友好的框架。这条只对新手适用。详细的框架文档、官方教程对新手来说都是极大的帮助和鼓励.
为什么选择Django?
首先介绍一下Django,Django具有以下特点:
- 功能完善、要素齐全:该有的、可以没有的都有,自带大量常用工具和框架,无须你自定义、组合、增删及修改。
- 完善的文档:经过十多年的发展和完善,Django有广泛的实践案例和完善的在线文档。开发者遇到问题时可以搜索在线文档寻求解决方案。
- 强大的数据库访问组件:Django的Model层自带数据库ORM组件,使得开发者无须学习其他数据库访问技术(SQL、pymysql、SQLALchemy等)。
- 灵活的URL映射:Django使用正则表达式管理URL映射,灵活性高。新版的2.0,进一步提高了URL编写的优雅性。
- 丰富的Template模板语言:类似jinjia模板语言,不但原生功能丰富,还可以自定义模板标签,并且与其ORM的用法非常相似。
- 自带后台管理系统admin:只需要通过简单的几行配置和代码就可以实现一个完整的后台数据管理控制平台。
- 完整的错误信息提示:在开发调试过程中如果出现运行错误或者异常,Django可以提供非常完整的错误信息帮助定位问题。
根据前面的选择原则我们逐条对比一下:
# 1. 主流、活跃程序:
# 从GitHub的数据来看,Django的开发非常活跃,迭代速度也非常快
# 2. 是否可以满足需求:
# Django以要素齐全、工具丰富、框架庞大著称,基本上别的框架有的他有,别的框架没有的他也有,
# 如果Django满足不了需求,那么别的框架同时也一样.
# 3. 时效性:
# Django有很长的开发喝实践过程,或早或晚的文档、教程、帮助、博客等等非常多,资料更新速度也很快.
# 4. 入门友好程度
# 一个框架能否流行起来,对新手入门友好非常关键.Django在这一点做的非常好.
Django的不足
Django也有一些缺点:
框架庞大,被认为不够精简,捆绑的内容太多
首先,对于新手,Django集成好的工具和部件,让你无须再费脑力去学习如何安装、调试、集成、兼容别的工具,Django帮你都集成好了,而且保证兼容性,可用性和方便性,就好比联想一体机,开机即用,效率也高,而一些如flask的框架,虽然精简,但是你要自己安装各种工具、ORM、插件等等,好比DIY电脑,在用之前,要知道买什么配件,怎么搭配,怎么组装,怎么配置效率才高,将新手的热情消耗在非关键性的内容上.
其次,对于老手,Django也是开放的,你完全可以关闭不必要的功能,忽略不使用的组件,或者自定义希望的组件,包括ORM和Template在内,都可以自由选择.在异步通信方面略有欠缺
从本质上来讲,Tornado在异步协程机制实现高并发的服务上要强一些,Django在这方面有追赶的目标,但这不是说Django就差到不能用了.
基于Python进行Web开发的技术栈
想要熟练地使用Django进行Web开发,设计生产环境可用的,能够应付一定规模访问量的Web应用,开发者要学会的远远不止Django本身,Python基础、环境搭建、前端语言、API设计、网站架构、系统管理、持续集成、服务化数据处理、并发处理等等,都是相关的只是领域,包括但不限于以下的内容:
# 熟悉Python语言
# 对前端的HTML\CSS\JavaScript比较熟悉
# 对网络基础,比如HTTP、TCP/IP等比较熟悉
# 熟悉数据库、缓存、消息队列等技术的使用场景和使用方法
# 日常能使用Linux或Mac系统工作(Windows标配)
# 有性能优化经验,能快速定位问题
# 除此之外,还要对业务有深刻理解,能够写出可维护性足够高的代码,当然,以上都是对经验丰富的开发者而言,
# 对于新手刚入门者,我们朝着这个目标努力学习就好
# 下面是基于Python的Web开发技术栈
# 1、web应用
# 运行在浏览器上的应用
# 2、c/s b/s 架构
# client/server:客户端服务器架构,C++
# brower/server:浏览器服务器架构,Java、Python
# 底层均是基于socket
# 3、Python Web框架
# a.socket b.页面路由 c.模板渲染
# Django a用的wsgiref b自己写的 c自己写的 功能全面
# Flask a用的第三方 b自己写的 c自己写的 小而轻
# Tornado a自己写的 b自己写的 c自己写的 支持高并发
Django简介
Django是什么?
Django 是一个高级的 Python 网络框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django负责处理网站开发中麻烦的部分,因此你可以专注于编写应用程序,而无需重新开发。
它是免费和开源的,有活跃繁荣的社区,丰富的文档,以及很多免费和付费的解决方案。
Django可以使你的应用具有以下优点
完备性
Django遵循“功能完备”的理念,提供开发人员可能想要“开箱即用”的几乎所有功能。因为你需要的一切都是一个”产品“的一部分,它们都可以无缝结合在一起,遵循一致性设计原则,并且具有广泛和最新的文档
通用性
Django 可以(并已经)用于构建几乎任何类型的网站—从内容管理系统和维基,到社交网络和新闻网站。它可以与任何客户端框架一起工作,并且可以提供几乎任何格式(包括 HTML,Rss源,JSON,XML等)的内容。你正在阅读的网站就是基于Django。
在内部,尽管它为几乎所有可能需要的功能(例如几个流行的数据库,模版引擎等)提供了选择,但是如果需要,它也可以扩展到使用其他组件。
安全性
Django 帮助开发人员通过提供一个被设计为“做正确的事情”来自动保护网站的框架来避免许多常见的安全错误。例如,Django提供了一种安全的方式来管理用户账户和密码,避免了常见的错误,比如将session放在cookie中这种易受攻击的做法(取而代之的是cookies只包含一个密钥,实际数据存储在数据库中)或直接存储密码而不是密码哈希。
密码哈希是通过密码散列函数发送密码而创建的固定长度值。 Django 能通过运行哈希函数来检查输入的密码-就是-将输出的哈希值与存储的哈希值进行比较是否正确。然而由于功能的“单向”性质,即使存储的哈希值受到威胁,攻击者也难以解决原始密码。(但其实有彩虹表-译者观点)
默认情况下,Django可以防范许多漏洞,包括SQL注入,跨站点脚本,跨站点请求伪造和点击劫持
可扩展
Django使用基于组件的"无共享"架构(架构的每一部分独立于其他架构,因此可以根据需要进行替换或更改)在不用部分之间有明确的分隔意味着它可以通过任何级别添加硬件来扩展服务: 缓存服务器,数据库服务器或应用程序服务器,一些最繁忙的网站已经成功地缩放了Django,以满足他们的需求(例如Instagram和Disqus,仅举两个例子,可自行添加)
可维护性
Django代码编写时遵照设计原则和模式,鼓励创建可维护和重复使用的代码,特别是他用了不要重复自己(DRY)原则,所以没有不必要的重复,减少了代码的数量.Django还将相关功能分组到可重用的"应用程序"中,并且在较低程序级别将相关代码分组
灵活性
Django使用Python编写的,他在许多平台上运行,意味着你不受任务特定的服务器平台的限制,并且可以在许多种类的Linux,Windows和Mac OsX上运行应用程序,此外,Django得到许多网络托管商的好评,他们经常提供特定的基础设施和托管Django网站的文档.
Django的出生
Django最初由2003年到2005年间由负责创建和维护报纸网站的网络团队开发。在创建了许多网站后,团队开始考虑并重用许多常见的代码和设计模式。这个共同的代码演变一个通用的网络开发框架,2005年7月被开源“Django”项目。
Django不断发展壮大——从2008年9月的第一个里程碑版本(1.0)到最近发布的(1.11)-(2017)版本。每个版本都添加了新功能和错误修复,从支持新类型的数据库,模版引擎和缓存,到添加“通用”视图函数和类(这减少了开发人员必须编写的代码量)一些编程任务。
Django有多欢迎
服务器端框架的受欢迎程度没有任何可靠和明确的测量(尽管Hot Frameworks网站 尝试使用诸如计算每个平台的GitHub项目数量和StackOverflow问题的机制来评估流行度)。一个更好的问题是Django是否“足够流行”,以避免不受欢迎的平台的问题。它是否继续发展?如果您需要帮助,可以帮您吗?如果您学习Django,有机会获得付费工作吗?
基于使用Django的流行网站数量,为代码库贡献的人数以及提供免费和付费支持的人数,那么是的,Django是一个流行的框架
使用Django的流行网站包括:Disqus,Instagram,骑士基金会,麦克阿瑟基金会,Mozilla,国家地理,开放知识基金会,Pinterest和开放栈.
Django是特定?
Web框架通常将自己称为"特定" 或"无限制".
特定框架是对处理任何特定任务的"正确方法" 有意见的框架,他们经常支持特定领域的快速发展(解决特定类型的问题),因为正确的做法是通常被很好的理解和记录在案,然而,他们在解决其主要领域之外的问题可能不那么灵活,并且倾向于为可以使用那些组件和方法提供较少的选择.
相比之下,无限制的框架对于将组件粘合在一起以实现目标或甚至应使用那些组件的最佳方式限制较少,它们使开发人员更容易使用最合适的工具来完成特定任务,尽管您需要自己查找这些组件。
Django“有点有意义”,因此提供了“两个世界的最佳”。它提供了一组组件来处理大多数Web开发任务和一个(或两个)首选的使用方法。然而,Django的解耦架构意味着您通常可以从多个不同的选项中进行选择,也可以根据需要添加对全新的支持
原生Socket服务
demo1
-- index.html
-- server.py
基础socket服务
import socket
# 利用socket建立服务器对象
server = socket.socket()
# 设置ip和端口
server.bind(('127.0.0.1', 8001))
# 设置监听
server.listen(5)
print('服务器设置成功')
print('浏览器访问:http://127.0.0.1:8001')
while True:
# 阻塞等待客户端数据
client, address = server.accept()
# 接收数据
data = client.recv(1024)
print('接收到数据: ', data)
# 返回数据
client.send(b'Normal Socket Web')
# 关闭连接(必须关闭每一次连接)
client.close()
浏览器错误:发送的响应无效,原因:响应不满足http协议
# 请求发来的数据
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
Cookie: csrftoken=szfYLDVuqvRhlveNpNE2rp1GYOcI5x7mRNfvkRWTMRNRwWxXMZWOhL1MqknYJ7jg; sessionid=3pphvmw2icub0bea7nn02u6wev17k4uw\r\n
\r\n'
http协议
什么是http协议
# HTTP(HyperText Transport Protocol)是超文本传输协议
# 基于TCP/IP协议基础上的应用层协议,底层实现仍为socket
# 基于请求-响应模式:通信一定是从客户端开始,服务器端接收到客户端一定会做出对应响应
# 无状态:协议不对任何一次通信状态和任何数据做保存
# 无连接:一次连接只完成一次请求-响应,请求-响应完毕后会立即断开连接
http工作原理(事务)
# 一次http操作称之为一个事务,工作过程可分为四步
# 1.客户端与服务端建立连接
# 2.客户端发生一个http协议指定格式的请求
# 3.服务器端接收请求后,响应一个http协议指定格式的响应
# 4.客户端将服务器的响应显示展现给用户
请求报文
POST / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n
usr=abc&pwd=123
响应报文
# 响应行 响应头 响应体
HTTP/1.1 200 OK\r\n
Content-type:text/html\r\n
\r\n
Login Success
修改返回数据,完善响应体
# 字符串
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'\r\n')
client.send(b'Normal Socket Web')
# html代码,请求头要设置支持html代码
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
client.send(b'<h1>Normal Socket Web</h1>')
# html文件(同级目录建立一个index.html页面)
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
# 利用文件方式读取页面
with open('index.html', 'rb') as f:
dt = f.read()
client.send(dt)
修改接受数据,模拟后台路由
# 分析接收到的数据
data = client.recv(1024)
# 保证接收到的数据作为字符串进行以下处理
data = str(data, encoding='utf-8')
# 拆分出地址位
route = data.split('\r\n')[0].split(' ')[1]
# 匹配地址,做出不同的响应
if route == '/index':
with open('index.html', 'rb') as f:
dt = f.read()
elif route == '/login': # 新建login页面
with open('login.html', 'rb') as f:
dt = f.read()
else:
dt = b'404'
client.send(dt)
状态码
# 1打头:消息通知
# 2打头:请求成功
# 3打头:重定向
# 4打头:客户端错误
# 5打头:服务器端错误
框架演变
目录结构
part2
-- favicon.ico
-- index.html
-- manage.py=
manage.py
import socket
import pymysql
# 响应头
RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n'
# 请求处理
def index():
# 以字节方式读取文件
with open('index.html', 'rb') as f:
dt = f.read()
return dt
def ico():
with open(favicon.jpeg, 'rb') as f:
dt = f.read()
return dt
def user():
# 数据库操作
conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute('select * from user')
users = cur.fetchall()
print(users)
users = '''%d:%s
%d:%s''' % (users[0]['id'], users[0]['name'], users[1]['id'], users[1]['name'])
return users.encode('utf-8')
# 设置路由
urls = {
# 请求路径与请求处理函数一一对应
'/index': index,
favicon.jpeg: ico,
'/user': user
}
# 设置socket
def serve(host, port):
server = socket.socket()
server.bind((host, port))
print('start:http://' + host + ':' + str(port))
server.listen(5)
while True:
sock, addr = server.accept()
data = sock.recv(1024)
data = str(data, encoding='utf-8')
print(data)
route = data.split('\r\n')[0].split(' ')[1]
resp = b'404'
if route in urls:
resp = urls[route]()
sock.send(RESP_HEADER)
sock.send(resp)
sock.close()
# 启服务
if __name__ == '__main__':
serve('127.0.0.1', 8002)
项目演变
目录结构
03_proj
-- template
-- index.html
-- user.html
favicon.ico
start.py
urls.py
views.py
index.html
<h1>{{ name }}</h1>
user.html
<table border="1">
<tr>
<th>id</th>
<th>name</th>
<th>password</th>
</tr>
{% for user in users%}
<tr>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.password}}</td>
</tr>
{% endfor %}
</table>
start.py
from wsgiref.simple_server import make_server
from urls import urls
def app(env, response):
print(env)
# 设置响应头
response("200 OK", [('Content-type', 'text/html')])
route = env['PATH_INFO']
print(route)
data = urls['error']()
if route in urls:
data = urls[route]()
# 返回二进制响应体
return [data]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8003, app)
print('start:http://127.0.0.1:8003')
server.serve_forever()
urls.py
from views import *
urls = {
'/index': index,
'/favicon.ico': ico,
'/user': user,
'error': error
}
views.py
import pymysql
# 利用jinja2来渲染模板,将后台数据传给前台
from jinja2 import Template
def index():
with open('templates/index.html', 'r') as f:
dt = f.read()
tem = Template(dt)
resp = tem.render(name='主页')
return resp.encode('utf-8')
def ico():
with open('favicon.ico', 'rb') as f:
dt = f.read()
return dt
def user():
# 数据库操作
conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute('select * from user')
users = cur.fetchall()
print(users)
with open('templates/user.html', 'r') as f:
dt = f.read()
tem = Template(dt)
resp = tem.render(users=users)
return resp.encode('utf-8')
def error():
return b'404'
Django代码是什么样的?
在传统的数据驱动网站中,Web应用程序会等待来自Web浏览器(或其他客户端)的HTTP请求,当接收到请求时,应用程序根据URL和可能的Post数据或GET数据中的信息确定需要的内容,根据需要,可以从数据库读取或写入信息,或执行满足需求所需的其他任务,然后,该应用程序将返回对Web浏览器的响应,通常将检索到的数据插入HTML模板中的占位符来动态创建用于浏览器显示的HTML页面.
Django网络应用程序通常将处理每个步骤的代码分组到单独的文件中.
URLs: 虽然可以通过单个功能来吹每个URL的请求,但是编写单独的视图函数来处理每个资源是更加可维护的.URL映射器用于根据URL将HTTP请求重定向到相应的视图,URL映射器还可以匹配出现在URL中的字符串或数字的特写模式,并将其作为数据传递给视图功能.
View: 视图是一个请求处理函数,他接受HTTP请求并返回HTTP响应,视图通过模型访问满足请求所需的数据,并将响应的格式委托给模板.
Models: 模型是定义应用程序数据结构的Python对象,并提供在数据库中管理(添加,修改,删除)和查询记录的机制.
Templates: 模板是定义文件(例如HTML页面)的结构或布局的文本文件,用于表示实际内容的占位符,一个视图可以使用HTML模板,从数据填充他动态地创建一个HTML页面模型,可以使用模板来定义任何类型的文件的结构: 不一定是HTML.
此种组织被Django称为"模型视图模板(MVT)"架构,他与更加熟悉的Model View Controller架构有许多相似之处.
1.将请求发送到正确的视图(urls.py)
URL映射器通常存储在名为urls.py的文件中,在下面示例中,mapper(urlpatterns)定义了特定URL模式和相应视图函数之间的映射列表,如果接收到具有与指定模式匹配的URL(例如 r'&$',下面)的HTTP请求,则将调用相关联的视图功能(例如views.index)并传递请求.
urlpatterns = [
url(r'^$', views.index),
url(r'^([0-9]+)/$', views.best),
]
# 注意:
# 该urlpatterns对象的列表url()功能,在Python中,使用方括号定义列表,项目以逗号分隔,
# 并可能有一个可选的逗号,例如:[item1,item2,item3,].
# 该模式的奇怪的语法称为正则表达式
# 第二个参数url()是当模式匹配时,将被调用的另一个函数,符号views.index表示该函数被调用,
# index()并且可以在被调用的模块中找到views(即在一个名为views.py的文件中.)
2.处理请求(views.py)
视图是web应用程序的核心,从web客户端接收HTTP请求并返回HTTP响应,在两者之间,他们编制框架的其他资源来访问数据库,渲染模板等.
下面例子显示了一个最小的视图功能index(),这可以通过我们的URL映射器在上一节调用.
像所有视图函数一样,他接受一个HttpRequest对象作为参数(request)并返回一个HttpResponse对象,在这种情况下,我们对请求不做任何事情,我们的响应只是返回一个硬编码的字符串,我们会向您显示一个请求.
## filename:views.py(Django视图函数)
从django.http导入HttpResponse
def index(request):
#Get HttpRequest-request参数
#使用请求中的信息执行操作。
#Return HttpResponse
return HttpResponse('你好,Django!')
# 注意:
# Python模块是函数的'库',存储在单独的文件中,我们可能想在我们的代码块中使用他们,
# 在这里我们只从django.http模块导入了HttpResponse对象,使我们可以在视图中使用它:
# from django.http import HttpResponse
# 还有其他方法可以从模块导入一些或所有对象.
# 如上所示,使用def关键字声明函数,在函数名称后面的括号列出命名参数: 整行以冒号结尾,
# 注意下一行是否都进行了缩进,缩进很重要,因为他指定代码行在该特定块内(强制缩进是Python的一个关键特征),
# 也是Python代码很容易阅读的一个原因).
# 视图通常存储在一个名为views.py的文件中
3.定义数据模型(models.py)
Django Web应用程序通过被称为模型的Python对象来管理和查询数据,模型定义存储数据的结构,包括字段类型以及字段可能的最大值,默认值,选择列表选项,文档帮助文本,表单的标签文本等,模型的定义与底层数据库无关,你可以选择其中一个作为项目设置的一部分,一旦你选择了要使用的数据库,你就不需要直接与之交谈,只需编写模型结构和其他代码,Django可以处理与数据库通信的所有辛苦的工作.
下面的代码片段为Team对象展示了一个非常简单的Django模型,本Team类是从Django的类派生models.Model,他将团队名称和团队级别定义为字符字段,并为每个记录指定了要存储的最大字符数,team_level可以是几个值中的一个,因此,我们将其定义为一个选择片段,并在被展示的数据和被存储的数据之间建立映射,并设置一个默认值.
# filename: models.py
from django.db import models
class Team(models.Model):
team_name = models.CharField(max_length=40)
TEAM_LEVELS = (
('U09', 'Under 09s'),
('U10', 'Under 10s'),
('U11', 'Under 11s'),
... #list other team levels
)
team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
# 注意:
# Python支持'面向对象编程',这是一种编程风格,我们将代码组织到对象中,
# 其中包括用于对该对象进行操作的相关数据和功能,对象也可以从其他对象继承/扩展/派生,
# 允许相关对象之间的共同行为被共享,在Python中,我们使用关键字Class定义对象的‘蓝图’,
# 我们可以根据类中的模型创建类型的多个特定实例.
# 例如,我们有个Team类,它来自于Model类,这意味着他是一个模型,并且将包含模型的所有方法,
# 但是我们也可以给他自己的专门功能,在我们的模型中,我们定义了数据库需要存储我们的数据字段,
# 给出他们的具体名称,Django使用这些定义(包括字段名称)来创建底层数据库.
4.查询数据(views.py)
Django模型提供了一个而用于搜索数据库的简单查询API,这可以使用不同的标准(例如,精确,不区分大小写,大于等等)来匹配多个字段,并且可以支持复杂语句(例如,您可以在拥有一个团队的U11团队上指定搜索名称为以'Fr'开头或以'al'结尾).
代码片段显示了一个视图函数(资源处理程序),用于显示我们所有的U09团队,粗体显示如何使用模型查询API过滤所有记录,其中该team_level字段具有正确的文本'U09'(请注意,该条件如何filter()作为参数传递给该函数,该字段名称和匹配类型由双下划线: team_level_exact)
## filename: views.py
from django.shortcuts import render
from .models import Team
def index(request):
list_teams = Team.objects.filter(team_level__exact="U09")
context = {'youngest_teams': list_teams}
return render(request, '/best/index.html', context)
此功能使用render()功能创建HttpResponse发送回浏览器的功能,这个函数是一个快捷方式: 他通过组合指定的HTML模板和一些数据来插入模板(在名为'context'的变量中提供)来创建一个HTML文件.
5.呈现数据(HTML模板)
模板系统允许你指定输出文档的结构,使用占位符{% if youngest_teams%}来生成页面填写的数据,模板通常用于创建HTML,但也可以创建其他类型的文档,Django支持其原生模板系统和另一种流行的Python库(称为jinja2)开箱即用(如果需要,也可以支持其他系统).
代码片段显示render()了上一节函数调用的HTML模板的外观,这个模板已经被写入这样的想法,即他将被访问一个列表变量,youngest_teams当他被渲染时:
## filename: best/templates/best/index.html
<!DOCTYPE html>
<html lang="en">
<body>
{% if youngest_teams %}
<ul>
{% for team in youngest_teams %}
<li>{{ team.team_name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No teams are available.</p>
{% endif %}
</body>
</html>
前面部分显示了几乎每个Web应用程序将使用的主要功能: URL映射,视图,模型和模板.
Django提供的其他内容呢包括:
# 表单: HTML表单用于收集用户数据以便在服务器上进行处理,Django简化了表单创建,验证和处理.
# 用户身份验证和权限: Django包含了一个强大的用户身份验证和权限系统,该系统已经构建了安全性.
# 缓存: 与提供静态内容相比,动态创建内容需要更大的计算强度(也更缓慢),DJango提供灵活的缓存,
# 以便你可以存储所有或部分的页面,如无必要,不会重新呈现网页.
# 管理网站: 当你使用基本骨架创建应用时,就已经默认包含了一个Django管理站点,它十分轻松创建了一个管理页面,
# 使网站管理员能够创建、编辑和查看站点中的任何数据模型.
# 序列化数据: Django可以轻松的将数据序列化,并支持XML或JSON格式,
# 这会有助于创建一个Web服务(Web服务指数据纯粹为其他应用程序或站点所用,
# 并不会在自己的站点中显示),或是有助于创建一个由客户端代码处理和呈现所有数据的网站
6. MVC和MTV
Example1
tree mvc_demo/
mvc_demo/
├── Controller
│ ├── account.py
│ └── __pycache__
│ └── account.cpython-36.pyc
├── Model
├── Socket_Demo11.py
└── View
└── index.html
Controller/account.py
cat mvc_demo/Controller/account.py
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
# 2020/1/17 10:38
def handle_index():
import time
v = str(time.time())
f = open('View/index.html',mode='rb')
data=f.read()
f.close()
data = data.replace(b'@u',v.encode('utf-8'))
return [data,]
def handle_date():
return ['<h1>Hello,Date!</h1>'.encode('utf-8'), ]
View/index.html
cat mvc_demo/View/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>INDEX @u</h1>
</body>
</html>
Socket_Demo1.py
cat mvc_demo/Socket_Demo11.py
from wsgiref.simple_server import make_server
from Controller import account
URL_DICT = {
"/index": account.handle_index,
"/date": account.handle_date,
}
def RunServer(environ, start_response):
# environ 封装了客户端发来的所有数据
# start_response 封装要返回用户的数据,响应头、状态码
start_response('200 OK', [('Content-Type', 'text/html')])
current_url = environ['PATH_INFO']
func = None
if current_url in URL_DICT:
func = URL_DICT[current_url]
if func:
return func()
else:
return ['<h1>404</h1>'.encode('utf-8')]
if __name__ == '__main__':
httpd = make_server('', 8001, RunServer)
print("Serving HTTP on port 8000...")
httpd.serve_forever()
访问测试
cd mvc_demo/
python3 Socket_Demo11.py
Serving HTTP on port 8000...
127.0.0.1 - - [17/Jan/2020 11:13:10] "GET /index HTTP/1.1" 200 161
curl localhost:8001/index
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>INDEX 1579230790.0229425</h1>
</body>
</html>
# MVC
# Model View Controller
# 数据库 模板文件 业务处理
# MTV
# Model Template View
# 数据库 模板文件 业务处理
Django生命周期
# 用户请求 ----> URL对应关系(匹配) ----> 视图函数 ----> 返回用户字符串
# 用户请求 ----->URL对应关系(匹配) ----> 视图函数 ----> 打开一个HTML文件,读取内容
# 1.浏览器发送请求
# 2.wsgi服务器接收到请求,将请求解析交给Django
# 3.Django中间件过滤请求信息,交给路由匹配,如果匹配成功
# 4.路由完成业务逻辑的分发,到指定app下views中指定的视图函数,可以去数据库里面取数据,
# 5.视图函数完成具体的业务逻辑,和模板渲染,返回字符串响应结果
# 6.将处理结果通过服务器返回给浏览器
Django简单部署操作
CMD: Django安装与项目的创建
# 安装:pip3 install django==2.2
# 查看版本号:django-admin --version
# 新建项目:1.前往目标目录 2.django-admin startproject proj_name
# 项目目录,包含项目最基本的一些配置
# 项目名:
|--项目同名文件夹
|---- __init__.py 模块的配置文件
|---- settings.py 配置总文件
|---- urls.py url配置文件,主路由
|---- wsgi.py (web server gateway interface),
# 服务器网关接口,python应用与web服务器直接通信的接口
# templates:模板文件夹,存放html文件的(页面),支持使用Django模板语言(DTL),也可以使用第三方(jinja2)
# manage.py:项目管理器,与项目交互的命令行工具集的入口,查看支持的所有命令python3 manage.py
如果是windows电脑会发现pythonx.exe的目录下多了一个django_admin.exe
我们可以cd到那个目录,使用下面命令创建第一个django项目.
django-admin.exe startproject mysite
然后我们可以使用下面命令将此项目运行起来
python.exe C:\Users\You-Men\AppData\Local\Programs\Python\Python37-32\Scripts\mysite\manage.py runserver浏览器访问上面提示信息的地址就会出现下面页面了.
如果觉得使用django-admin工具麻烦可以加入环境变量.
将django-admin那个目录加入到系统环境变量的PATH里面,注意用;分号隔开.
app应用的创建
1.Django是面向应用开发,在应用中完成具体的业务逻辑
2.什么是应用app: 就好比项目中的一个功能模块,一个项目可以拥有多个功能模块,但至少得有一个,Django称之为app
3.如何创建app(在项目目录下):python3 manage.py startapp app01
migrations:数据迁移(移植)模块,内容都是由Django自动生成
# __init__.py
# admin.py:应用的后台管理系统配置
# apps.py:django 1.9后,本应用的相关配置
# models.py:数据模型模块,使用ORM框架,类似于MVC模式下的Model层
# tests.py:自动化测试模块,可以写自动化测试脚本
# views.py:执行相应的逻辑代码模块
Django目录介绍
django-admin startproject mysite
tree mysite/
mysite/
├── manage.py
└── mysite
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
# 各文件和目录解释:
# 外层的mysite/目录与Django无关,只是你项目的容器,可以任意重命名,[工程名称]
# manage.py: 一个命令行工具,用于与Django进行不同方式的交互脚本,非常重要
# 内部的mysite/目录是真正的项目文件包裹目录,他的名字是你引用内部文件的包名,例如: mysite.urls.
# mysite/__init__.py: 一个定义包的空文件
# mysite/setting.py: 项目的主配置文件,非常重要.
# mysite/urls.py: 路有文件,所有的任务都是从这里开始分配,相当于Django驱动站点的内容表格,非常重要.
# mysite/wsgi.py: 一个基于WSGI的web服务器进入点,提供底层的网络通信功能,通常不用关心,
# 但是上线时候不要使用wsgi,使用uwsgi,通常再配合nginx。
python3 manage.py startapp cmdb
tree cmdb/
cmdb/
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
# app
# migrations: 数据修改表结构
# admin: Django为我们提供的后台管理
# apps: 配置当前app
# models: ORM写指定的类,通过命令创建数据库结构
# test: 单元测试
# views: 业务逻辑代码
Django初始化配置
配置数据库
# 安装pymysql
sudo wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate
sudo python get-pip.py
sudo pip install pymysql
sudo pip3 install pymysql
# 数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'youmen_db',
'USER':'root',
'PASSWORD':'ZHOUjian.20',
'HOST':'116.196.83.113',
'PORT':'3306',
}
}
静态文件
# 新建一个与templates同级文件夹,配置文件添加
STATICFILES_DIRS=os.path.join(BASE_DIR,'static'),
# 静态文件检索的文件夹
语言和时区
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai' #
USE_I18N = True #internationalization 国际化 支持多语言
USE_L10N = True #本地化,比如农历
USE_TZ = True
#修改: 配置文件:
ALLOWED_HOSTS = ['*'] #允许所有访问
ALLOWED_HOSTS = ['192,168,0.114','127.0.0.1'] #列表为了防止黑客入侵,只允许列表中的ip地址访问
#表示此Django站点可以投放的主机/域名的字符串列表。这是防止HTTP主机头部攻击的安全措施
启动项目
# 终端: python3 manage.py runserver 127.0.0.1:8801
python manage.py runserver # 启动
python manage.py runserver 0.0.0.0:8080 #启动改变端口号和外部访问:
python manage.py makemigrations # 生成迁移文件
python manage.py migrate # 执行迁移,存到数据库
django-admin startapp xxx # 新建一个app
Django后台管理(admin)界面
uls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
url('admin/',admin.site.urls),
]
app/models.py
from django.db import models
# Create your models here.
class UserType(models.Model):
name = models.CharField(max_length=32)
class UserInfo(models.Model):
username=models.CharField(max_length=32)
pwd=models.CharField(max_length=32)
email=models.CharField(max_length=32)
user_type=models.ForeignKey(UserType,on_delete=models.CASCADE)
admin.py
from django.contrib import admin
# Register your models here.
from cmdb import models
admin.site.register(models.UserInfo)
项目运行起来,访问IP:PORT/admin即可访问登录页面,但是需要先创建一个超级用户
# 先将之前models.py文件的表创建出来**
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
Username (leave blank to use 'you-men'): youmen
Email address: [email protected]
Password:
Password (again):
Django示例之登录页面
# (如果需要调用js,css文件可以配置一下settings.py,html文件就可以调用static里面的css,js文件了)
static
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
定义路由规则
url.py
"login" --> 函数名
# Example
from django.conf.urls import url
from django.contrib import admin
from cmdb import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login',views.login),
url(r'^home',views.home)
]
定义视图函数
# app下views.py
# 获取用户请求的发来数据
# def func(request):
# request.method GET / POST
# http://127.0.0.1:8000/home?pid=123&name=nginx
# request.GET.get('',None) # 获取用户请求发来的数据
# request.POST.get('',None)
# request.FILES.get('files') # 此处files对应<input type="file" name="files" />
# checkbox等多选内容(值)
# request.POST.getlist()
# GET获取数据 POST: 提交数据
# 返回用户数据
# return HttpResponse('字符串')
# return render(request,"HTTP模板的路径")
# return redirect('url路径') # return redirect('/login') # 此处/代指前面的地址.
# Example
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.shortcuts import redirect
# Create your views here.
from django.shortcuts import HttpResponse
def login(request):
# 包含用户提交的所有信息
# 获取用户提交方法
# print(request.method)
error_msg = ""
if request.method == "POST":
# 用户通过POST提交过来的数据通过request.GET.get('',None)获取
user = request.POST.get('user',None)
pwd = request.POST.get('pwd',None)
if user == 'root' and pwd == '123':
# 去跳转到
return redirect(home);
else:
error_msg = "用户名或密码错误"
# 用户密码不匹配
return render(request,'login.html',{'error_msg':error_msg})
USER_LIST = [
{'username':'alex','email': 'Tom','gender':'男'},
{'username':'alex','email': 'Tom','gender':'男'},
{'username':'alex','email': 'Tom','gender':'男'},
]
# for index in range(20):
# temp = {'username':'alex' + str(index) ,'email': 'Tom','gender':'男'}
# USER_LIST.append(temp)
def home(request):
if request.method == "POST":
# 获取用户提交的数据 POST请求中
u = request.POST.get('username')
e = request.POST.get('email')
g = request.POST.get('gender')
temp = {'username':u, "email":e,'gender':g}
USER_LIST.append(temp)
return render(request,'home.html',{'user_list': USER_LIST})
# def home(request):
模板渲染
特殊的模板语言
分别对应前面view.py当中的变量
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body style="margin: 0;">
<div style="height: 48px; background: #dddddd;"></div>
<div>
<div>
<form action="/home" method="post">
<input type="text" name="username" placeholder="用户名"/>
<input type="text" name="email" placeholder="邮箱"/>
<input type="text" name="gender" placeholder="性别" />
<input type="submit" value="添加" />
</form>
</div>
<table>
{% for row in user_list %}
<tr>
<td>{{ row.username }}</td>
<td>{{ row.email }}</td>
<td>{{ row.gender }}</td>
</tr>
{% endfor %}
</table>
</div>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/commons.css">
</head>
<style>
.label{
width: 80px;
text-align: right;
display: inline-block;
}
</style>
<body>
<form action="/login" method="post">
<p>
<label for="username">用户名:</label>
<input id="user" name="user" type="text">
</p>
<p>
<label for="password">密码:</label>
<input id="pwd" name="pwd" type="password">
<input type="submit" value="提交">
<span style="color: red;">{{ error_msg }}</span>
</p>
</form>
<script src="/static/jquery.min.js"></script>
</body>
</html>
Django示例之注册页面
registerd功能页面
views.py
from django.shortcuts import render,HttpResponse,redirect
import os
# Create your views here.
def index(request):
return HttpResponse('index')
error_masg = ""
def registered(request):
if request.method == "GET":
print(request.method)
return render(request,'registered.html')
elif request.method == "POST":
obj = request.FILES.get('files')
print(obj,type(obj),obj.name)
file_path = os.path.join('upload',obj.name)
f = open(file_path,mode='wb')
for i in obj.chunks():
f.write(i)
f.close()
return render(request,'registered.html')
# 文件上传功能需要在from标签注明enctype="multipart/form-data"
registerd.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/registered" method="POST" enctype="multipart/form-data">
<p>
<input type="text" name="user" placeholder="用户名">
</p>
<p>
<input type="password" name="pwd" placeholder="密码" />
</p>
<p>
男: <input type="radio" name="gender" value="1" />
女: <input type="radio" name="gender" value="2" />
张扬: <input type="radio" name="gender" value="3" />
</p>
<p>
男: <input type="checkbox" name="favor" value="11" />
女: <input type="checkbox" name="favor" value="22" />
张扬: <input type="checkbox" name="favor" value="33" />
</p>
<p>
<select name="area" multiple>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="tj">天津</option>
<option value="gz">广州</option>
</select>
</p>
<p>
<input type="file" name="files">
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
Django的CBV和FBV
Django的字典
Example
views.py
from django.shortcuts import render,redirect
USER_DICT = {
'k1':'root1',
'k2':'root2',
'k3':'root3',
'k4':'root4',
}
ef index(request):
return render(request,'index.html',{'user_dict':USER_DICT})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ user_dict.k1 }}
<u1>
{% for k in user_dict.keys %}
<li>{{ k }}</li>
{% endfor %}
</u1>
<u1>
{% for val in user_dict.values %}
<li>{{ val }}</li>
{% endfor %}
</u1>
<u1>
{% for row in user_dict.items %}
<li>{{ k }}-{{ row }}</li>
{% endfor %}
</u1>
</body>
</html>
Django的URL正则
# URL两种方式
# a.
url(r'^detail-(\d+)-(\d+).html', views.detail),
def func(request, nid, uid):
pass
def func(request, *args):
args = (2,9)
def func(request, *args, **kwargs):
args = (2,9)
# b.
url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)
def func(request, nid, uid):
pass
def funct(request, **kwargs):
kwargs = {'nid': 1, 'uid': 3}
def func(request, *args, **kwargs):
args = (2,9)
urls.py
from django.conf.urls import url
from django.contrib import admin
from cmdb import views
urlpatterns = [
# url(r'^admin/', admin.site.urls),
url(r'^index',views.index),
# url(r'^login',views.login),
# url(r'^registered',views.registered),
# url(r'^home/',views.Home.as_view),
url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html',views.detail),
]
view.py
from django.shortcuts import render,HttpResponse,redirect
import os
# Create your views here.
USER_DICT={
'1':{'name':'root','email':'[email protected]'},
'2':{'name':'root','email':'[email protected]'},
'3':{'name':'root','email':'[email protected]'},
'4':{'name':'root','email':'[email protected]'},
'5':{'name':'root','email':'[email protected]'},
}
# USER_DICT = {
# 'k1':'root1',
# 'k2':'root2',
# 'k3':'root3',
# 'k4':'root4',
# }
def index(request):
return render(request,'index.html',{'user_dict':USER_DICT})
def detail(request,nid,uid):
print(nid,uid)
# nid = request.GET.get('nid')
return HttpResponse(nid)
# detail_info = USER_DICT[nid]
# return render(request,'detail.html',{'detail_info':detail_info})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<u1>
{% for k,row in user_dict.items %}
<li><a href="/detail-{{ k }}.html">{{ row.name }}</a></li>
{% endfor %}
</u1>
</body>
</html>
Django对应路由名称.
# name
# 对URL路由关系进行命名,以后可以根据此名称生成自己想要的URL,
# name只是为了更方便的生成URL,
url(r'^asdfasdfasdf/', views.index, name='i1'),
url(r'^yug/(\d+)/(\d+)/', views.index, name='i2'),
url(r'^buy/(?P<pid>\d+)/(?P<nid>\d+)/', views.index, name='i3'),
def func(request, *args, **kwargs):
from django.urls import reverse
url1 = reverse('i1') # asdfasdfasdf/
url2 = reverse('i2', args=(1,2,)) # yug/1/2/
url3 = reverse('i3', kwargs={'pid': 1, "nid": 9}) # buy/1/9/
xxx.html
{% url "i1" %} # asdfasdfasdf/
{% url "i2" 1 2 %} # yug/1/2/
{% url "i3" pid=1 nid=9 %} # buy/1/9/
注:
# 当前的URL
request.path_info
路由分发
project/urls.py
from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
url(r'^cmdb/', include("app01.urls")),
url(r'^monitor/', include("app02.urls")),
]
app01/urls.py
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^login/', views.login),
]
app02/urls.py
from django.conf.urls import url,include
from django.contrib import admin
from app02 import views
urlpatterns = [
url(r'^login/', views.login),
]
Django之ORM操作.
Django的ORM简介
在MVC或者MTV的设计模式中,模型(M代表对数据库的操作,那么如何操作数据库呢?
虽然我们可以手动切换数据库环境,然后敲入SQL语句,但明显不符合我们?
作为一名运维开发,讲的是自动化,实现的是Python环境下的操作,所以我们必然是通过写Python代码的方式。可是....Python和数据库语言SQL是两码事啊,它根本操作不了数据库!没关系,我们可以在Python代码中嵌入SQL语句,比如下面的方式:
常用的model模型
CharField 字符串字段
IntegerField 整数型字段
DateTimeField 时间日期字段
ForeignKey 定义多对一关系
Model迁移
# 创建连接,这里先忽略创建方法
conn = ......
# 创建游标
cursor = conn.cursor()
# 执行SQL,并返回收影响行数
effect_row = cursor.execute("insert into host (hostname,port,ip) values('ubuntu','22','10.0.0.2');")
# 提交,不然无法保存新建或者修改的数据
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
但是问题又来了,Python怎么创建和数据库的连接呢?或者更直白的说Python怎么连接数据库呢?可以使用类似pymysql这一类的第三方模块(针对不同的数据库,有不同的模块)。于是我们可以进行如下的连接:
conn = pymysql.connect(host='137.78.5.130', port=3306, user='root', passwd='123456', db='test')
好了,这样似乎就Ok了。但是,如果你有很多的数据库操作,并且你的Python程序员不是专业的DBA,写的SQL语句很烂,甚至经常写错,怎么办?
聪明的人想出了一个办法:用Python语法来写,然后使用一个中间工具将Python代码翻译成原生的SQL语句,这样你总不会写错了吧?这个中间工具就是所谓的ORM(对象关系映射)
ORM将一个Python的对象映射为数据库中的一张关系表。它将SQL封装起来,程序员不再需要关心数据库的具体操作,只需要专注于自己本身代码和业务逻辑的实现。
于是,整体的实现过程就是:Python代码,通过ORM转换成SQL语句,再通过pymysql去实际操作数据库。
最典型的ORM就是SQLAlchemy了,如果你的Web框架自身不带ORM系统,那么你可以安装使用它,SQLAlchemy使用者还是比较多的,本身功能也比较强大,大家可以自行学习。
Django自带ORM系统,不需要额外安装别的ORM。当然,也可以安装并使用其它的ORM,比如SQLAlchemy,但是不建议这么做,因为Django系统庞大,集成完善,模型层与视图层、模板层结合得比较紧密,使用自带的ORM更方便更可靠,并且Django自带的ORM功能也非常强大,也不难学。
Django的ORM系统体现在框架内就是模型层。想要理解模型层的概念,关键在于理解用Python代码的方式来定义数据库表的做法!一个Python的类,就是一个模型,代表数据库中的一张数据表!Django奉行Python优先的原则,一切基于Python代码的交流,完全封装SQL内部细节。
使用Django的ORM增删改查
1.创建django程序:
# 终端命令: django-admin startproject sitename
# IDE创建django程序本质上都是自动那个上述命令
# 其他常用命令
python manage.py runserver 0.0.0.0
python manage.py startapp appname
python manage.py syncdb
python manage.py makemigrations
python manage.py migrate
python manage.py createuperuser
2.创建cmdbapp应用
python manage.py startapp cmdb
3.模板
TEMPLATE_DIRS = (
os.path.join(BASE_DIR,``'templates'``),
)
4.静态文件
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static')
)
5. 修改数据库引擎
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'orm_demo',
'USER':'root',
'PASSWORD':'ZHOUjian.22',
'HOST':'121.36.43.223',
'PORT':'3306',
}
}
sudo wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate
sudo python get-pip.py
sudo pip install pymysql
sudo pip3 install pymysql
# 安装mysql-connector
# sudo pip3 install mysql-connector-python
#
# 因为Django里面默认使用MySQLdb模块,但我们一般使用pymysq所以需要到
# Project目录的__init__.py文件加入两行
import pymysql
pymysql.install_as_MySQLdb()
# 同时注意准备好Mysql环境并允许远程登录.
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'ZHOUjian.22'
flush privileges;
6.创建一个模型
一个模型(model)就是一个单独的、确定的数据的信息源,包含了数据的字段和操作方法。通常,每个模型映射为一张数据库中的表。
基本的原则如下:
- 每个模型在Django中的存在形式为一个Python类
- 每个模型都是django.db.models.Model的子类
- 模型的每个字段(属性)代表数据表的某一列
- Django将自动为你生成数据库访问API
/Django_ORM_Demo/cmdb/models.py\
from django.db import models
# Create your models here.
class UserInfo(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=20)
# 每一个字段都是一个类属性,每个类属性表示数据表中的一个列。
# 上面的代码,相当于下面的原生SQL语句:
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
python manage.py migrate
python manage.py makemigrations
注意
- 表名
myapp_person
由Django自动生成,默认格式为“项目名称+下划线+小写类名”,你可以重写这个规则。 - Django默认自动创建自增主键
id
,当然,你也可以自己指定主键。 - 上面的SQL语句基于
PostgreSQL
语法。
通常,我们会将模型编写在其所属app下的models.py文件中,没有特别需求时,请坚持这个原则,不要自己给自己添加麻烦。
创建了模型之后,在使用它之前,你需要先在settings文件中的
INSTALLED_APPS
处,注册models.py
文件所在的myapp
。看清楚了,是注册app,不是模型,也不是models.py
。如果你以前写过模型,可能已经做过这一步工作,可跳过。
7.我们创建一个简单的项目
1.创建一个应用app01
python manage.py startapp app01
2.先到Django_ORM_Demo1/urls.py做一个分发路由
from django.conf.urls import url,include
urlpatterns = [
url(r'^cmdb/', include("app01.urls")),
]
- 到Django_ORM_Demo1/app01/urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^login/',views.login),
url(r'^orm/',views.orm),
]
- 写app01/view.py逻辑文件
from django.shortcuts import render,HttpResponse,redirect
from cmdb import models
# Create your views here.
def login(request):
if request.method == "GET":
return render(request, 'login.html')
elif request.method == "POST":
return render(request, 'login.html')
else:
return redirect('/index/')
def orm(request):
# 创建1
# models.UserInfo.objects.create(username='root',password='123')
# 创建2
# obj = models.UserInfo(username='alice',password='567')
# obj.save()
# 创建3
# dic = {'username':'tom','password':'999'}
# models.UserInfo.objects.create(**dic)
# 查询所有
# result = models.UserInfo.objects.all()
# for row in result:
# print(row.id,row.username,row.password)
# print(result)
# where(filter)查询
# result = models.UserInfo.objects.filter(username='root')
# 如果在'root',后面加上password='123'等同于and条件
# for row in result:
# print(row.id,row.username,row.password)
# print(result)
# 删除id=10的行
# models.UserInfo.objects.filter(id=10).delete()
# 更新所有
# models.UserInfo.objects.all().update(password=1314520)
# return HttpResponse('orm')
# 更新指定
models.UserInfo.objects.filter(id=8).update(password='WuNai')
return HttpResponse('orm')
5./Django_ORM_Demo1/templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="POST" enctype="multipart/form-data">
<p>
<input type="text" name="user" placeholder="用户名">
</p>
<p>
<input type="password" name="pwd" placeholder="密码" />
</p>
<p>
男: <input type="radio" name="gender" value="1" />
女: <input type="radio" name="gender" value="2" />
张扬: <input type="radio" name="gender" value="3" />
</p>
<p>
男: <input type="checkbox" name="favor" value="11" />
女: <input type="checkbox" name="favor" value="22" />
张扬: <input type="checkbox" name="favor" value="33" />
</p>
<p>
<select name="area" multiple>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="tj">天津</option>
<option value="gz">广州</option>
</select>
</p>
<p>
<input type="file" name="files">
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
7.运行django项目
python manage.py runserver
当你每次对模型进行增、删、修改时,请务必执行命令python manage.py migrate,让操作实际应用到数据库上。这里可以选择在执行migrate之前,先执行python manage.py makemigrations让修改动作保存到记录文件中,方便github等工具的使用。
ORM字段
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定义无符号整数字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
字段
ORM参数
null 数据库中字段是否可以为空
db_column 数据库中字段的列名
db_tablespace
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引
obj = UserGroup.objects.filter(id=1).update(caption='CEO')
obj = UserGroup.objects.filter(id=1).first()
obj.caption = "CEO"
obj.save()
verbose_name Admin中显示的字段名称,django设置中文
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}
validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)
参数
Example
ORM怎删改查进阶操作
# 获取个数
#
# models.Tb1.objects.filter(name='seven').count()
# 大于,小于
# models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
# models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
# models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
# in
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
# isnull
# Entry.objects.filter(pub_date__isnull=True)
# contains
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven")
# range
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
# 其他类似
# startswith,istartswith, endswith, iendswith,
# order by
#
# models.Tb1.objects.filter(name='seven').order_by('id') # asc
# models.Tb1.objects.filter(name='seven').order_by('-id') # desc
# group by
#
# from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
# limit 、offset
# models.Tb1.objects.all()[10:20]
# regex正则匹配,iregex 不区分大小写
# Entry.objects.get(title__regex=r'^(An?|The) +')
# Entry.objects.get(title__iregex=r'^(an?|the) +')
# date
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
# year
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)
# month
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)
# day
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)
# week_day
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)
# hour
#
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12)
# minute
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29)
# second
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31)
其他操作
# extra
#
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)
# Q
# 方式一:
# Q(nid__gt=10)
# Q(nid=8) | Q(nid__gt=10)
# Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
# 方式二:
# con = Q()
# q1 = Q()
# q1.connector = 'OR'
# q1.children.append(('id', 1))
# q1.children.append(('id', 10))
# q1.children.append(('id', 9))
# q2 = Q()
# q2.connector = 'OR'
# q2.children.append(('c1', 1))
# q2.children.append(('c1', 10))
# q2.children.append(('c1', 9))
# con.add(q1, 'AND')
# con.add(q2, 'AND')
# models.Tb1.objects.filter(con)
# 执行原生SQL
# from django.db import connection, connections
# cursor = connection.cursor() # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone()
Django实例之基于ORM实现用户登录
DJango实例之ORM外键
Django的一对多和多对多
一对多表创建和获取表单数据三种方式
创建一个django项目,创建app,配置static,加入apps,注释csrf,配置数据库连接
1. Django_Orm_table_Demo1/urls.py
from django.conf.urls import url,include
urlpatterns = [
url(r'^cmdb/', include("cmdb.urls")),
]
2. Django_Orm_table_Demo1/cmdb/urls.py
from django.conf.urls import url,include
from cmdb import views
urlpatterns = [
url(r'^business',views.business),
]
3.Django_Orm_table_Demo1/cmdb/models.py
from django.db import models
class Business(models.Model):
caption = models.CharField(max_length=32)
code = models.CharField(max_length=32,default='PASS')
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32,db_index=True)
ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
port = models.IntegerField()
b = models.ForeignKey(to="Business", to_field='id',on_delete=models.CASCADE,)
4. Django_Orm_table_Demo1/cmdb/views.py
from django.shortcuts import render,HttpResponse,redirect
from cmdb import models
def business(request):
v1 = models.Business.objects.all()
v2 = models.Business.objects.all().values('id','caption')
v3 = models.Business.objects.all().values_list('id','caption')
return render(request, 'business.html', {'v1': v1,'v2': v2, 'v3': v3})
def host(request):
v4 = models.Host.objects.filter(nid__gt=0)
for row in v4:
print(row.nid,row.hostname,row.ip,row.port,row.b_id,row.b.caption,row.b.code,sep="\t")
# print(row.b.fk.name)
# return HttpResponse('host')
return render(request,'host.html',{'v4':v4})
# 外键通过`.`进行跨表
# `__`也能跨表
5.Django_Orm_table_Demo1/templates/business.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>业务线列表(对象)</h1>
<ul>
{% for row in v1 %}
<li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li>
{% endfor %}
</ul>
<h1>业务线列表(字典)</h1>
<ul>
{% for row in v2 %}
<li>{{ row.id }} - {{ row.caption }}</li>
{% endfor %}
</ul>
<h1>业务线列表(元组)</h1>
<ul>
{% for row in v3 %}
<li>{{ row.0 }} - {{ row.1 }}</li>
{% endfor %}
</ul>
</body>
</html>
5.Django_Orm_table_Demo1/templates/host.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>业务线列表(对象)</h1>
<table border="">
<thead>
<tr>
<th>主机名</th>
<th>IP</th>
<th>端口</th>
<th>业务线名称</th>
</tr>
</thead>
<tbody>
{% for row in v4 %}
<tr bid="{{ row.nid }}" bid="{{ row.b_id }}">
<td>{{ row.hostname }}</td>
<td>{{ row.ip }}</td>
<td>{{ row.port }}</td>
<td>{{ row.b.caption }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
一对多数据实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.hide{
display: none;
}
.shade{
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: black;
opacity: 0.6;
z-index: 100;
}
.add_model{
position: fixed;
height: 300px;
width: 500px;
top: 100px;
left: 50%;
z-index: 111;
border: 1px solid red;
background-color: blanchedalmond;
margin-left: -270px;
}
</style>
</head>
<body>
<h1>业务线列表(对象)</h1>
<div>
<input id="add_host" type="button" value="添加" />
</div>
<table border="1">
<thead>
<tr>
<th>主机名</th>
<th>IP</th>
<th>端口</th>
<th>业务线名称</th>
<th>序号</th>
</tr>
</thead>
<tbody>
{% for row in v4 %}
<tr bid="{{ row.nid }}",bid="{{ row.b_id }}">
<td>{{ forloop.counter }}</td>
<td>{{ row.hostname }}</td>
<td>{{ row.ip }}</td>
<td>{{ row.port }}</td>
<td>{{ row.b.caption }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="shade hide"></div>
<div class="add_mode hide ">
<div class="group">
<input type="text" placeholder="主机名" name="hostname" />
</div>
<div class="group">
<input type="text" placeholder="IP" name="ip" />
</div>
<div class="group">
<input type="text" placeholder="端口" name="port" />
</div>
<div class="group">
<select>业务线一</select>
<select>业务线二</select>
<select>业务线三</select>
<select>业务线四</select>
</div>
<input type="submit" value="提交" />
<input type="submit" value="取消" />
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
$(function () {
$('#add_host').click(function () {
$(".shade,.add_model").removeClass('hide');
})
})
</script>
</body>
</html>