Web开发是Python语言应用领域的重要部分,也是目前最主要的Web开发语言之一,在其二十多年的历史中出现了数十种Web框架,好比Django、Tornado、Flask、Twisted、Bottle和Web.py等,有的历史悠久,有的发展迅速,还有的已经中止维护.php
Django:css
发布于2003年,是当前Python世界里最负盛名且最成熟的Web框架,最初被用来制做在线新闻的Web站点,Django的各模板之间结合得比较紧密,因此在功能强大的同时又是一个相对封闭的系统(依然能够自定义的),可是其健全的在线文档和开发社区,使开发者遇到问题能找到解决办法.html
Tornado:前端
一个强大的、支持协程、高效并发且可扩展的Web服务器,发布于2009年9月,应用于FriendFeed、Facebook等社交网站。它的强项在于能够利用异步协程机制实现高并发的服务。python
Flask:mysql
Python Web框架家族里比较年轻的一个,发布于2010年,它吸取了其余框架的优势而且把本身的主要领域定义在了微小项目上,以短小精干,简洁明了著称。jquery
Twisted:linux
一个有着十多年历史的开源事件驱动框架。它不像前三种着眼于Web应用开发,而是适用从传输层到自定义应用协议的全部类型的网络程序的开发,并能在不一样的操做系统上提供很高的运行效率。可是,目前对Python3的支持有限,建议使用Python2.7。nginx
MVC
git
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),这个请求回去访问视图函数,(若是不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户),视图函数调用模型,模型去数据库查找数据,而后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
首先介绍一下Django,Django具备如下特色:
根据前面的选择原则咱们逐条对比一下:
# 1. 主流、活跃程序: # 从GitHub的数据来看,Django的开发很是活跃,迭代速度也很是快 # 2. 是否能够知足需求: # Django以要素齐全、工具丰富、框架庞大著称,基本上别的框架有的他有,别的框架没有的他也有, # 若是Django知足不了需求,那么别的框架同时也同样. # 3. 时效性: # Django有很长的开发喝实践过程,或早或晚的文档、教程、帮助、博客等等很是多,资料更新速度也很快. # 4. 入门友好程度 # 一个框架可否流行起来,对新手入门友好很是关键.Django在这一点作的很是好.
Django也有一些缺点:
框架庞大,被认为不够精简,捆绑的内容太多
首先,对于新手,Django集成好的工具和部件,让你无须再费脑力去学习如何安装、调试、集成、兼容别的工具,Django帮你都集成好了,并且保证兼容性,可用性和方便性,就比如联想一体机,开机即用,效率也高,而一些如flask的框架,虽然精简,可是你要本身安装各类工具、ORM、插件等等,比如DIY电脑,在用以前,要知道买什么配件,怎么搭配,怎么组装,怎么配置效率才高,将新手的热情消耗在非关键性的内容上.
其次,对于老手,Django也是开放的,你彻底能够关闭没必要要的功能,忽略不使用的组件,或者自定义但愿的组件,包括ORM和Template在内,均可以自由选择.在异步通讯方面略有欠缺
从本质上来说,Tornado在异步协程机制实现高并发的服务上要强一些,Django在这方面有追赶的目标,但这不是说Django就差到不能用了.
想要熟练地使用Django进行Web开发,设计生产环境可用的,可以应付必定规模访问量的Web应用,开发者要学会的远远不止Django自己,Python基础、环境搭建、前端语言、API设计、网站架构、系统管理、持续集成、服务化数据处理、并发处理等等,都是相关的只是领域,包括但不限于如下的内容:
# 熟悉Python语言 # 对前端的HTML\CSS\JavaScript比较熟悉 # 对网络基础,好比HTTP、TCP/IP等比较熟悉 # 熟悉数据库、缓存、消息队列等技术的使用场景和使用方法 # 平常能使用Linux或Mac系统工做(Windows标配) # 有性能优化经验,能快速定位问题 # 除此以外,还要对业务有深入理解,可以写出可维护性足够高的代码,固然,以上都是对经验丰富的开发者而言, # 对于新手刚入门者,咱们朝着这个目标努力学习就好 # 下面是基于Python的Web开发技术栈 # 一、web应用 # 运行在浏览器上的应用 # 二、c/s b/s 架构 # client/server:客户端服务器架构,C++ # brower/server:浏览器服务器架构,Java、Python # 底层均是基于socket # 三、Python Web框架 # a.socket b.页面路由 c.模板渲染 # Django a用的wsgiref b本身写的 c本身写的 功能全面 # Flask a用的第三方 b本身写的 c本身写的 小而轻 # Tornado a本身写的 b本身写的 c本身写的 支持高并发
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)版本。每一个版本都添加了新功能和错误修复,从支持新类型的数据库,模版引擎和缓存,到添加“通用”视图函数和类(这减小了开发人员必须编写的代码量)一些编程任务。
服务器端框架的受欢迎程度没有任何可靠和明确的测量(尽管Hot Frameworks网站 尝试使用诸如计算每一个平台的GitHub项目数量和StackOverflow问题的机制来评估流行度)。一个更好的问题是Django是否“足够流行”,以免不受欢迎的平台的问题。它是否继续发展?若是您须要帮助,能够帮您吗?若是您学习Django,有机会得到付费工做吗?
基于使用Django的流行网站数量,为代码库贡献的人数以及提供免费和付费支持的人数,那么是的,Django是一个流行的框架
使用Django的流行网站包括:Disqus,Instagram,骑士基金会,麦克阿瑟基金会,Mozilla,国家地理,开放知识基金会,Pinterest和开放栈.
Web框架一般将本身称为"特定" 或"无限制".
特定框架是对处理任何特定任务的"正确方法" 有意见的框架,他们常常支持特定领域的快速发展(解决特定类型的问题),由于正确的作法是一般被很好的理解和记录在案,然而,他们在解决其主要领域以外的问题可能不那么灵活,而且倾向于为能够使用那些组件和方法提供较少的选择.
相比之下,无限制的框架对于将组件粘合在一块儿以实现目标或甚至应使用那些组件的最佳方式限制较少,它们使开发人员更容易使用最合适的工具来完成特定任务,尽管您须要本身查找这些组件。
Django“有点有意义”,所以提供了“两个世界的最佳”。它提供了一组组件来处理大多数Web开发任务和一个(或两个)首选的使用方法。然而,Django的解耦架构意味着您一般能够从多个不一样的选项中进行选择,也能够根据须要添加对全新的支持
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(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'
在传统的数据驱动网站中,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 # 数据库 模板文件 业务处理
# 用户请求 ----> URL对应关系(匹配) ----> 视图函数 ----> 返回用户字符串 # 用户请求 ----->URL对应关系(匹配) ----> 视图函数 ----> 打开一个HTML文件,读取内容 # 1.浏览器发送请求 # 2.wsgi服务器接收到请求,将请求解析交给Django # 3.Django中间件过滤请求信息,交给路由匹配,若是匹配成功 # 4.路由完成业务逻辑的分发,到指定app下views中指定的视图函数,能够去数据库里面取数据, # 5.视图函数完成具体的业务逻辑,和模板渲染,返回字符串响应结果 # 6.将处理结果经过服务器返回给浏览器
# 安装: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里面,注意用;分号隔开.
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-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: 业务逻辑代码
配置数据库
# 安装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主机头部攻击的安全措施 # (若是须要调用js,css文件能够配置一下settings.py,html文件就能够调用static里面的css,js文件了) STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), )
启动项目
# 终端: 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
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: youmen@163.com Password: Password (again):
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>
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>
URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。
from django.conf.urls import url #循环urlpatterns,找到对应的函数执行,匹配上一个路径就找到对应的函数执行,就再也不往下循环了,并给函数传一个参数request,和wsgiref的environ相似,就是请求信息的全部内容 urlpatterns = [ url(正则表达式, views视图函数,参数,别名), ] # 参数说明 # 正则表达式: 一个正则表达式字符串 # views视图函数: 一个可调用对象,一般为一个视图函数或一个指定视图函数路径的字符串 # 参数: 可选的要传递给视图函数的默认参数(字典形式) # 别名: 一个可选的name参数
注意
from django.urls import path urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
场景设置
假如咱们作了图书管理系统,图书管理系统是这样设置的,首页给咱们展现满世界排名靠前面的书,当你想看那一年的书,你的url就应该拼接上哪一年,而且将此年份传递给后端逻辑,也就是对应的views函数中。好比你的首页为127.0.0.1:8000/book/,当你想看2003年有哪些书时,你访问的url就对应为127.0.0.1:8000/book/2003/,这也是先后端间接传递数据,那么这种需求如何完成呢?咱们先写一个views函数,与对应的html。
URL配置
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^index/', views.login), # 无名分组 (给应用视图函数传递位置参数) url(r'books/(\d{4})/', views.year_books), # 彻底匹配 url(r'^books/(\d{4})/(\d{2})/', views.year_mouth_books), url(r'^books/(\d{4})/(\d{2})/(\d{2})', views.year_mouth_day_books), ]
我为何不写2003?而写\d{4}? 你直接写2003就至关于写死了,若是用户想看200四、200五、2006....等,你要写一堆的url吗,是否是在articles后面写一个正则表达式/d{4}/就行啦。
可是此时你写的仍是有问题的,我以前说过,你此时写的url路由匹配是模糊匹配,你若是是这样写的url,当从浏览器输入127.0.0.1:8000/book/2003/12/08它仍是映射到year_books这个函数的,由于url遵循轮询机制:
因此针对上面的状况,咱们应该将这些URL设定为彻底匹配
注意事项
# 1. urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则再也不继续。 # 2. 若要从URL中捕获一个值,只须要在它周围放置一对圆括号(分组匹配)。 # 3. 不须要添加一个前导的反斜杠(也就是写在正则最前面的那个/),由于每一个URL 都有。例如,应该是^books而不是 ^/books。 # 4. 每一个正则表达式前面的'r' 是可选的可是建议加上。 # 5. ^books& 以什么结尾,以什么开头,严格限制路径。 # 是否开启URL访问地址后面没有/跳转至带有/的路径的配置项 APPEND_SLASH=True
Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其做用就是自动在网址结尾加'/'。其效果就是:咱们定义了urls.py:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^blog/$', views.blog), ] # 访问 http://www.example.com/blog 时,默认将网址自动转换为 http://www.example/com/blog/ 。 # 若是在settings.py中设置了 APPEND_SLASH=False,此时咱们再请求 http://www.example.com/blog 时就会提示找不到页面。 # 注意:无名分组传递给views函数的为位置参数。
上面的示例使用简单的正则表达式分组匹配(经过圆括号)来捕获URL中的值并以位置参数形式传递给视图。
在更高级的用法中,能够使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。
在Python的正则表达式中,分组命名正则表达式组的语法是
(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式。
URL配置
from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), #某年的,(?P<year>[0-9]{4})这是命名参数(正则命名匹配还记得吗?), # 那么函数year_archive(request,year),形参名称必须是year这个名字。 # 并且注意若是你这个正则后面没有写$符号,即使是输入了月份路径,也会被它拦截下拉,由于它的正则也能匹配上 url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), #某年某月的 url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), # 某年某月某日的 ] # 这个实现与前面的示例彻底相同,只有一个细微的差异:捕获的值做为关键字参数而不是位置参数传递给视图函数
Views配置
def year_article(request, year=2000): print(year) return HttpResponse(f'出版年限:{year}') def year_mouth_article(request, year, mouth): print(year, mouth) return HttpResponse(f'出版年限:{year},出版月份:{mouth}') def year_mouth_day_article(request, year, mouth, day): print(year, mouth, day) return HttpResponse(f'出版年限:{year},出版月份:{mouth},出版天数:{day}')
注意: 命名分组是url设置的组名对应views函数的参数,这是关键字参数,也能够对应views函数的默认值参数。
在实际应用中,使用分组命名匹配的方式可让你的URLconf 更加明晰且不容易产生参数顺序问题的错误,可是有些开发人员则认为分组命名组语法太丑陋、繁琐。
至于究竟应该使用哪种,你能够根据本身的喜爱来决定。
URLconf匹配的位置
URLconf 在请求的URL 上查找,将它当作一个普通的Python 字符串。不包括GET和POST参数以及域名。
例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/。
在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找
myapp/
。URLconf 不检查请求的方法。换句话讲,全部的请求方法 —— 同一个URL的
POST
、GET
、HEAD
等等 —— 都将路由到相同的函数.
捕获的参数永远是字符串
每一个在URLconf中捕获的参数都做为一个普通的Python字符串传递给视图,不管正则表达式使用的是什么匹配方式。例如,下面这行URLconf 中:
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
传递到视图函数views.year_archive()中的year参数永远是一个字符串类型
视图函数中指定默认值
# urls.py中 from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/$', views.page), url(r'^blog/page(?P<num>[0-9]+)/$', views.page), ] # views.py中,能够为num指定默认值 def page(request, num="1"): pass
在上面的例子中,两个URL模式指向相同的view - views.page - 可是第一个模式并无从URL中捕获任何东西。
若是第一个模式匹配上了,page()函数将使用其默认参数num=“1”,若是第二个模式匹配,page()将使用正则表达式捕获到的num值。
建立项目urls.py
from django.conf.urls import url,include from app01 import views urlpatterns = [ # 首页 url(r'^$',views.base), url('^app01/',include('app01.urls')), ]
aap01应用的urls.py
from django.conf.urls import url from app01 import views urlpatterns = [ url('^login/',views.login), ]
app01应用的views.py
from django.shortcuts import render from django.shortcuts import redirect def login(request): return render(request,'login1.html') def base(request): return render(request,'base.html')
URLconfs 具备一个钩子,让你传递一个Python 字典做为额外的参数传递给视图函数。
django.conf.urls.url()
函数能够接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。
from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), # 注意,这就像一个命名分组同样,你的函数里面的必须有一个形参,形参必须叫作foo才行,若是是命名分组的url,那么foo参数写在函数的哪一个位置都行,若是不是命名分组,那么都是将这个形参写在参数的最后。 ]
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求而且返回Web响应。
响应能够是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
不管视图自己包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此以外没有更多的要求了——能够说“没有什么神奇的地方”。为了将代码放在某处,你们约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为
views.py
的文件中。
一个简单的视图
下面是一个以HTML文档的形式返回当前日期和时间的视图
from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
首先,咱们从django.http模块引入了HttpResponse类,以及Python的datetime库.
接着,咱们定义了current_datetime函数,他就是视图函数,每一个视图函数都使用HttpRequest对象做为第一个参数,而且一般称之为request.
注意,视图函数的名称并不重要,不须要用一个统一的命名方式来命名,以便让Django识别它,咱们将其命名为current_datetime,是由于这个名称可以比较准确地反映出他实现的功能.
这个视图会返回一个HttpResponse对象,其中包含生成的响应,每一个视图函数都负责返回一个HttpResponse对象
DJango使用功能请求和响应对象来经过系统传递状态.
当浏览器向服务端请求一个页面时,Django建立一个HttpRequest对象,该对象包含关于请求的元数据,而后, Django加载相应的视图,将这个HttpRequest对象做为第一个参数传递给视图函数.
每一个视图负责返回一个HttpResponse对象
简单过程
当一个页面被请求时,Django就会建立一个包含本次请求源信息(请求报文中的请求行,首部信息,内容主体等)
请求相关的经常使用值
# path_info 返回用户访问url,不包括域名 # method 请求中使用的HTTP方法的字符串表示,全大写表示 # GET 包含全部HTTP GET参数的类字典对象 # POST 包含全部HTTP POST参数的类字典对象 # body 请求体,byte类型 request.POST的数据就是从body里面提取到的
上传文件示例
def upload(request): """ 保存上传文件前,数据须要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的所有内容读进内存。从内存读取一次,写磁盘一次。 但当上传文件很大时,django会把上传文件写到临时文件中,而后存放到系统临时文件夹中。 :param request: :return: """ if request.method == "POST": # 从请求的FILES中获取上传文件的文件名,file为页面上type=files类型input的name属性值 filename = request.FILES["file"].name # 在项目目录下新建一个文件 with open(filename, "wb") as f: # 从上传的文件对象中一点一点读 for chunk in request.FILES["file"].chunks(): # 写入本地文件 f.write(chunk) return HttpResponse("上传OK")
方法
1.HttpRequest.get_host() 根据从HTTP_X_FORWARDED_HOST(若是打开 USE_X_FORWARDED_HOST,默认为False)和 HTTP_HOST 头部信息返回请求的原始主机。 若是这两个头部没有提供相应的值,则使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有详细描述。 USE_X_FORWARDED_HOST:一个布尔值,用于指定是否优先使用 X-Forwarded-Host 首部,仅在代理设置了该首部的状况下,才能够被使用。 例如:"127.0.0.1:8000" 注意:当主机位于多个代理后面时,get_host() 方法将会失败。除非使用中间件重写代理的首部。 2.HttpRequest.get_full_path() 返回 path,若是能够将加上查询字符串。 例如:"/music/bands/the_beatles/?print=true" 3.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 返回签名过的Cookie 对应的值,若是签名再也不合法则返回django.core.signing.BadSignature。 若是提供 default 参数,将不会引起异常并返回 default 的值。 可选参数salt 能够用来对安全密钥强力攻击提供额外的保护。max_age 参数用于检查Cookie 对应的时间戳以确保Cookie 的时间不会超过max_age 秒。 >>> request.get_signed_cookie('name') 'Tony' >>> request.get_signed_cookie('name', salt='name-salt') 'Tony' # 假设在设置cookie的时候使用的是相同的salt >>> request.get_signed_cookie('non-existing-cookie') ... KeyError: 'non-existing-cookie' # 没有相应的键时触发异常 >>> request.get_signed_cookie('non-existing-cookie', False) False >>> request.get_signed_cookie('cookie-that-was-tampered-with') ... BadSignature: ... >>> request.get_signed_cookie('name', max_age=60) ... SignatureExpired: Signature age 1677.3839159 > 60 seconds >>> request.get_signed_cookie('name', False, max_age=60) False 4.HttpRequest.is_secure() 若是请求时是安全的,则返回True;即请求通是过 HTTPS 发起的。 5.HttpRequest.is_ajax() 若是请求是经过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是不是字符串'XMLHttpRequest'。 大部分现代的 JavaScript 库都会发送这个头部。若是你编写本身的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 能够工做。 若是一个响应须要根据请求是不是经过AJAX 发起的,而且你正在使用某种形式的缓存例如Django 的 cache middleware, 你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应可以正确地缓存。
Example1
from django.shortcuts import render,HttpResponse,redirect # Create your views here. def index(request): print(request.method) #请求方式 print(request.path) #请求路径,不带参数的 print(request.POST) #post请求数据 字典格式 print(request.GET) #get的请求数据 字典格式 print(request.META) #请求头信息,未来用到哪一个我们再说哪一个 print(request.get_full_path()) #获取请求路径带参数的,/index/?a=1 print(request.is_ajax()) #判断是否是ajax发送的请求,True和False ''' Django必定最后会响应一个HttpResponse的示例对象 三种形式: HttpResponse('字符串') 最简单 render(页面) 最重要 2.1 两个功能 -- 读取文件字符串 -- 嵌入变量(模板渲染) html里面:{{ name }} , {'name':'太白'}做为render的第三个参数,想写多个变量{'name':'太白','hobby':['篮球','羽毛球']....} redirect() 重定向 最难理解,某个网站搬家了,网址变了,访问原来的网址就重定向到一个新网址,就叫作重定向,网站本身作的重定向,你访问仍是访问的你以前的, 你本身啥也不用作,浏览器发送请求,而后服务端响应,而后服务端告诉浏览器,你直接跳转到另一个网址上,那么浏览器又自动发送了另一个请求,发送到服务端, 服务端返回一个页面,包含两次请求,登录成功后跳转到网站的首页,网站首页的网址和你login登录页面的网址是不用的。 ''' return render(request,'index.html',{'name':'太白'}) # return HttpResponse('ok')
与Django自动建立的HttpRequest对象相比,HttpResponse对象使咱们的职责范围了,咱们写的每一个视图都须要实例化,填充和返回一个HttpResponse.
HttpResponse类位于django.http模块中
简单实用
传递字符串
from django.http import HttpResponse response = HttpResponse("Here's the text of the Web page.") response = HttpResponse("Text only, please.", content_type="text/plain")
设置或删除响应头信息
response = HttpResponse() response['Content-Type'] = 'text/html; charset=UTF-8' del response['Content-Type'] # 属性 # HttpResponse.content; 响应内容 # HttpResponse.charset; 响应内容的编码 # HttpResponse.status_code; 响应的状态码
响应对象主要有三种形式
HttpResponse() render() redirect()
HttpResponse
HttpResponse()括号内直接跟一个具体的字符串做为响应体,比较直接很简单,通常使用后面两种形式.
render
结合一个给定的模板和一个指定的上下文字典,并返回一个渲染后的HttpResponse对象
from django.shortcuts import render def my_view(request): # 视图的代码写在这里 return render(request, 'myapp/index.html', {'foo': 'bar'}) # 上面的代码等同于 from django.http import HttpResponse from django.template import loader def my_view(request): # 视图代码写在这里 t = loader.get_template('myapp/index.html') c = {'foo': 'bar'} return HttpResponse(t.render(c, request))
redirect
好比登陆成功以后,跳转到别的页面,这不是你操做的而是后台帮你操做的,为的是用户体验。还有一种状况:web网站每一段时间代码就须要更新,可是有时候更新不了了,就须要从新写一个页面,这样就会自动给你跳转到新的url上,老得网站不会维护了,还有你常常访问的网址因为某种不可描述的缘由有危险了,这是须要咱们重定向新的网址 哈哈
那么为何不给他新的?原来的老用户只是知道你的老网站。固然这只是一些状况,其实redirect具体用法还有不少,他的参数能够是:
# 1. 一个模型:将调用模型的get_absolute_url() 函数 # 2.一个视图,能够带有参数:将使用urlresolvers.reverse 来反向解析名称 # 3.一个绝对的或相对的URL,将原封不动的做为重定向的位置。 # 默认返回一个临时的重定向;传递permanent=True 能够返回一个永久的重定向。
咱们能够用多种方式使用redirect()函数
1.传递一个具体的ORM对象
# 将调用ORM对象的get_absolute_url()方法来获取重定向的URL; from django.shortcuts import redirect def my_view(request): ... object = MyModel.objects.get(...) return redirect(object)
2. 传递一个视图的名称
# views 示例 def my_view(request): ... return redirect('home') def home(request): return HttpResponse('跳转成功!') # urls示例: urlpatterns = [ url(r'home/', views.home, name='home'), # 起别名,这样之后不管url如何变化,访问home直接能够映射到home函数 ]
3.传递要重定向到的一个具体的网址
def my_view(request): ... return redirect('/index')
4. 固然也能够是一个完整的网址
def my_view(request): ... return redirect('http://example.com/')
重定向状态码
# 301和302的区别。 # 301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址能够从响应的Location首部中获取 # (用户看到的效果就是他输入的地址A瞬间变成了另外一个地址B)——这是它们的共同点。 # 他们的不一样在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向以后的网址; # 302表示旧地址A的资源还在(仍然能够访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301 # 重定向缘由: #(1)网站调整(如改变网页目录结构); #(2)网页被移到一个新地址; #(3)网页扩展名改变(如应用须要把.php改为.Html或.shtml)。 # 这种状况下,若是不作重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户获得一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的 # 网站,也须要经过重定向让访问这些域名的用户自动跳转到主站点等。 # 简单来讲就是: # 响应状态码:301,为临时重定向,旧地址的资源仍然能够访问。 # 响应状态码:302,为永久重定向,旧地址的资源已经被永久移除了,这个资源不可访问了。 # 对普通用户来讲是没什么区别的,它主要面向的是搜索引擎的机器人。 # A页面临时重定向到B页面,那搜索引擎收录的就是A页面。 # A页面永久重定向到B页面,那搜索引擎收录的就是B页面。
FBV(function base views) 就是在视图里使用函数处理请求。以前都是FBV模式写的代码,因此就不写例子了。
CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,若是只用函数来开发,有不少面向对象的优势就错失了(继承、封装、多态)。因此Django在后来加入了Class-Based-View。可让咱们用类写View。这样作的优势主要下面两种:
- 提升了代码的复用性,能够使用面向对象的技术,好比Mixin(多继承)
- 能够用不一样的函数针对不一样的HTTP方法处理,而不是经过不少if判断,提升代码可读性
views视图函数
from django.shortcuts import render,HttpResponse from django.views import View # 从django.views模块中引用View类 class Login(View): """ 本身定义get post方法,方法名不能变。这样只要访问此url,get请求自动执行get方法,post请求自动执行post方法,与咱们写的FBV if request.method == 'GET' or 'POST' 同样。 """ def get(self, request): return render(request, 'login.html') def post(self, request): username = request.POST.get('username') password = request.POST.get('password') if username.strip() == 'taibai' and password.strip() == '123': return HttpResponse('登陆成功') return render(request, 'login.html')
url路由
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^login/', views.Login.as_view()), # 引用views函数重的Login类,而后调用父类的as_view()方法 ] # CBV传参,和FBV相似,有名分组,无名分组 # url写法:无名分组的 # url(r'^cv/(\d{2})/', views.Myd.as_view(),name='cv'), # url(r'^cv/(?P<n>\d{2})/', views.Myd.as_view(name='xxx'),name='cv'), # 若是想给类的name属性赋值,前提你的Myd类里面必须有name属性(类属性, # 定义init方法来接受属性行不通,可是能够自行研究一下,看看如何行通,意义不大), # 而且以前类里面的name属性的值会被覆盖掉
templates的html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <h1>你好 欢迎来到plus会所,请先登陆</h1> <form action="" method="post"> <input type="text" name="username"> <p></p> <input type="text" name="password"> <p></p> <input type="submit"> </form> </body> </html>
面试有问过,若是你用CBV模式,你的get或者post方法是从哪里执行的? 可否在get执行以前或者以后作一些特殊的操做?
你的get或者post方法都是在源码的dispatch方法中执行的,咱们能够利用重写父类的dispatch方法,就可以对get和post请求搞事情了。可是若是我想单独对某个请求方法搞事情,那么只能加上装饰器了。
对于FBV这种开发方式,加上装饰器很简单,就是咱们以前讲过的方式,这种比较简单,这里就直接展现view视图函数的代码便可
def wrapper(func): def inner(*args,**kwargs): print('请求来了!') ret = func(*args,**kwargs) print('请求走了!') return ret return inner @wrapper def home(request): print('执行home函数') return render(request, 'home.html')
方法一: 直接加装饰器
由于template,urls很简单,此处只展现views函数
views视图函数
def wrapper(func): def inner(*args,**kwargs): print('请求来了!') ret = func(*args,**kwargs) print('请求走了!') return ret return inner class Login(View): @wrapper def get(self, request): print('get 方法') return render(request, 'login.html') def post(self, request): username = request.POST.get('username') password = request.POST.get('password') if username.strip() == 'taibai' and password.strip() == '123': return HttpResponse('登陆成功') return render(request, 'login.html') ''' 执行流程: 请求来了! 执行home函数 请求走了! '''
方法二: 借助method_decorator模块
from django.utils.decorators import method_decorator def wrapper(func): def inner(*args,**kwargs): print('请求来了!') ret = func(*args,**kwargs) print('请求走了!') return ret return inner class Login(View): @method_decorator(wrapper) def get(self, request): print('get 方法') return render(request, 'login.html') def post(self, request): username = request.POST.get('username') password = request.POST.get('password') if username.strip() == 'taibai' and password.strip() == '123': return HttpResponse('登陆成功') return render(request, 'login.html')
方式三: 给全部方法都加上装饰器
在咱们的源码dispatch方法。咱们能够在子类中重写父类的dispatch方法,由于不管执行什么请求方法(post,get,push,delete等等)都是dispatch方法利用反射调用的。因此,咱们给此方法加上装饰器便可。
class Login(View): @wrapper def dispatch(self, request, *args, **kwargs): ret = super().dispatch(request, *args, **kwargs) return ret
方式四: 直接在类上加装饰器(不经常使用)
注意
# 注意csrf-token装饰器的特殊性,在CBV模式下它只能加在dispatch上面(后面再说) # 下面这是csrf_token的装饰器: # @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即使settings中没有设置csrfToken全局中间件。 # @csrf_exempt,取消当前函数防跨站请求伪造功能,即使settings中设置了全局中间件。 # 注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect
什么是模版系统?这里作一个简单解释。要想明白什么是模版系统,那么咱们得先分清楚静态页面和动态页面。咱们以前学过的都是静态页面,所谓的静态页面就是浏览器向后端发送一个请求,后端接收到这个请求,而后返回给浏览器一个html页面,这个过程不涉及从数据库取出数据渲染到html页面上,只是单纯的返回一个页面(数据所有在html页面上)。而动态页面就是在给浏览器返回html页面以前,须要后端与数据库之间进行数据交互,而后将数据渲染到html页面上在返回给浏览器。言外之意静态页面不涉及数据库,动态页面须要涉及从数据库取出数据。那么模版系统是什么呢?若是你只是单纯的写静态页面,也就没必有必要用模版系统了,只用动态页面才须要模版系统。
简单来讲,模版系统就是在html页面想要展现的数据库或者后端的数据的标签上作上特殊的占位(相似于格式化输出),经过render方法将这些占位的标签里面的数据替换成你想替换的数据,而后再将替换数据以后的html页面返回给浏览器,这个就是模版系统。
模板渲染的官方文档
关于模板渲染你只须要记两种特殊符号(语法):
{{ }}和 {% %}
变量相关的用{{}},逻辑相关的用{%%}。
Example1
url
from django.conf.urls import url,include from app01 import views urlpatterns = [ # 首页 url(r'^$',views.base), url('^app01/',include('app01.urls')), ] # Django_Demo1/app01/urls.py (第二个app应用的urls.py) from django.conf.urls import url from app01 import views urlpatterns = [ url('^login/',views.login), ]
views
render第三个参数接受一个字典的形式,经过字典的键值对index.html页面进行渲染,这也就是模板渲染
from django.shortcuts import render from django.shortcuts import redirect def login(request): name = "幽梦" age = 20 name_list = ['幽梦', 'flying', ] dic = {'幽梦': 18, 'flying': 21} return render(request, 'login1.html', {'name': name, 'age': age, 'name_list': name_list, 'dic_class': dic})
login1.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .c1 { height: 48px; background: #dddddd; text-align: center } </style> </head> <body style="margin: 0;"> <div class="c1">欢迎来到You-Men博客</div> <ul> <li>{{ name }}</li> <li>{{ age }}</li> <li>{{ name_list }}</li> <li>{{ dic_class }}</li> </ul> </body> </html>
在Django的模板语言中按此语法使用: {{ 变量名 }}
当模板引擎遇到一个变量,他将计算这个变量,而后用结果替换掉他自己, 变量的命名包括任何字母数字以及下划线("_")的组合. 变量名称中不能有空格或标点字符.
深度查询点符(.)在模板语言中有特殊的含义,当模板系统遇到点("."),他将以这样的顺序查询:
# 字典查询(Dictionary lookup) # 属性或方法查询(Attribute or method lookup) # 数字索引查询(Numeric index lookup)
经过简单示例咱们已经知道模板系统对于变量的渲染是如何作到的,很是简单,下面将演示一下深刻的渲染,咱们不想将整个列表或字典渲染到html,而是将列表里的元素,或者字典的某个值渲染到html页面中,就能够经过万能的点
views
from django.shortcuts import render from django.shortcuts import redirect def home(request): name = "幽梦" age = 20 name_list = ['幽梦', 'flying', '红玫瑰','紫玫瑰','白玫瑰'] dic = {'幽梦': 18, 'flying': 21,"黄玫瑰":22} return render(request, 'home.html', locals()) def base(request): return render(request, 'base.html')
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .c1 { height: 48px; background: #dddddd; text-align: center } </style> </head> <body style="margin: 0;"> <div class="c1">欢迎来到You-Men博客</div> <ul> <li>{{ name }}</li> <li>{{ age }}</li> <li>{{ name_list.2 }}</li> <li>{{ dic.黄玫瑰 }}</li> </ul> </body> </html>
1. 什么是过滤器?
有的时候咱们经过render渲染到html的数据并非咱们最终想要的数据,好比后端向前端传递的数据为hello,但咱们想要hello显示为HELLO,这个能够后端提早处理的, 诸如此类需求咱们能够经过过滤器来解决.
2. 语法
# 过滤器的语法: {{ value | filter_name:参数 }} # 使用管道符"|"来应用过滤器 # 例如: {{ name|lower }}会将name变量应用lower过滤器以后再显示他的值,lower在这里的做用就是将文本所有都变为小写. # 注意事项: # 1. 过滤器支持"链式"操做,即一个过滤器的输出做为另外一个过滤器的输入. # 2. 过滤器能够接受参数,例如: {{ ss|truncatewords:30 }},这将显示ss的前三十个词 # 3. 过滤器参数包含空格的话,必须用引号包裹起来,好比使用逗号和空格去链接一个一个列表中的元素, 如{{ list|join:',' }} # 4. '|'左右没有空格!没有空格 ! 没有空格 # Django的模板语言中提供了大约六十个内置过滤器
经常使用过滤器
1. default: 若是一个变量是false或者为空,或者给定的默认值,不然,使用变量的值
views: a = '' # 没有变量a或者变量a为空 html: # 显示啥也没有 {{ value|default:"啥也没有"}}
2. length: 返回值的长度,做用于字符串和列表
views: name_list = ['幽梦', 'flying', '红玫瑰','紫玫瑰','白玫瑰'] return render(request, 'home.html', { 'name_list':name_list}) html: {{ name_list|length }} # 显示为5
3. filesizeformat: 将值格式化为一个"人类可读的"文件尺寸(例如'13kb','4.1M','102bytes')
views: value = 1048576 html: {{ value|filesizeformat }} # 显示为1.0MB
4. slice: 切片,支持python中能够用切片的全部数据类型
views: name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥'] s = '太白金星讲师' html: {{ name_list|slice:'1:3' }} # ['天琪', '傻强'] {{ s|slice:'1::2' }} # '白星师'
5. date: 时间格式化
# views time = datetime.datetime.now() return render(request, 'home.html', { 'time':time }) # html <li>{{ time|date:"Y-m-d H:i:s" }}</li>
truncatechars: 若是字符串字符多余指定的字符数量,那么会被截断,截断的字符串将以可翻译的省略号序列("...")结尾
# views: describe = '1999年3月,马云正式辞去公职,后来被称为18罗汉的马云团队回到杭州,凑够50万元人民币' # html: {{ describe|truncatechars:9 }} # 1999年3... # 截断9个字符,三个点也算三个字符 # 咱们浏览网页能够常常看到一个连接下面有一段注释,而后就是...
truncatewords: 在必定数量的字后截断字符串,是截多少个单词
views: words = 'i love you my country china' html: {{ words|truncatewords:3 }} # i love you...
cut: 移除value中全部的与给出变量相同的字符串
views: words = 'i love you my country china' html: {{ words|cut:3 }} # iloveyou
join: 设定链接符将可迭代对象的元素链接在一块儿与字符串的join方法相同
views: name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥'] dic = {'name':'太白','age': 18} tu = (1, 2, 3) html: <li>{{ name_list|join:'_'}}</li> <li>{{ dic|join:','}}</li> <li>{{ tu|join:'+'}}</li> ''' 王阔_天琪_傻强_志晨_健身哥 name,age 1+2+3 '''
safe
Django的模板中在进行模板渲染的时候会对HTML标签和JS等语法标签进行自动转义,缘由显而易见,这样是为了安全,django担忧这是用户添加的数据,好比若是有人给你评论的时候写了一段js代码,这个评论一提交,js代码就执行啦,这样你是否是能够搞一些坏事儿了,写个弹窗的死循环,那浏览器还能用吗,是否是会一直弹窗啊,这叫作xss攻击,因此浏览器不让你这么搞,给你转义了。可是有的时候咱们可能不但愿这些HTML元素被转义,好比咱们作一个内容管理系统,后台添加的文章中是通过修饰的,这些修饰多是经过一个相似于FCKeditor编辑加注了HTML修饰符的文本,若是自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,若是是一个单独的变量咱们能够经过过滤器“|safe”的方式告诉Django这段代码是安全的没必要转义。
咱们去network那个地方看看,浏览器看到的都是渲染以后的结果,经过network的response的那个部分能够看到,这个a标签所有是特殊符号包裹起来的,并非一个标签,这都是django搞得事情。
不少网站,都会对你提交的内容进行过滤,一些敏感词汇、特殊字符、标签、黄赌毒词汇等等,你一提交内容,人家就会检测你提交的内容,若是包含这些词汇,就不让你提交,其实这也是解决xss攻击的根本途径,例如博客园:
timesnce
将日期格式设为自该日期起的时间(例如, "四天,5小时")
采用一个可选参数,他是一个包含用做比较点的日期的变量(不带参数,比较点为如今)。 例如,若是since_12是表示2012年6月28日日期实例,而且comment_date是2018年3月1日日期实例:
views: year_12 = datetime.datetime.now().replace(year=2012, month=6, day=28) year_18 = datetime.datetime.now().replace(year=2018, month=3, day=1) html: <li>{{ year_12|timesince:year_18}}</li> ''' 5 years, 8 months ''' # 若是year_18不写,默认就是距如今的时间段
timeuntil
似于timesince,除了它测量从如今开始直到给定日期或日期时间的时间。 例如,若是今天是2006年6月1日,而conference_date是保留2006年6月29日的日期实例,则{{ conference_date | timeuntil }}将返回“4周”。
使用可选参数,它是一个包含用做比较点的日期(而不是如今)的变量。 若是from_date包含2006年6月22日,则如下内容将返回“1周”:
{{ conference_date|timeuntil:from_date }}
更多内置过滤器(此连接页面最下面的内置过滤器):https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#filters
如今咱们已经能够从后端经过模版系统替换掉前端的数据了,可是若是只是替换掉数据,确实不够灵活,好比,咱们要想在前端页面展现可迭代对象name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']每一个元素,你如和展现呢?
# 前端页面 <ul> <li>{{ name_list.0 }}</li> <li>{{ name_list.1 }}</li> <li>{{ name_list.2 }}</li> <li>{{ name_list.3 }}</li> </ul>
这样写明显很low,咱们要是能够用上for循环就行了。Django这么强大的框架,不可能想不到这点的,这里模版系统给咱们提供了另外一种标签,就是能够在html页面中进行for循环以及if判断等等。
标签看起来像是这样的:
{% tag %}
。标签比变量更加复杂:一些在输出中建立文本,一些经过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。与python语法不一样的是:一些标签须要开始和结束标签 (例如{% tag %} ...
标签 内容 ... {% endtag %})。学习下面几种标签以前,咱们要从新写一个url、views以及html,方便分类学习不与上面的变量产生冲突。
for标签
基本语法
{% for 变量 in render的可迭代对象 %} {{ 变量 }} {% endfor %} # 例如: {% for foo in name_list %} {{ foo }} {% endfor %}
view
import datetime from django.shortcuts import render from django.shortcuts import redirect def home(request): name = "幽梦" age = 20 size = 2342314 time = datetime.datetime.now() name_list = ['幽梦', 'flying', '红玫瑰','紫玫瑰','白玫瑰'] dic = {'幽梦': 18, 'flying': 21,"黄玫瑰":22} return render(request, 'home.html', { 'name_list':name_list })
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .c1 { height: 48px; background: #dddddd; text-align: center } </style> </head> <body style="margin: 0;"> <div class="c1">欢迎来到You-Men博客</div> <li>{{ name }}</li> <li>{{ age }}</li> <ul> {% for i in name_list %} <li>{{ i }}</li> {% endfor %} </ul> <li>{{ dic }}</li> </body> </html>
遍历一个列表
<ul> {% for foo in name_list %} <li>{{ foo }}</li> {% endfor %} </ul>
反向遍历一个列表
{% for foo in name_list reversed %} <li>{{ foo }}</li> {% endfor %}
遍历一个字典: 有items,keys,values参数
<ul> {% for key,value in dic.items %} <li>{{ key }}: {{ value }}</li> {% endfor %} {% for key in dic.keys %} <li>{{ key }}</li> {% endfor %} {% for value in dic %} <li>{{ value }}</li> {% endfor %} </ul>
forloop
模版系统给咱们的for标签还提供了forloop的功能,这个就是获取循环的次数,有多种用法:
forloop.counter # 当前循环的索引值(从1开始),forloop是循环器,经过点来使用功能 forloop.counter0 # 当前循环的索引值(从0开始) forloop.revcounter # 当前循环的倒序索引值(从1开始) forloop.revcounter0 # 当前循环的倒序索引值(从0开始) forloop.first # 当前循环是否是第一次循环(布尔值) forloop.last # 当前循环是否是最后一次循环(布尔值) forloop.parentloop # 本层循环的外层循环的对象,再经过上面的几个属性来显示外层循环的计数等 <ul> {% for foo in name_list %} <li>{{ forloop.counter }} {{ foo }}</li> {% endfor %} {% for foo in name_list %} {{ forloop.counter0 }} <li>{{ foo }}</li> {% endfor %} {% for foo in name_list %} <li>{{ forloop.revcounter }} {{ foo }}</li> {% endfor %} {% for foo in name_list %} <li>{{ forloop.first }} {{ foo }}</li> {% endfor %} </ul>
for..empty..组合
若是遍历的可迭代对象是空的或者就没有这个对象,利用这个组合能够提示用户
<ul> {% for foo in aaa %} <li>{{ foo }}</li> {% empty %} <li>查询的内容啥也没有</li> {% endfor %} {% for foo in value %} <li>{{ foo }}</li> {% empty %} <li>查询的内容啥也没有</li> {% endfor %} </ul>
IF标签
基本语法
{% ``if %}
会对一个变量求值,若是它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。
{% if 条件 %} 结果 <!--不知足条件,不会生成这个标签--> {% elif 条件 %} 结果 {% else %} <!--也是在if标签结构里面的--> 结果 {% endif %}
elif和else必定要在if endif里面,设置多个elif或者没有elif,有没有else均可以
{% if dic.age > 18 %} <p>能够干点儿该作的事儿了~</p> {% elif dic.age < 18 %} <p>小孩子,懂什么</p> {% else %} <p> 风华正茂的年龄~</p> {% endif %}
条件也能够与过滤功能配合
% if name_list|length > 4 %} <p>列表元素超过4个</p> {% else %} <p>列表元素太少!</p> {% endif %}
条件也能够加逻辑运算符
if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。
with
使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名,当你须要一个
昂贵
的方法(好比访问数据库)不少次的时候是很是有用的,记住 ! 等号的左右不要加空格! !
{% with total=business.employees.count %} {{ total }} <!--只能在with语句体内用--> {% endwith %} {% with business.employees.count as total %} {{ total }} {% endwith %}
forloop.first, forloop.last,forloop.parentloop
forloop.first
{% for foo in name_list %} {% if forloop.first %} {{ foo }} {% else %} <p>只有第一次循环打印</p> {% endif %} {% endfor %}
forloop.parenloop: 必定注意! 他是返回本此循环的外层循环对象,这个对象能够调用forloop的各类方法进行获取相应的数据
测试此方法,咱们要在views函数加一个数据类型: lis = [['A', 'B'], ['C', 'D'], ['E', 'F']]
{% for i in lis %} {% for j in i %} {# <p>{{ forloop.parentloop }}</p>#} <p>{{ forloop.parentloop.counter }} {{ forloop.counter }} {{ j }}</p> {% endfor %} {% endfor %}
注意事项
# 1. Django的模板语言不支持连续判断,即不支持如下写法: {% if a > b > c %} ... {% endif %} # 2. Django的模板语言中属性的优先级大于方法 def xx(request): d = {"a": 1, "b": 2, "c": 3, "items": "100"} return render(request, "xx.html", {"data": d}) # 如上,咱们在使用render方法渲染一个页面的时候,传的字典d有一个key是items而且还有默认的 d.items() 方法,此时在模板语言中: {{ data.items }} # 默认会取d的item key的值
这个标签是否是很是熟悉?以前咱们以post方式提交表单的时候,会报错,还记得咱们在settings里面的中间件配置里面把一个csrf的防护机制给注销了啊,自己不该该注销的,而是应该学会怎么使用它,而且不让本身的操做被forbiden,经过这个标签就能搞定。
当咱们加上此标签以后,再次发出get请求,render返回给咱们的页面中多了一个隐藏的input标签,而且这个标签里面有个一键值对:
键:name,值:随机的一堆密文。 那么这个是干什么用的呢?其实他的流程是这样的:
第一次发送get请求,在views视图函数返回给你login.html页面以前,render会将csrf_token标签替换成一个隐藏的input标签,此标签的键值对就是上面那个键值对而且Django将这个键值对保存在内存;当你再次进行post请求时,他会验证你的form表单里面的隐藏的input标签的键值对是否与我内存中存储的键值对相同,若是相同,你是合法的提交,容许经过;若是不相同,则直接返回给你forbidden页面。这就比如说,第一次get请求,他返回你一个盖戳的文件,当你在进行post请求时他会验证是否是那个盖戳的文件。他的目的就是你提交post请求时,必须是从我给你的get请求返回的页面提交的。为何这么作呢?是由于有人登陆你的页面时是能够绕过的get请求返回的页面直接进行post请求登陆的,好比说爬虫。直接经过requests.post('/login/')直接请求的,这样是没有csrftoken的,这样就直接拒绝了。
说了这么多,目的就是一个:验证当你post提交请求时,是否是从我给你(你经过get请求的)页面上提交的数据。
咱们能够写一个简单爬虫验证如下
import requests ret = requests.post('http://127.0.0.1:8000/login/', data={'username': 'taibai', 'password': '123'} ) print(ret.content) # 若是你保留这这个验证,则经过爬虫是登陆不成功的,只能返回你一个forbidden的html页面。 # 若是你将settings那个中间件注释掉,那么就能够成功访问了:
模版就是django提供的用于html发送浏览器以前,须要在某些标签进行替换的系统,而继承咱们立马就会想到这是面向对象的三大特性之一。其实模版继承就是拥有这两个特性的模版的高级用法。
通常管理系统都是这样的布局,这个就是固定的导航条和侧边栏。不管我点击侧边栏里的那个按钮,这两部分不会更换只会改变中间的内容。
那么接下来咱们实现一个这样的布局。
咱们要准备4个html页面:base.html、menu1.html、menu2.html、menu3.html,这四个页面的导航条与左侧侧边栏同样,每一个页面对应一个url。而且每一个页面的左侧侧边栏菜单1、菜单2、菜单三能够实现跳转:跳转到menu1.html、menu2.html、menu3.html三个页面。而顶端导航条只是样式便可。接下来借助于Django,咱们实现这四个页面并对应urls能够跑通流程。
urls
urlpatterns = [ url(r'^base/', views.base), url(r'^menu1/', views.menu1), url(r'^menu2/', views.menu2), url(r'^menu3/', views.menu3), ]
views
def base(request): return render(request, 'base.html') def menu1(request): return render(request, 'menu1.html') def menu2(request): return render(request, 'menu2.html') def menu3(request): return render(request, 'menu3.html')
html
base.html: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } .sidebar{ background-color: #b2b2b2; color: #435dff; width: 20%; height: 1000px; float: left; } .sidebar ul{ margin: 0; } .menu{ width: 80%; float: right; } </style> </head> <body> <div class="nav clearfix"> <a href="">普通洗浴</a> <a href="">盆儿堂</a> <a href="">局部护理</a> <a href="">关于咱们</a> <a href="">预定电话</a> <input type="text">搜索 </div> <div class='sidebar'> <ul> <li><a href="/menu1/">菜单一</a></li> <li><a href="/menu2/">菜单二</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> 首页 </div> </body> </html> menu1.html: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } .sidebar{ background-color: #b2b2b2; color: #435dff; width: 20%; height: 1000px; float: left; } .sidebar ul{ margin: 0; } .menu{ width: 80%; float: right; } </style> </head> <body> <div class="nav clearfix"> <a href="">普通洗浴</a> <a href="">盆儿堂</a> <a href="">局部护理</a> <a href="">关于咱们</a> <a href="">预定电话</a> <input type="text">搜索 </div> <div class='sidebar'> <ul> <li><a href="/menu1/">菜单一</a></li> <li><a href="/menu2/">菜单二</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> 菜单一首页 </div> </body> </html> menu2.html: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } .sidebar{ background-color: #b2b2b2; color: #435dff; width: 20%; height: 1000px; float: left; } .sidebar ul{ margin: 0; } .menu{ width: 80%; float: right; } </style> </head> <body> <div class="nav clearfix"> <a href="">普通洗浴</a> <a href="">盆儿堂</a> <a href="">局部护理</a> <a href="">关于咱们</a> <a href="">预定电话</a> <input type="text">搜索 </div> <div class='sidebar'> <ul> <li><a href="/menu1/">菜单一</a></li> <li><a href="/menu2/">菜单二</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> 菜单2首页 </div> </body> </html> menu3.html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } .sidebar{ background-color: #b2b2b2; color: #435dff; width: 20%; height: 1000px; float: left; } .sidebar ul{ margin: 0; } .menu{ width: 80%; float: right; } </style> </head> <body> <div class="nav clearfix"> <a href="">普通洗浴</a> <a href="">盆儿堂</a> <a href="">局部护理</a> <a href="">关于咱们</a> <a href="">预定电话</a> <input type="text">搜索 </div> <div class='sidebar'> <ul> <li><a href="/menu1/">菜单一</a></li> <li><a href="/menu2/">菜单二</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> 菜单三首页 </div> </body> </html> base menu1 menu2 menu3
上面个人的需求虽然完成了可是有没有什么没问题?你会发现html重复代码太多了,若是领导不瞎,最晚后天你就能够领盒饭了。
2. 母版继承示例
因此针对与我上面的需求,很显然你如今所拥有的知识点已经解决不了了。那么接下来就是本节的重点:模版继承。根据这个知识点名字的特色,咱们应该想到,咱们可不能够创建一个父类,而后让全部的子孙类都继承个人父类,这样我就能够节省不少代码了,让个人代码很是的清新、简单。这里的父类就不叫父类了他有一个专有名词:母版。
接下来咱们先建立一个母版。(最好不要将base页面直接做为母版,母版就是只设置公用的部分,其余一律不要。)
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } .sidebar{ background-color: #b2b2b2; color: #435dff; width: 20%; height: 1000px; float: left; } .sidebar ul{ margin: 0; } .menu{ width: 80%; float: right; } </style> </head> <body> <div class="nav clearfix"> <a href="">普通洗浴</a> <a href="">盆儿堂</a> <a href="">局部护理</a> <a href="">关于咱们</a> <a href="">预定电话</a> <input type="text">搜索 </div> <div class='sidebar'> <ul> <li><a href="/menu1/">菜单一</a></li> <li><a href="/menu2/">菜单二</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> </div> </body> </html>
接下来,咱们将base menu1 menu2 menu3这四个页面所有清空,而后在每一个页面的最上面加上这么一行代码:接下来,咱们将base menu1 menu2 menu3这四个页面所有清空,而后在每一个页面的最上面加上这么一行代码:
{% extends 'master_edition.html' %}
这个就实现了我要继承母版master_edition.html。
3. 自定制效果
如今已经完成了继承母版,这个只是减小了重复代码,尚未实现每一个页面自定制的一些内容,若是你想要实现自定制的内容怎么作?相似于模版系统你是否是应该在母版的具体位置作一个标识,而后在本身的页面对应的地方进行自定制?那么这个相似于%占位符的特定的标识叫作钩子。这几个页面只是在menu div不一样,因此咱们就在这里作一个钩子就好了。
在母版的html对应位置:
<div class="menu"> {% block content %} {% endblock %} </div>
block endblock就是对应的钩子,content是此钩子的名字。
而后在base menu1 menu2 menu3的页面上(此时就以base页面举例):
{% block content %} base页面首页 {% endblock %}
这样你的代码是否是很是的简单了?
那么咱们不只能够在对应的html标签设置钩子,还能够在css、js设定对应的钩子。因此母版继承中通常设定钩子的地方就是三部分: html、css、js。
以css举例:
咱们将base页面的顶端导航条的背景颜色设置成红色:
首先如今母版页面对应的位置设置钩子:
<style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } .sidebar{ background-color: #b2b2b2; color: #435dff; width: 20%; height: 1000px; float: left; } .sidebar ul{ margin: 0; } .menu{ width: 80%; float: right; } {% block nav %} {% endblock %} </style>
而后找到base页面:
{% block nav %} .nav{ background-color: red; } {% endblock %}
这样你的base页面的导航条就变成红色啦!
4. 保留母版内容并添加新特性
还有一个状况咱们也会遇到,就是我既要留住母版的内容,又要在本身的html添加一些新的标签。这个咱们在面向对象时是否是也遇到过?当时用什么方法既执行父类方法又能够执行子类方法?super!在这里咱们也用super!
母版html:
<div class="menu"> {% block content %} <div>这是母版测试页面</div> {% endblock %} </div>
base.html
{% block content %} {{ block.super }} base页面首页 {% endblock %}
在钩子里面加上{{ block.super }}便可
注意
{% extends %}
标签,它必须是模版中的第一个标签。其余的任何状况下,模版继承都将没法工做,模板渲染的时候django都不知道你在干啥。{% block %}
标签越好。请记住,子模版没必要定义所有父模版中的blocks,因此,你能够在大多数blocks中填充合理的默认内容,而后,只定义你须要的那一个。多一点钩子总比少一点好。{% block %}
中。{{ block.super }}
variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }}
will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template. 将子页面的内容和继承的母版中block里面的内容同时保留。block
标签。组件就是将一组经常使用的功能封装起来,保存在单独的html文件中,(如导航条,页尾信息等)其余页面须要此组功能时,按以下语法导入便可。 这个与继承比较相似,可是‘格局’不一样,继承是须要写一个大的母版,凡是继承母版的一些html基本上用的都是母版页面的布局,只有一部分是本身页面单独展示的,这比如多以及排布好的多个组件。
{% include 'xx.html' %}
好比咱们写一个nav导航条组件
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ background-color: black; color: #eaeaea; height: 30px; width: 100%; } .clearfix{ content: ''; display: block; clear: both; } </style> </head> <body> <div class="nav clearfix"> <a href="">普通洗浴</a> <a href="">盆儿堂</a> <a href="">局部护理</a> <a href="">关于咱们</a> <a href="">预定电话</a> <input type="text">搜索 </div> </body> </html>
而后咱们在建立流程去使用咱们的组件
urls: url(r'^component/', views.component), views: def component(request): return render(request,'component.html') component页面: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Bootstrap 101 Template</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> {% include 'nav.html' %} <h1>你好,世界!</h1> <div>我是componet页面</div> </body> </html>
组件和插件的区别
# 组件是提供某一完整功能的模块,如: 编辑器组件,QQ空间提供的关注组件等 # 而插件更倾向封闭某一个功能方法的函数 # 这二者的区别在JavaScript里区别很小,组件这个名词用的很少,通常统称插件
DBA(数据库管理员)+应用层开发。
通常中大型公司(或者数据量巨大、读取数据的需求频繁而且追求极致效率的公司)会有专门的DBA管理数据库,编写sql语句,对于应用层开发来讲,不用写sql语句,直接调用他写的接口就行。因此在这种公司通常来讲,开发人员应该'供'着DBA,由于你想写入或者取出的数据须要依赖于DBA去执行,或者是你写的比较复杂的sql语句须要让DBA帮你看一下,效率行不行、是否是须要优化等等,这就须要看大家的交情或者其心情了。哈哈(开个玩笑)。
应用程序开发+sql语句编写。
这种状况多存在于小公司,没有专门设置DBA岗位,要求开发人员什么都会一些,linux、数据库、前端等等,这样成本下降而且减小因为部门之间的沟通带来的损失,提升工做流程效率。
应用程序开发+ORM。
这种模式sql不用你写,你直接写类、对象,应为你对这些更加游刃有余。而后经过你写的类、对象,等等经过相应的转换关系直接转化成对应的原生sql语句,这种转化关系就是ORM:对象-关系-映射。你直接写一个类就是建立一张表,你实例化一个对象就是增长一条数据,这样以来,既能够避开写sql语句的麻烦,并且能够提高咱们的开发效率。
MTV或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不须要依赖于特定的数据库,经过简单的配置就能够轻松更换数据库,这极大的减轻了开发人员的工做量,不须要面对因数据库变动而致使的无效劳动.
ORM是“对象-关系-映射”的简称。(Object Relational Mapping,简称ORM)(未来会学一个sqlalchemy,是和他很像的,可是django的orm没有独立出来让别人去使用,虽然功能比sqlalchemy更强大,可是别人用不了)
类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎,明白orm是什么了,剩下的就是怎么使用orm,怎么来写类对象关系语句。
这样开发效率确定是提高了,可是也有一点点缺陷就是经过ORM转化成的sql语句虽然是准确的,可是不必定是最优的。
#sql中的表 #建立表: CREATE TABLE employee( id INT PRIMARY KEY auto_increment , name VARCHAR (20), gender BIT default 1, birthday DATA , department VARCHAR (20), salary DECIMAL (8,2) unsigned, ); #sql中的表纪录 #添加一条表纪录: INSERT employee (name,gender,birthday,salary,department) VALUES ("alex",1,"1985-12-12",8000,"保洁部"); #查询一条表纪录: SELECT * FROM\\ employee WHERE age=24; #更新一条表纪录: UPDATE employee SET birthday="1989-10-24" WHERE id=1; #删除一条表纪录: DELETE FROM employee WHERE name="alex" #python的类 class Employee(models.Model): id=models.AutoField(primary_key=True) name=models.CharField(max_length=32) gender=models.BooleanField() birthday=models.DateField() department=models.CharField(max_length=32) salary=models.DecimalField(max_digits=8,decimal_places=2) #python的类对象 #添加一条表纪录: emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保洁部") emp.save() #查询一条表纪录: Employee.objects.filter(age=24) #更新一条表纪录: Employee.objects.filter(id=1).update(birthday="1989-10-24") #删除一条表纪录: Employee.objects.filter(name="alex").delete()
1 . 建立一个django项目
2 . 经过类建立数据表
从django.db 引入models模块,建立表经过构建一个类去设定,数据库中不区分大小写,因此你的UserInfo在数据库中直接编译成了userinfo,此类必须继承models.Model类,经过设定类的静态属性就会转化成sql语句。
注意装mysql须要远程受权
grant all privileges on *.* to admin@"%" identified by 'ZHOUjian.21' with grant option; flush privileges;
3 . 修改settings.py配置文件
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'cmdb', 'USER': 'admin', 'PASSWORD': 'ZHOUjian.20', 'HOST': '121.36.43.223', 'PORT': '3306', } } # 使用pymysql注意到__init.py加入如下两行代码 import pymysql pymysql.install_as_MySQLdb()
注意
注意:NAME即数据库的名字,在mysql链接前该数据库必须已经建立,而上面的sqlite数据库下的db.sqlite3则是项目自动建立 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动咱们的Django项目前,咱们须要激活咱们的mysql。接下来,咱们要提早先给mysql建立cmdb的数据库
4 . 经过类建立数据表
从django.db 引入models模块,建立表经过构建一个类去设定,数据库中不区分大小写,因此你的UserInfo在数据库中直接编译成了userinfo,此类必须继承models.Model类,经过设定类的静态属性就会转化成sql语句。
from django.db import models # Create your models here. class UserInfo(models.Model): """ 下面几个类的属性经过ORM映射就对应成了: create table userinfo( id int primary key auto_increment, name varchar(16), age int, current_date date) """ id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) age = models.IntegerField() current_date = models.DateField()
5 . 在对应的数据库中生成表结构
上面咱们已经经过类构建了一个表,可是尚未对应的生成真实的数据库中的表结构,因此咱们要将上面的类生成真生的数据库中的表结构。对应只有行代码。
在terminal输入指令:
python manage.py makemigrations
接下来咱们会发现migrations出现一个0001_initial.py的文件,这个文件是执行上述命令以后产生的脚本文件,这个文件就是一个记录
这个指令其实就是执行第一个指令生成的记录也就是那个脚本文件,而后就会在你对应的数据库中生成一个真正的表,生成的表名字前面会自带应用的名字,例如:你的userinfo表在数据表里面叫作:cmdb_userinfo。
同步执行指令的原理
在执行 python manager.py makemigrations时 Django 会在相应的 app 的migrations文件夹下面生成 一个python脚本文件 在执行 python manager.py migrate 时 Django才会生成数据库表,那么Django是如何生成数据库表的呢? Django是根据 migrations下面的脚本文件来生成数据表的 每一个migrations文件夹下面有多个脚本,那么django是如何知道该执行那个文件的呢,django有一张django-migrations表,表中记录了已经执行的脚本,那么表中没有的就是还没执行的脚本,则 执行migrate的时候就只执行表中没有记录的那些脚本。 有时在执行 migrate 的时候若是发现没有生成相应的表,能够看看在 django-migrations表中看看 脚本是否已经执行了, 能够删除 django-migrations 表中的记录 和 数据库中相应的 表 , 而后从新 执行
Django的ORM系统体如今框架内就是模型层。想要理解模型层的概念,关键在于理解用Python代码的方式来定义数据库表的作法!一个Python的类,就是一个模型,表明数据库中的一张数据表!Django奉行Python优先的原则,一切基于Python代码的交流,彻底封装SQL内部细节。
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 = ( 'DIRS': [os.path.join(BASE_DIR,'templates')], )
4.静态文件
STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static') )
5. 修改数据库引擎
settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'cmdb', ]
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): """ 下面几个类的属性经过ORM映射就对应成了: create table userinfo( id int primary key auto_increment, name varchar(16), age int, current_date date) """ id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) age = models.IntegerField() current_date = models.DateField() 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
。若是你之前写过模型,可能已经作过这一步工做,可跳
<1> CharField 字符串字段, 用于较短的字符串. CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所容许的最大字符数. <2> IntegerField #用于保存一个整数. <3> DecimalField 一个浮点数. 必须 提供两个参数: 参数 描述 max_digits 总位数(不包括小数点和符号) decimal_places 小数位数 举例来讲, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: models.DecimalField(..., max_digits=5, decimal_places=2) 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: models.DecimalField(..., max_digits=17, decimal_places=10) #max_digits大于等于17就能存储百万以上的数了 admin 用一个文本框(<input type="text">)表示该字段保存的数据. <4> AutoField 一个 IntegerField, 添加记录时它会自动增加. 你一般不须要直接使用这个字段; 自定义一个主键:my_id=models.AutoField(primary_key=True) 若是你不指定主键的话,系统会自动添加一个主键字段到你的 model. <5> BooleanField A true/false field. admin 用 checkbox 来表示此类字段. <6> TextField 一个容量很大的文本字段. admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). <7> EmailField 一个带有检查Email合法性的 CharField,不接受 maxlength 参数. <8> DateField 一个日期字段. 共有下列额外的可选参数: Argument 描述 auto_now 当对象被保存时(更新或者添加都行),自动将该字段的值设置为当前时间.一般用于表示 "last-modified" 时间戳. auto_now_add 当对象首次被建立时,自动将该字段的值设置为当前时间.一般用于表示对象建立时间. (仅仅在admin中有意义...) <9> DateTimeField 一个日期时间字段. 相似 DateField 支持一样的附加选项. <10> ImageField 相似 FileField, 不过要校验上传对象是不是一个合法图片.#它有两个可选参数:height_field和width_field, 若是提供这两个参数,则图片将按提供的高度和宽度规格保存. <11> FileField 一个文件上传字段. 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, 该格式将被上载文件的 date/time 替换(so that uploaded files don't fill up the given directory). admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) . 注意:在一个 model 中使用 FileField 或 ImageField 须要如下步骤: (1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 做为该目录的公共 URL. 要确保该目录对 WEB服务器用户账号是可写的. (2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪一个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 出于习惯你必定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来讲,若是你的 ImageField 叫做 mug_shot, 你就能够在模板中以 {{ object.#get_mug_shot_url }} 这样的方式获得图像的绝对路径. <12> URLField 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且 没有返回404响应). admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) <13> NullBooleanField 相似 BooleanField, 不过容许 NULL 做为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项 admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. <14> SlugField "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们一般用于URLs 若你使用 Django 开发版本,你能够指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. #在 之前的 Django 版本,没有任何办法改变50 这个长度. 这暗示了 db_index=True. 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate the slug, via JavaScript,in the object's admin form: models.SlugField (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields. <13> XMLField 一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径. <14> FilePathField 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. 参数 描述 path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此获得可选项目. Example: "/home/images". match 可选参数. 一个正则表达式, 做为一个字符串, FilePathField 将使用它过滤文件名. 注意这个正则表达式只会应用到 base filename 而不是 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的所有子目录. 这三个参数能够同时使用. match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: FilePathField(path="/home/images", match="foo.*", recursive=True) ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif <15> IPAddressField 一个字符串形式的 IP 地址, (i.e. "24.124.1.30"). <16> CommaSeparatedIntegerField 用于存放逗号分隔的整数值. 相似 CharField, 必需要有maxlength参数.
(1)null 若是为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False. (1)blank 若是为True,该字段容许不填。默认为False。 要注意,这与 null 不一样。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。 若是一个字段的blank=True,表单的验证将容许该字段是空值。若是字段的blank=False,该字段就是必填的。 (2)default 字段的默认值。能够是一个值或者可调用对象。若是可调用 ,每有新对象被建立它都会被调用,若是你的字段没有设置能够为空,那么未来若是咱们后添加一个字段,这个字段就要给一个default值 (3)primary_key 若是为True,那么这个字段就是模型的主键。若是你没有指定任何一个字段的primary_key=True, Django 就会自动添加一个IntegerField字段作为主键,因此除非你想覆盖默认的主键行为, 不然不必设置任何一个字段的primary_key=True。 (4)unique 若是该值设置为 True, 这个数据字段的值在整张表中必须是惟一的 (5)choices 由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 若是设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>并且这个选择框的选项就是choices 中的选项。 (6)db_index 若是db_index=True 则表明着为此字段设置数据库索引。 DatetimeField、DateField、TimeField这个三个时间字段,均可以设置以下属性。 (7)auto_now_add 配置auto_now_add=True,建立数据记录的时候会把当前时间添加到数据库。 (8)auto_now 配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
'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)',
1.建立一个应用app01
python manage.py startapp app01 # 记得在settings.py注册app01
2.先到Django_ORM_Demo1/urls.py作一个分发路由
from django.conf.urls import url,include urlpatterns = [ url(r'^cmdb/', include("app01.urls")), ]
3.到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), ]
4.经过orm建立module表
from django.db import models from django.db import models class UserInfo(models.Model): name = models.CharField(max_length=16) password = models.CharField(max_length=20) python manage.py makemigrations python manage.py migrate
5.写app01/view.py逻辑文件
from django.shortcuts import render,HttpResponse,redirect from cmdb import models 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(name='root',password='123') # 建立2 # obj = models.UserInfo(name='tom',password='456') # 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')
6./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等工具的使用。
在python中orm的对应关系有三种:
类 ----------> 表
类的对象 ----------> 行(记录)
类的属性 ----------> 表的字段(重点)
咱们要想操做一行的数据,就应该将相应的类引入,而后经过实例化对象增长数据。接下来,咱们从新建立一个数据库为orm01,建立一个student表结构,从这个表中去研究单表的操做
增: 添加表记录
# 咱们先去module.py定义表结构 class Student(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) age = models.IntegerField() # 生成记录并建立表结构 python manage.py makemigrations python manage.py migrate # 方式一 # 在index函数中操做: def index(request): # 实例化一个对象这就是一条记录 student_obj = models.Student( name='王阔', age=18 ) # 将此记录增长到数据表中 student_obj.save() return render(request,'index.html') # 方式二 # 咱们经过方式1给student表中添加了一条表记录,可是上面那种方法不灵活,接下来咱们介绍一种更加灵活的方法。 models.Student.objects.create( name='ZHOU', age=20 ) new_obj = models.Student.objects.create( name='ZHOU', age=19 ) print(new_obj) # Student object print(new_obj.name) # 志远 # 批量建立 # 好比依次插入三十条数据 obj_list = [models.Student(name=f'幽梦{i}', age=20) for i in range(1, 31)] models.Student.objects.bulk_create(obj_list) # 更新和增长 # orm还有一种方法是对一条表记录进行更新或者增长的操做,有则更新无则增长,相似于字典的这个操做dic['name']='barry' models.Student.objects.update_or_create( name='乔妹儿1', # 筛选条件 defaults={ # 须要更新或者增长的值 'age': 1000, } ) # 简单的查询 # 1. all()所有取出 # 经过all从orm取出来的是一个QuerySet类型,这里面有不少个Student类的对象也就是model对象,这个QuerySet相似于列表,可是不一样与列表,在这里咱们知道能够循环遍历取值便可. all_objs = models.Student.objects.all() print(all_objs) # QuerySet类型 这里面有不少个Student类的对象也就是model对象。 # 经过遍历能够获取每一个对象的name属性 for i in all_objs: print(i.name) # filter(条件)查询 # 经过条件查询获取结果,返回的也是QuerySet类型,若是查询不到内容不会报错,返回一个空的QuerySet集合。 objs = models.Student.objects.filter(age=20) print(objs) objs = models.Student.objects.filter(id=2) print(objs) # <QuerySet [<Student: 傻强>]> print(objs[0].id) # 2 能够经过索引取值 objs = models.Student.objects.filter(name='太白') print(objs) # get(条件)查询 # 这个get比较特殊,它返回的是model对象,经过get条件查询,查询的结果有且只有1个。 # 若是超过一个则报错为:get() returned more than one Student -- it returned 2(有几个显示几个)! # 若是没有则报错为:Student matching query does not exist. # obj = models.Student.objects.get(name='太白') # 报错:Student matching query does not exist. # obj = models.Student.objects.get(age=20) # get() returned more than one Student -- it returned 20! obj = models.Student.objects.get(id=1) print(obj) # model对象 # 通常使用get条件查询,前提一要肯定你的条件锁定的是一行记录. # exclude排除 # 经过object对象或者QuerySet集合调用,返回QuserySet集合。 # 下面咱们演示了上面说到的链式点操做,能够一直进行点的操做,由于此方法能够QuerySet集合调用,而且返回的仍是QuerySet集合。 objs = models.Student.objects.exclude(id=1) print(objs) # 排除id为1的行记录,将剩下全部的返回 objs = models.Student.objects.filter(age=20).exclude(name='齐佳乐') print(objs) # <QuerySet [<Student: 乔妹儿1>, <Student: 乔妹儿1>, <Student: 健身哥>, <Student: 张雨薇>]> objs = models.Student.objects.filter(age=20).exclude(name='齐佳乐').exclude(name='健身哥') print(objs) # <QuerySet [<Student: 乔妹儿1>, <Student: 乔妹儿1>, <Student: 张雨薇>]> # order_by排序 # 经过object对象或者QuerySet集合调用,返回QuserySet集合 # object对象调用 objs = models.Student.objects.order_by('age') # 经过姓名升序排列 print(objs) # queryset集合调用 objs = models.Student.objects.all().order_by('age') # 经过姓名升序排列 print(objs) # 经过年龄升序排列,相同年龄的按照id降序排列 objs = models.Student.objects.all().order_by('age', '-id') print(objs) # reverse反转 # 经过order_by返回的QuerySet集合调用,返回一个QuerySet集合。 objs = models.Student.objects.reverse() print(objs) # 这样没有做用,all filter等都没有做用 objs = models.Student.objects.order_by('id').reverse() # 只能经过order_by返回的QuerySet集合调用 print(objs) # count计数 # 经过QuerySet集合调用,返回一个元素个数 num = models.Student.objects.all().count() print(num) # 12 num = models.Student.objects.filter(age=20).count() print(num) # 5 # first返回第一个model对象 # 经过QuerySet集合调用,返回第一个model对象 obj = models.Student.objects.filter(age=20).first() print(obj) # last返回最后一个model对象 # 经过QuerySet集合调用,返回最后一个model对象 obj = models.Student.objects.filter(age=20).last() print(obj) # exists判断是否存在 # 经过QuerySet集合调用,返回bool值 flag = models.Student.objects.filter(age=25).exists() print(flag) # False # 这里咱们要说一句,他的效率是高的,尤为是做为if判断的条件。 # 虽说你能够经过下面的方式判断,可是若是这个集合一下取出上百万条数据,那么你的效率就很低的, # 而exists翻译成sql语句是limit=1,也就是说它只看第一个对象存不存在就返回bool值了。 if Queryset集合: pass # 咱们也能够经过配置settings,每次执行orm语句时都将相应的sql语句展现出来,这样能够查看一下exists(). # settings配置: LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } } # values_list # 经过QuerySet集合调用,返回一个QuerySet集合 # 可是这个QuerySet集合比较特殊,这个里面的元素不是model对象,而是元组的形式 query_tuple = models.Student.objects.filter(age=20).values_list() print(query_tuple) # <QuerySet [(4, '乔妹儿1', 20), (24, '乔妹儿1', 20), (27, '健身哥', 20), (28, '张雨薇', 20), (32, '齐佳乐', 20)]> query_tuple = models.Student.objects.filter(age=20).values_list().exclude(name='齐佳乐') print(query_tuple) # < QuerySet[(4, '乔妹儿1', 20), (24, '乔妹儿1', 20), (27, '健身哥', 20), (28, '张雨薇', 20)] > # 还能够指定想获取的字段 query_tuple = models.Student.objects.filter(age=20).values_list('name','age') print(query_tuple) # <QuerySet [('乔妹儿1', 20), ('乔妹儿1', 20), ('健身哥', 20), ('张雨薇', 20), ('齐佳乐', 20)]> # values # 经过QuerySet集合调用,返回一个QuerySet集合 # 可是这个QuerySet集合比较特殊,这个里面的元素不是model对象,而是字典的形式 query_dict = models.Student.objects.filter(age=19).values() print(query_dict) # <QuerySet [{'id': 3, 'name': '志远', 'age': 19}, {'id': 25, 'name': '董伟华', 'age': 19}]> query_dict = models.Student.objects.filter(age=19).values('name', 'age') print(query_dict) # <QuerySet [{'name': '志远', 'age': 19}, {'name': '董伟华', 'age': 19}]> # distinct去重 # 经过QuerySet集合调用,返回一个QuerySet集合 # 这里要注意一点:不管是all仍是filter 你对整个对象去重是没有意义的,只要有一个字段不一样,都不是重复的。 query_objs = models.Student.objects.filter(age=20).distinct() print(query_objs) # <QuerySet [<Student: id:4 name:乔妹儿1 age:20>, <Student: id:24 name:乔妹儿1 age:20>, <Student: id:27 name:健身哥 age:20>, <Student: id:28 name:张雨薇 age:20>, <Student: id:32 name:齐佳乐 age:20>]> # 因此这个去重通常都用于values或者values_list query_objs = models.Student.objects.filter(age=20).values('age').distinct() print(query_objs) # <QuerySet [{'age': 20}]> # 基于双下划线的模糊查询 # 咱们在使用filter方法时,一直在使用 = 条件,可是没有使用过> < >=等条件,这是由于ORM不支持这种写法,不用着急,咱们能够根据另外一种写法去实现。 query_objs = models.Student.objects.filter(age__gt=19) # 大于 query_objs = models.Student.objects.filter(age__gte=19) # 大于等于 query_objs = models.Student.objects.filter(age__lt=20) # 小于 query_objs = models.Student.objects.filter(age__lte=20) # 小于等于 query_objs = models.Student.objects.filter(age__range=[18, 20]) # 范围 左右都包含 query_objs = models.Student.objects.filter(name__contains='xiao') # 针对字符串类型,内容含有 query_objs = models.Student.objects.filter(name__icontains='xiao') # 针对字符串类型,内容含有 不区分大小写 query_objs = models.Student.objects.filter(name__startswith='x') # 匹配以x开头 query_objs = models.Student.objects.filter(name__istartswith='x') # 匹配以x开头,不区分大小写 query_objs = models.Student.objects.filter(name__endswith='o') # 匹配以x结尾 query_objs = models.Student.objects.filter(name__iendswith='o') # 匹配以x结尾,不区分大小写 # 日期 # 日期这个字段查询起来比较特殊,如今咱们要从新建立一个表。 class Birthday(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) date = models.DateField() def __str__(self): return self.name # 注意date字段,这个字段要求的是date类型,咱们若是存放的是datetime类型也是能够的,只不过只是显示年月日。 # 接下来咱们给表中插入一些数据: import datetime models.Birthday.objects.create(name='太白1', date=datetime.datetime.now()) models.Birthday.objects.create(name='太白2', date='2000-04-25') models.Birthday.objects.create(name='太白3', date='2000-06-25') models.Birthday.objects.create(name='太白4', date='2000-08-26') models.Birthday.objects.create(name='太白5', date='2000-12-28') models.Birthday.objects.create(name='太白6', date='2001-03-26') models.Birthday.objects.create(name='太白7', date='2001-08-30') models.Birthday.objects.create(name='太白8', date='2003-01-13') models.Birthday.objects.create(name='太白9', date='2005-10-01') # 查询2000年出生的人,这样写就会报错了 query_objs = models.Birthday.objects.filter(date='2000') print(query_objs) # "'2000' value has an invalid date format. It must be in YYYY-MM-DD format."] # 查询2000年出生的人 query_objs = models.Birthday.objects.filter(date__year='2000') print(query_objs) # <QuerySet [<Birthday: 太白2>, <Birthday: 太白3>, <Birthday: 太白4>, <Birthday: 太白5>]> # 查询2000年4月出生的人 query_objs = models.Birthday.objects.filter(date__year='2000',date__month='04') print(query_objs) # <QuerySet [<Birthday: 太白2>]> # 查询2000年大于4月小于12月出生的人 query_objs = models.Birthday.objects.filter(date__year='2000', date__month__gt='04',date__month__lt='12') print(query_objs) # <QuerySet [<Birthday: 太白3>, <Birthday: 太白4>]> # 删: 删除行记录 # 从对象性质来区分删除有两种 # 1. 调用model对象删除 # 这样就是删除单条记录 models.Student.objects.get(id=20).delete() # 2. 调用QuerySet集合删除 # 这样就是批量删除记录,知足条件记录都会被删除 models.Student.objects.filter(age=20).delete() b = Blog.objects.get(pk=1) # This will delete the Blog and all of its Entry objects. b.delete() # 要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 自己。这是一种保护机制, # 是为了不意外地调用 Entry.objects.delete() 方法致使 全部的 记录被误删除。若是你确认要删除全部的对象,那么你必须显式地调用: Entry.objects.all().delete() # 若是不想级联删除,能够设置为: pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True) # 改: 更新行记录 # 更新记录的方法只有一个就是update,此方法只能用于QuerySet集合,model对象是不能够使用的。也就是说update能够进行批量更新的操做,而且他会返回一个更新的行记录数量的返回值。 # 我先增长了一条name同样的行记录 ''' # models.Student.objects.create( name='乔妹儿1', age=100 ) ''' count = models.Student.objects.filter(name='乔妹儿1').update(age=20) print(count) # 2 更新了2条
# 获取个数 # # 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等于十一、2二、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项目,建立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>
表和表之间的关系
一对1、多对1、多对多 ,用book表和publish表本身来想一想关系,想一想里面的操做,加外键约束和不加外键约束的区别,一对一的外键约束是在一对多的约束上加上惟一约束。
实例:咱们来假定下面这些概念,字段和关系
做者模型:一个做者有姓名和年龄。
做者详细模型:把做者的详情放到详情表,包含生日,手机号,家庭住址等信息。做者详情模型和做者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个做者,一个做者也能够写多本书,因此做者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,因此出版商和书籍是一对多关联关系(one-to-many)。
from django.db import models class Author(models.Model): #比较经常使用的信息放到这个表里面 nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 与AuthorDetail创建一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面均可以 authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #就是foreignkey+unique,只不过不须要咱们本身来写参数了,而且orm会自动帮你给这个字段名字拼上一个_id,数据库中字段名称为authorDetail_id class AuthorDetail(models.Model):#不经常使用的放到这个表里面 nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() # 多对多的表关系,mysql的时候是怎么创建的,是否是手动建立一个第三张表,而后写上两个字段,每一个字段外键关联到另外两张多对多关系的表,orm的manytomany自动帮咱们建立第三张表,两种方式创建关系均可以,之后的学习咱们暂时用orm自动建立的第三张表,由于手动建立的第三张表咱们进行orm操做的时候,不少关于多对多关系的表之间的orm语句方法没法使用 #若是你想删除某张表,你只须要将这个表注销掉,而后执行那两个数据库同步指令就能够了,自动就删除了。 class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish创建一对多的关系,外键字段创建在多的一方,字段publish若是是外键字段,那么它自动是int类型 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey里面能够加不少的参数,都是须要我们学习的,慢慢来,to指向表,to_field指向你关联的字段,不写这个, # 默认会自动关联主键字段,on_delete级联删除字段名称不须要写成publish_id,orm在翻译foreignkey的时候会自动给你这个字段拼上一个_id, # 这个字段名称在数据库里面就自动变成了publish_id # 与Author表创建多对多的关系,ManyToManyField能够建在两个模型中的任意一个,自动建立第三张表, # 而且注意一点,你查看book表的时候,你看不到这个字段,由于这个字段就是建立第三张表的意思,不是建立字段的意思, # 因此只能说这个book类里面有authors这个字段属性 authors=models.ManyToManyField(to='Author',) # 注意无论是一对多仍是多对多,写to这个参数的时候,最后后面的值是个字符串,否则你就须要将你要关联的那个表放到这个表的上面 class UserInfo(models.Model): name = models.CharField(max_length=16) password = models.CharField(max_length=20) class Student(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) age = models.IntegerField()
方式一: 自行建立第三张表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") class Author(models.Model): name = models.CharField(max_length=32, verbose_name="做者姓名") # 本身建立第三张表,分别经过外键关联书和做者 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
方式二: 经过ManyToManyField自动
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 经过ORM自带的ManyToManyField自动建立第三张表 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="做者姓名") books = models.ManyToManyField(to="Book", related_name="authors") #自动生成的第三张表咱们是没有办法添加其余字段的
方式三: 设置ManyTomanyField并指定自行建立的第三张表(称为中介模型)
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 本身建立第三张表,并经过ManyToManyField指定关联 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="做者姓名") books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book")) # through_fields接受一个2元组('field1','field2'): # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") #能够扩展其余的字段了 class Meta: unique_together = ("author", "book")
注意
# 当咱们须要在第三张关系表中存储额外的字段时,就要使用第三种方式,第三种方式仍是能够使用多对多关联关系操做的接口(all、add、clear等等) # 当咱们使用第一种方式建立多对多关联关系时,就没法使用orm提供的set、add、remove、clear方法来管理多对多的关系了。
一对一相关字段
to 设置要关联的表。 to_field 设置要关联的字段。 on_delete 同ForeignKey字段。 建立一对一关系字段时的一些参数
一对多相关字段
to 设置要关联的表 to_field 设置要关联的表的字段 related_name 反向操做时,使用的字段名,用于代替原反向查询时的'表名_set'。 related_query_name 反向查询操做时,使用的链接前缀,用于替换表名。 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。 建立一对多关系字段时的一些参数
多对多相关字段
多对多的参数: to 设置要关联的表 related_name 同ForeignKey字段。 related_query_name 同ForeignKey字段。 through 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。 但咱们也能够手动建立第三张表来管理多对多关系,此时就须要经过 through来指定第三张表的表名。 through_fields 设置关联的字段。 db_table 默认建立第三张表时,数据库中表的名称。 建立多对多字段时的一些参数
元信息
元信息 ORM对应的类里面包含另外一个Meta类,而Meta类封装了一些数据库的信息。主要字段以下: class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book") db_table ORM在数据库中的表名默认是 app_类名,能够经过db_table能够重写表名。db_table = 'book_model' index_together 联合索引。 unique_together 联合惟一索引。 ordering 指定默认按什么字段排序。 ordering = ['pub_date',] 只有设置了该属性,咱们查询到的结果才能够被reverse(),不然是能对排序了的结果进行反转(order_by()方法排序过的数据) 建立表时的一些元信息设置
获取元信息,能够经过model对象._meta.verbose_name等获取本身经过verbose_name指定的表名,model对象._meta.model_name获取小写的表名,还有model对象.app_label能够获取这个对象的app应用名等等操做。例如:book_obj = models.Book.objects.get(id=1),book_obj._meta.model_name。
关于db_column和verbose_name
1.指定字段名: 在定义字段的时候,增长参数db_column=’real_field’; 2.指定表名: 在model的class中,添加Meta类,在Meta类中指定表名db_table # 例如在某个models.py文件中,有一个类叫Info: class Info(models.Model): ''''' 信息统计 ''' app_id = models.ForeignKey(App) app_name = models.CharField(verbose_name='应用名', max_length=32, db_column='app_name2') class Meta: db_table = 'info' verbose_name = '信息统计' verbose_name_plural = '信息统计' # 其中db_column指定了对应的字段名,db_table指定了对应的代表; # 若是不这样指定,字段名默认为app_name, 而代表默认为app名+类名: [app_name]_info. # verbose_name指定在admin管理界面中显示中文;verbose_name表示单数形式的显示,verbose_name_plural表示复数形式的显示;中文的单数和复数通常不做区别。 # 建立完这个表,咱们本身能够经过navicat工具来看看数据库里面的那些表,出版社这个表里面没有任何的关系字段, # 这种单表的数据,咱们能够先添加几条数据,在进行下面的增删改查的操做。 # 生成表以下:
注意事项
# 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也能够覆写为别的名称 # id 字段是自动添加的 # 对于外键字段,Django 会在字段名上添加"_id" 来建立数据库中的列名 # 这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。 # 定义好模型以后,你须要告诉Django _使用_这些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。 # 外键字段 ForeignKey 有一个 null=True 的设置(它容许外键接受空值 NULL),你能够赋给它空值 None 。 # 我们的表里面包含了一对1、一对多、多对多的关系,咱们基于这几个表来练习,未来不管有多少张表,都逃脱不了这三个关系,操做起来都是同样的。
on_delete
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。 models.CASCADE 删除关联数据,与之关联也删除 models.DO_NOTHING 删除关联数据,引起错误IntegrityError models.PROTECT 删除关联数据,引起错误ProtectedError models.SET_NULL 删除关联数据,与之关联的值设置为null(前提FK字段须要设置为可空) models.SET_DEFAULT 删除关联数据,与之关联的值设置为默认值(前提FK字段须要设置默认值) models.SET 删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) # ForeignKey的db_contraint参数 关系和约束你们要搞清楚,我不加外键能不能表示两个表之间的关系啊,固然能够 可是咱们就不能使用ORM外键相关的方法了,因此咱们单纯的将外键换成一个其余字段类型,只是单纯的存着另一个关联表的主键值是不能使用ORM外键方法的。 #db_constraint=False只加二者的关系,没有强制约束的效果,而且ORM外键相关的接口(方法)还能使用,因此若是未来公司让你创建外键,而且不能有强制的约束关系,那么就能够将这个参数改成False customer = models.ForeignKey(verbose_name='关联客户', to='Customer',db_constraint=False)
操做前先简单的录入一些数据:仍是create和save两个方法,和单表的区别就是看看怎么添加关联字段的数据
一对多