web应用
Web应用程序是一种能够经过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只须要有浏览器便可,不须要再安装其余软件。应用程序有两种模式C/S、B/S。C/S是客户端/服务器端程序,也就是说这类程序通常独立运行。而B/S就是浏览器端/服务器端应用程序,这类应用程序通常借助谷歌,火狐等浏览器来运行。WEB应用程序通常是B/S模式。Web应用程序首先是“应用程序”,和用标准的程序语言,如java,python等编写出来的程序没有什么本质上的不一样。在网络编程的意义下,浏览器是一个socket客户端,服务器是一个socket服务端。php
import socket
def handle_request(client):
request_data = client.recv(1024)
print("request_data: ",request_data)
client.send("HTTP/1.1 200 OK\r\nstatus: 200\r\nContent-Type:text/html\r\n\r\n".encode("utf8"))
client.send("<h1>Hello, luffycity!</h1><img src=''>".encode("utf8"))
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8812))
sock.listen(5)
while True:
print("the server is waiting for client-connection....")
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ == '__main__':
main()
http协议简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。css
HTTP是一个属于应用层的面向对象的协议,因为其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,通过几年的使用与发展,获得不断地完善和扩展。HTTP协议工做于客户端-服务端架构为上。浏览器做为HTTP客户端经过URL向HTTP服务端即WEB服务器发送全部请求。Web服务器根据接收到的请求后,向客户端发送响应信息。html

http协议特性
http协议是基于TCP/IP协议之上的应用层协议。java
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并 返回。换句话说,确定是先从客户端开始创建通讯的,服务器端在没有 接收到请求以前不会发送响应python

(3) 无状态保存
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通讯状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不作持久化处理。mysql
-
-
-

使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议自己并不保留以前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特地把HTTP协议设计成 如此简单的。但是,随着Web的不断发展,因无状态而致使业务处理变得棘手 的状况增多了。好比,用户登陆到一家购物网站,即便他跳转到该站的 其余页面后,也须要能继续保持登陆状态。针对这个实例,网站为了能 够掌握是谁送出的请求,须要保存用户的状态。HTTP/1.1虽然是无状态协议,但为了实现指望的保持状态功能, 因而引入了Cookie技术。有了Cookie再用HTTP协议通讯,就能够管 理状态了。有关Cookie的详细内容稍后讲解。git
无链接
无链接的含义是限制每次链接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开链接。采用这种方式能够节省传输时间。程序员
http请求协议与响应协议
http协议包含由浏览器发送数据到服务器须要遵循的请求协议与服务器发送数据到浏览器须要遵循的请求协议。用于HTTP协议交互的信被为HTTP报文。请求端(客户端)的HTTP报文 作请求报文,响应端(服务器端)的 作响应报文。HTTP报文自己是由多行数据构成的字 文本。 web

请求协议
请求格式

请求方式: get与post请求
-
GET提交的数据会放在URL以后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
-
GET提交的数据大小有限制(由于浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
-
GET与POST请求在服务端获取请求数据方式不一样。
响应协议
响应格式

响应状态码
状态码的职 是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户能够知道服务器端是正常 理了请求,仍是出 现了 。状态码如200 OK,以3位数字和缘由 成。数字中的 一位指定了响应 别,后两位无分 。响应 别有以5种。ajax

演示示例:
import socket
sock=socket.socket()
sock.bind(("127.0.0.1",8808))
sock.listen(5)
while 1:
print("server waiting.....")
conn,addr=sock.accept()
data=conn.recv(1024)
print("data",data)
# 读取html文件
with open("login.html","rb") as f:
data=f.read()
conn.send((b"HTTP/1.1 200 OK\r\n\r\n%s"%data))
conn.close()
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
用户名 <input type="text" name="user">
密码 <input type="password" name="pwd">
<input type="submit">
</form>
</body>
</html>
web框架
Web框架(Web framework)是一种开发框架,用来支持动态网站、网络应用和网络服务的开发。这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方法。web框架已经实现了不少功能,开发人员使用框架提供的方法而且完成本身的业务逻辑,就能快速开发web应用了。浏览器和服务器的是基于HTTP协议进行通讯的。也能够说web框架就是在以上十几行代码基础张扩展出来的,有不少简单方便使用的方法,大大提升了开发的效率。
wsgiref模块
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
若是要动态生成HTML,就须要把上述步骤本身来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,若是咱们本身来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的作法是底层代码由专门的服务器软件实现,咱们用Python专一于生成HTML文档。由于咱们不但愿接触到TCP链接、HTTP原始请求和响应格式,因此,须要一个统一的接口协议来实现这样的服务器软件,让咱们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块。
from wsgiref.simple_server import make_server
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
httpd = make_server('', 8080, application)
print('Serving HTTP on port 8000...')
# 开始监听HTTP请求:
httpd.serve_forever()
DIY一个web框架

models.py
import pymysql
#链接数据库
conn = pymysql.connect(host='127.0.0.1',port= 3306,user = 'root',passwd='',db='web') #db:库名
#建立游标
cur = conn.cursor()
sql='''
create table userinfo(
id INT PRIMARY KEY ,
name VARCHAR(32) ,
password VARCHAR(32)
)
'''
cur.execute(sql)
#提交
conn.commit()
#关闭指针对象
cur.close()
#关闭链接对象
conn.close()
启动文件manage.py
from wsgiref.simple_server import make_server
from app01.views import *
import urls
def routers():
URLpattern=urls.URLpattern
return URLpattern
def applications(environ,start_response):
path=environ.get("PATH_INFO")
start_response('200 OK', [('Content-Type', 'text/html'),('Charset', 'utf8')])
urlpattern=routers()
func=None
for item in urlpattern:
if path==item[0]:
func=item[1]
break
if func:
return [func(environ)]
else:
return [b"<h1>404!<h1>"]
if __name__ == '__main__':
server=make_server("",8889,applications)
print("server is working...")
server.serve_forever()
urls.py
from app01.views import *
URLpattern = (
("/login/", login),
)
views
import pymysql
from urllib.parse import parse_qs
def login(request):
if request.get("REQUEST_METHOD")=="POST":
try:
request_body_size = int(request.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
request_body = request['wsgi.input'].read(request_body_size)
data = parse_qs(request_body)
user=data.get(b"user")[0].decode("utf8")
pwd=data.get(b"pwd")[0].decode("utf8")
#链接数据库
conn = pymysql.connect(host='127.0.0.1',port= 3306,user = 'root',passwd='',db='web') # db:库名
#建立游标
cur = conn.cursor()
SQL="select * from userinfo WHERE NAME ='%s' AND PASSWORD ='%s'"%(user,pwd)
cur.execute(SQL)
if cur.fetchone():
f=open("templates/backend.html","rb")
data=f.read()
data=data.decode("utf8")
return data.encode("utf8")
else:
print("OK456")
return b"user or pwd is wrong"
else:
f = open("templates/login.html", "rb")
data = f.read()
f.close()
return data
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h4>登陆页面</h4>
<form action="" method="post">
用户名 <input type="text" name="user">
密码 <input type="text" name="pwd">
<input type="submit">
</form>
</body>
</html>
backend.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h4>welcome to oldboy!</h4>
</body>
</html>
yuan这个package就是一个web框架,下载这个web框架就能够快速实现一些简单的web功能,好比查看时间。
4.Django简介
1)MVC和MTV模型
2)Django的下载和安装
3)基于Django实现一个简单的实例
MVC与MTV模型
MVC
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式链接在一块儿,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图以下所示:

MTV
Django的MTV模式本质上和MVC是同样的,也是为了各组件间保持松耦合关系,只是定义上有些许不一样,Django的MTV分别是值:
- M 表明模型(Model): 负责业务对象和数据库的关系映射(ORM)。
- T 表明模板 (Template):负责如何把页面展现给用户(html)。
- V 表明视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层以外,还须要一个URL分发器,它的做用是将一个个URL的页面请求分发给不一样的View处理,View再调用相应的Model和Template,MTV的响应模式以下所示:

通常是用户经过浏览器向咱们的服务器发起一个请求(request),这个请求回去访问视图函数,(若是不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户),视图函数调用模型,模型去数据库查找数据,而后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
Django的下载与基本命令
一、下载Django:
二、建立一个django project
1
|
django
-
admin.py startproject mysite
|
当前目录下会生成mysite的工程,目录结构以下:

- manage.py ----- Django项目里面的工具,经过它能够调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其余一些工做的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
三、在mysite目录下建立应用
1
|
python manage.py startapp blog
|

四、启动django项目
1
|
python manage.py runserver
8080
|
这样咱们的django就启动起来了!当咱们访问:http://127.0.0.1:8080/时就能够看到:

基于Django实现的一个简单示例
url控制器
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/',views.index),
]
视图
from django.shortcuts import render
# Create your views here.
def index(request):
import datetime
now=datetime.datetime.now()
ctime=now.strftime("%Y-%m-%d %X")
return render(request,"index.html",{"ctime":ctime})
模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h4>当前时间:{{ ctime }}</h4>
</body>
</html>
执行效果以下:

Django-2 路由层(URLconf)
URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对应执行。
简单的路由配置
from django.urls import path,re_path
from app01 import views
urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/([0-9]{4})/$', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
注意:
- 若要从URL 中捕获一个值,只须要在它周围放置一对圆括号。
- 不须要添加一个前导的反斜杠,由于每一个URL 都有。例如,应该是
^articles
而不是 ^/articles
。
- 每一个正则表达式前面的'r' 是可选的可是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不该该转义
示例:
'''
一些请求的例子:
/articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数views.month_archive(request, '2005', '03')。
/articles/2005/3/ 不匹配任何URL 模式,由于列表中的第三个模式要求月份应该是两个数字。
/articles/2003/ 将匹配列表中的第一个模式不是第二个,由于模式按顺序匹配,第一个会首先测试是否匹配。请像这样自由插入一些特殊的状况来探测匹配的次序。
/articles/2003 不匹配任何一个模式,由于每一个模式要求URL 以一个反斜线结尾。
/articles/2003/03/03/ 将匹配最后一个模式。Django 将调用函数views.article_detail(request, '2003', '03', '03')。
'''
有名分组
上面的示例使用简单的、没有命名的正则表达式组(经过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。
在Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式。
下面是以上URLconf 使用命名组的重写:
from django.urls import path,re_path
from app01 import views
urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
这个实现与前面的示例彻底相同,只有一个细微的差异:捕获的值做为关键字参数而不是位置参数传递给视图函数。例如:
'''
/articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')。
/articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。
'''
在实际应用中,这意味你的URLconf 会更加明晰且不容易产生参数顺序问题的错误 —— 你能够在你的视图函数定义中从新安排参数的顺序。固然,这些好处是以简洁为代价;
分发
'''
At any point, your urlpatterns can “include” other URLconf modules. This
essentially “roots” a set of URLs below other ones.
'''
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^blog/', include('blog.urls')),
]
反向解析
在使用Django 项目时,一个常见的需求是得到URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人们强烈但愿不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 绝不相关的专门的URL 生成机制,由于这样容易致使必定程度上产生过时的URL。
在须要URL 的地方,对于不一样层级,Django 提供不一样的工具用于URL 反查:
- 在模板中:使用url 模板标签。
- 在Python 代码中:使用
from django.urls import reverse()函数
urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
#...
re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
#...
]
在模板中:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>
在python中:
from django.urls import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
# ...
year = 2006
# ...
return HttpResponseRedirect(reverse('news-year-archive', args=(year,))) # 同redirect("/path/")
当命名你的URL 模式时,请确保使用的名称不会与其它应用中名称冲突。若是你的URL 模式叫作comment
,而另一个应用中也有一个一样的名称,当你在模板中使用这个名称的时候不能保证将插入哪一个URL。在URL 名称中加上一个前缀,好比应用的名称,将减小冲突的可能。咱们建议使用myapp-comment
而不是comment
。
名称空间
命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不一样命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,由于已有的定义都处于其它命名空间中。
因为name没有做用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,当即返回
咱们在开发项目时,会常用name属性反解出URL,当不当心在不一样的app的urls中定义相同的name时,可能会致使URL反解错误,为了不这种事情发生,引入了命名空间。
project的urls.py:
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^app01/', include("app01.urls",namespace="app01")),
re_path(r'^app02/', include("app02.urls",namespace="app02")),
]
app01.urls:
urlpatterns = [
re_path(r'^index/', index,name="index"),
]
app02.urls:
urlpatterns = [
re_path(r'^index/', index,name="index"),
]
app01.views
from django.core.urlresolvers import reverse
def index(request):
return HttpResponse(reverse("app01:index"))
app02.views
from django.core.urlresolvers import reverse
def index(request):
return HttpResponse(reverse("app02:index"))
django2.0版的path
思考状况以下:
urlpatterns = [
re_path('articles/(?P<year>[0-9]{4})/', year_archive),
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]
考虑下这样的两个问题:
第一个问题,函数 year_archive
中year参数是字符串类型的,所以须要先转化为整数类型的变量值,固然year=int(year)
不会有诸如如TypeError或者ValueError的异常。那么有没有一种方法,在url中,使得这一转化步骤能够由Django自动完成?
第二个问题,三个路由中article_id都是一样的正则表达式,可是你须要写三遍,当以后article_id规则改变后,须要同时修改三处代码,那么有没有一种方法,只需修改一处便可?
在Django2.0中,可使用 path
解决以上的两个问题。
基本示例
这是一个简单的例子:
from django.urls import path
from . import views
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>/', views.article_detail),
]
基本规则:
- 使用尖括号(
<>
)从url中捕获值。
- 捕获值中能够包含一个转化器类型(converter type),好比使用
<int:name>
捕获一个整数变量。若果没有转化器,将匹配任何字符串,固然也包括了 /
字符。
- 无需添加前导斜杠。
如下是根据 2.0官方文档 而整理的示例分析表:

path转化器
文档原文是Path converters,暂且翻译为转化器。
Django默认支持如下5个转化器:
- str,匹配除了路径分隔符(
/
)以外的非空字符串,这是默认的形式
- int,匹配正整数,包含0。
- slug,匹配字母、数字以及横杠、下划线组成的字符串。
- uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
- path,匹配任何非空字符串,包含了路径分隔符
注册自定义转化器
对于一些复杂或者复用的须要,能够定义本身的转化器。转化器是一个类或接口,它的要求有三点:
to_python(self, value)
方法,value是由类属性 regex
所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
to_url(self, value)
方法,和 to_python
相反,value是一个具体的Python变量值,返回其字符串,一般用于url反向引用。
例子:
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
使用register_converter
将其注册到URL配置中:
from django.urls import register_converter, path
from . import converters, views
register_converter(converters.FourDigitYearConverter, 'yyyy')
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<yyyy:year>/', views.year_archive),
...
]
Django的视图层
视图函数
一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求而且返回Web响应。响应能够是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西均可以。不管视图自己包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此以外没有更多的要求了——能够说“没有什么神奇的地方”。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。
下面是一个返回当前日期和时间做为HTML文档的视图:
from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
让咱们逐行阅读上面的代码:
-
首先,咱们从 django.shortcuts模块导入了HttpResponse类,以及Python的datetime库。
-
接着,咱们定义了current_datetime函数。它就是视图函数。每一个视图函数都使用HttpRequest对象做为第一个参数,而且一般称之为request。
注意,视图函数的名称并不重要;不须要用一个统一的命名方式来命名,以便让Django识别它。咱们将其命名为current_datetime,是由于这个名称可以精确地反映出它的功能。
-
这个视图会返回一个HttpResponse对象,其中包含生成的响应。每一个视图函数都负责返回一个HttpResponse对象。

视图层,熟练掌握两个对象便可:请求对象(request)和响应对象(HttpResponse)
HttpRequest对象
request属性
django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。 除了特殊说明的以外,其余均为只读的。
/*
1.HttpRequest.GET
一个相似于字典的对象,包含 HTTP GET 的全部参数。详情请参考 QueryDict 对象。
2.HttpRequest.POST
一个相似于字典的对象,若是请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
POST 请求能够带有空的 POST 字典 —— 若是经过 HTTP POST 方法发送一个表单,可是表单中没有任何的数据,QueryDict 对象依然会被建立。
所以,不该该使用 if request.POST 来检查使用的是不是POST 方法;应该使用 if request.method == "POST"
另外:若是使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。
注意:键值对的值是多个的时候,好比checkbox类型的input标签,select标签,须要用:
request.POST.getlist("hobby")
3.HttpRequest.body
一个字符串,表明请求报文的主体。在处理非 HTTP 形式的报文时很是有用,例如:二进制图片、XML,Json等。
可是,若是要处理表单数据的时候,推荐仍是使用 HttpRequest.POST 。
4.HttpRequest.path
一个字符串,表示请求的路径组件(不含域名)。
例如:"/music/bands/the_beatles/"
5.HttpRequest.method
一个字符串,表示请求使用的HTTP 方法。必须使用大写。
例如:"GET"、"POST"
6.HttpRequest.encoding
一个字符串,表示提交的数据的编码方式(若是为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8')。
这个属性是可写的,你能够修改它来修改访问表单数据使用的编码。
接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
若是你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。
7.HttpRequest.META
一个标准的Python 字典,包含全部的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
CONTENT_TYPE —— 请求的正文的MIME 类型。
HTTP_ACCEPT —— 响应可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
HTTP_HOST —— 客服端发送的HTTP Host 头部。
HTTP_REFERER —— Referring 页面。
HTTP_USER_AGENT —— 客户端的user-agent 字符串。
QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
REMOTE_ADDR —— 客户端的IP 地址。
REMOTE_HOST —— 客户端的主机名。
REMOTE_USER —— 服务器认证后的用户。
REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
SERVER_NAME —— 服务器的主机名。
SERVER_PORT —— 服务器的端口(是一个字符串)。
从上面能够看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 以外,请求中的任何 HTTP 首部转换为 META 的键时,
都会将全部字母大写并将链接符替换为下划线最后加上 HTTP_ 前缀。
因此,一个叫作 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
8.HttpRequest.FILES
一个相似于字典的对象,包含全部的上传文件信息。
FILES 中的每一个键为<input type="file" name="" /> 中的name,值则为对应的数据。
注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的状况下才会
包含数据。不然,FILES 将为一个空的相似于字典的对象。
9.HttpRequest.COOKIES
一个标准的Python 字典,包含全部的cookie。键和值都为字符串。
10.HttpRequest.session
一个既可读又可写的相似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
完整的细节参见会话的文档。
11.HttpRequest.user(用户认证组件下使用)
一个 AUTH_USER_MODEL 类型的对象,表示当前登陆的用户。
若是用户当前没有登陆,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你能够经过 is_authenticated() 区分它们。
例如:
if request.user.is_authenticated():
# Do something for logged-in users.
else:
# Do something for anonymous users.
user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。
-------------------------------------------------------------------------------------
匿名用户
class models.AnonymousUser
django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具备下面几个不一样点:
id 永远为None。
username 永远为空字符串。
get_username() 永远返回空字符串。
is_staff 和 is_superuser 永远为False。
is_active 永远为 False。
groups 和 user_permissions 永远为空。
is_anonymous() 返回True 而不是False。
is_authenticated() 返回False 而不是True。
set_password()、check_password()、save() 和delete() 引起 NotImplementedError。
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
*/
request经常使用方法
/*
1.HttpRequest.get_full_path()
返回 path,若是能够将加上查询字符串。
例如:"/music/bands/the_beatles/?print=true"
2.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') 装饰你的视图以让响应可以正确地缓存。
*/
HttpResponse对象
响应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
HttpResponse()括号内直接跟一个具体的字符串做为响应体,比较直接很简单,因此这里主要介绍后面两种形式。
render()
1
2
3
|
render(request, template_name[, context])
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
|
参数:
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。若是字典中的某个值是可调用的,视图将在渲染模板以前调用它。
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面做为响应体。
redirect()
传递要重定向的一个硬编码的URL
1
2
3
|
def
my_view(request):
...
return
redirect(
'/some/url/'
)
|
也能够是一个完整的URL:
1
2
3
|
def
my_view(request):
...
return
redirect(
'http://example.com/'
)
|
key:两次请求
1)301和302的区别。
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址能够从响应的Location首部中获取
(用户看到的效果就是他输入的地址A瞬间变成了另外一个地址B)——这是它们的共同点。
他们的不一样在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向以后的网址;
302表示旧地址A的资源还在(仍然能够访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301
2)重定向缘由:
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用须要把.php改为.Html或.shtml)。
这种状况下,若是不作重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户获得一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的
网站,也须要经过重定向让访问这些域名的用户自动跳转到主站点等。
关于301与302
用redirect能够解释APPEND_SLASH的用法!
Django的模板层
你可能已经注意到咱们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中
。
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
尽管这种技术便于解释视图是如何工做的,但直接将HTML硬编码到你的视图里却并非一个好主意。 让咱们来看一下为何:
基于这些缘由,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 咱们可使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。
def current_time(req):
# ================================原始的视图函数
# import datetime
# now=datetime.datetime.now()
# html="<html><body>如今时刻:<h1>%s.</h1></body></html>" %now
# ================================django模板修改的视图函数
# from django.template import Template,Context
# now=datetime.datetime.now()
# t=Template('<html><body>如今时刻是:<h1>{{current_date}}</h1></body></html>')
# #t=get_template('current_datetime.html')
# c=Context({'current_date':str(now)})
# html=t.render(c)
#
# return HttpResponse(html)
#另外一种写法(推荐)
import datetime
now=datetime.datetime.now()
return render(req, 'current_datetime.html', {'current_date':str(now)[:19]})
1 模板语法之变量
在 Django 模板中遍历复杂数据结构的关键是句点字符, 语法:
views.py:
def index(request):
import datetime
s="hello"
l=[111,222,333] # 列表
dic={"name":"yuan","age":18} # 字典
date = datetime.date(1993, 5, 2) # 日期对象
class Person(object):
def __init__(self,name):
self.name=name
person_yuan=Person("yuan") # 自定义类对象
person_egon=Person("egon")
person_alex=Person("alex")
person_list=[person_yuan,person_egon,person_alex]
return render(request,"index.html",{"l":l,"dic":dic,"date":date,"person_list":person_list})
template:
1
2
3
4
5
6
|
<h4>{{s}}<
/
h4>
<h4>列表:{{ l.
0
}}<
/
h4>
<h4>列表:{{ l.
2
}}<
/
h4>
<h4>字典:{{ dic.name }}<
/
h4>
<h4>日期:{{ date.year }}<
/
h4>
<h4>类对象列表:{{ person_list.
0.name
}}<
/
h4>
|
注意:句点符也能够用来引用对象的方法(无参数方法):
1
|
<h4>字典:{{ dic.name.upper }}<
/
h4>
|
2 模板之过滤器
语法:
1
|
{{obj|filter__name:param}}
|
default
若是一个变量是false或者为空,使用给定的默认值。不然,使用变量的值。例如:
1
|
{{ value|default:
"nothing"
}}
|
length
返回值的长度。它对字符串和列表都起做用。例如:
若是 value 是 ['a', 'b', 'c', 'd'],那么输出是 4。
filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB'
, '4.1 MB'
, '102 bytes'
, 等等)。例如:
1
|
{{ value|filesizeformat }}
|
若是 value
是 123456789,输出将会是 117.7 MB
。
date
若是 value=datetime.datetime.now()
1
|
{{ value|date:
"Y-m-d"
}}
|
slice
若是 value="hello world"
1
|
{{ value|
slice
:
"2:-1"
}}
|
truncatechars
若是字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
参数:要截断的字符数
例如:
1
|
{{ value|truncatechars:
9
}}
|
safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,缘由显而易见,这样是为了安全。可是有的时候咱们可能不但愿这些HTML元素被转义,好比咱们作一个内容管理系统,后台添加的文章中是通过修饰的,这些修饰多是经过一个相似于FCKeditor编辑加注了HTML修饰符的文本,若是自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,若是是一个单独的变量咱们能够经过过滤器“|safe”的方式告诉Django这段代码是安全的没必要转义。好比:
1
|
value
=
"<a href="
">点击</a>"
|
这里简单介绍一些经常使用的模板的过滤器,更多详见
3 模板之标签
标签看起来像是这样的: {% tag %}
。标签比变量更加复杂:一些在输出中建立文本,一些经过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。一些标签须要开始和结束标签 (例如{% tag %} ...
标签 内容 ... {% endtag %})。
for标签
遍历每个元素:
{% for person in person_list %}
<p>{{ person.name }}</p>
{% endfor %}
能够利用{% for obj in list reversed %}反向完成循环。
遍历一个字典:
{% for key,val in dic.items %}
<p>{{ key }}:{{ val }}</p>
{% endfor %}
注:循环序号能够经过{{forloop}}显示
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
for ... empty
for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,能够有所操做。
{% for person in person_list %}
<p>{{ person.name }}</p>
{% empty %}
<p>sorry,no person here</p>
{% endfor %}
if 标签
{% if %}会对一个变量求值,若是它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。
{% if num > 100 or num < 0 %}
<p>无效</p>
{% elif num > 80 and num < 100 %}
<p>优秀</p>
{% else %}
<p>凑活吧</p>
{% endif %}
with
使用一个简单地名字缓存一个复杂的变量,当你须要使用一个“昂贵的”方法(好比访问数据库)不少次的时候是很是有用的
例如:
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
csrf_token
这个标签用于跨站请求伪造保护
4 自定义标签和过滤器
一、在settings中的INSTALLED_APPS配置当前app,否则django没法找到自定义的simple_tag.
二、在app中建立templatetags模块(模块名只能是templatetags)
三、建立任意 .py 文件,如:my_tags.py
四、在使用自定义simple_tag和filter的html文件中导入以前建立的 my_tags.py
五、使用simple_tag和filter(如何调用)
1
2
3
4
5
6
7
8
9
10
|
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.html
{
%
load xxx
%
}
{{ num|filter_multi:
2
}}
{{ num|filter_multi:
"[22,333,4444]"
}}
{
%
simple_tag_multi
2
5
%
} 参数不限,但不能放在
if
for
语句中
{
%
simple_tag_multi num
5
%
}
|
注意:filter能够用在if等语句后,simple_tag不能够
5 模板继承 (extend)
Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可让您建立一个基本的“骨架”模版,它包含您站点中的所有元素,而且能够定义可以被子模版覆盖的 blocks 。
经过从下面这个例子开始,能够容易的理解模版继承:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<title>{% block title %}My amazing site{%/span> endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
这个模版,咱们把它叫做 base.html
, 它定义了一个能够用于两列排版页面的简单HTML骨架。“子模版”的工做是用它们的内容填充空的blocks。
在这个例子中, block
标签订义了三个能够被子模版内容填充的block。 block
告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。
子模版可能看起来是这样的:
extends
标签是这里的关键。它告诉模版引擎,这个模版“继承”了另外一个模版。当模版系统处理这个模版时,首先,它将定位父模版——在此例中,就是“base.html”。
那时,模版引擎将注意到 base.html
中的三个 block
标签,并用子模版中的内容来替换这些block。根据 blog_entries
的值,输出可能看起来是这样的:
请注意,子模版并无定义 sidebar
block,因此系统使用了父模版中的值。父模版的 {% block %}
标签中的内容老是被用做备选内容(fallback)。
这种方式使代码获得最大程度的复用,而且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。
这里是使用继承的一些提示:
-
若是你在模版中使用 {% extends %}
标签,它必须是模版中的第一个标签。其余的任何状况下,模版继承都将没法工做。
-
在base模版中设置越多的 {% block %}
标签越好。请记住,子模版没必要定义所有父模版中的blocks,因此,你能够在大多数blocks中填充合理的默认内容,而后,只定义你须要的那一个。多一点钩子总比少一点好。
-
若是你发现你本身在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %}
中。
-
If you need to get the content of the block from the parent template, the {{ 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.
-
为了更好的可读性,你也能够给你的 {% endblock %}
标签一个 名字 。例如:
1
2
3
|
{
%
block content
%
}
...
{
%
endblock content
%
}
|
在大型模版中,这个方法帮你清楚的看到哪个 {% block %}
标签被关闭了。
- 不能在一个模版中定义多个相同名字的
block
标签。
ORM简介
ORM简介
- MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不须要依赖于特定的数据库,经过简单的配置就能够轻松更换数据库,这极大的减轻了开发人员的工做量,不须要面对因数据库变动而致使的无效劳动
- ORM是“对象-关系-映射”的简称。

View Code
单表操做
建立表
1 建立模型

建立名为book的app,在book下的models.py中建立模型:
from django.db import models
# Create your models here.
class Book(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
state=models.BooleanField()
pub_date=models.DateField()
price=models.DecimalField(max_digits=8,decimal_places=2)
publish=models.CharField(max_length=32)
2 更多字段和参数
每一个字段有一些特有的参数,例如,CharField须要max_length参数来指定VARCHAR
数据库字段的大小。还有一些适用于全部字段的通用参数。 这些参数在文档中有详细定义,这里咱们只简单介绍一些最经常使用的:
更多字段:
<1> CharField
字符串字段, 用于较短的字符串.
CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所容许的最大字符数.
<2> IntegerField
#用于保存一个整数.
<3> FloatField
一个浮点数. 必须 提供两个参数:
参数 描述
max_digits 总位数(不包括小数点和符号)
decimal_places 小数位数
举例来讲, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
models.FloatField(..., max_digits=5, decimal_places=2)
要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
models.FloatField(..., max_digits=19, decimal_places=10)
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
字段的默认值。能够是一个值或者可调用对象。若是可调用 ,每有新对象被建立它都会被调用。
(3)primary_key
若是为True,那么这个字段就是模型的主键。若是你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段作为主键,因此除非你想覆盖默认的主键行为,
不然不必设置任何一个字段的primary_key=True。
(4)unique
若是该值设置为 True, 这个数据字段的值在整张表中必须是惟一的
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 若是设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>并且这个选择框的选项就是choices 中的选项。
3 settings配置
若想将模型转为mysql数据库中的表,须要在settings中配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'bms', # 要链接的数据库,链接前须要建立好
'USER':'root', # 链接数据库的用户名
'PASSWORD':'', # 链接数据库的密码
'HOST':'127.0.0.1', # 链接主机,默认本级
'PORT':3306 # 端口 默认3306
}
}
注意1:NAME即数据库的名字,在mysql链接前该数据库必须已经建立,而上面的sqlite数据库下的db.sqlite3则是项目自动建立 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动咱们的Django项目前,咱们须要激活咱们的mysql。而后,启动项目,会报错:no module named MySQLdb 。这是由于django默认你导入的驱动是MySQLdb,但是MySQLdb 对于py3有很大问题,因此咱们须要的驱动是PyMySQL 因此,咱们只须要找到项目名文件下的__init__,在里面写入:
1
2
|
import
pymysql
pymysql.install_as_MySQLdb()
|
最后经过两条数据库迁移命令便可在指定的数据库中建立表 :
1
2
|
python manage.py makemigrations
python manage.py migrate
|
注意2:确保配置文件中的INSTALLED_APPS中写入咱们建立的app名称
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"book"
]
注意3:若是报错以下:
1
|
django.core.exceptions.ImproperlyConfigured: mysqlclient
1.3
.
3
or
newer
is
required; you have
0.7
.
11.None
|
MySQLclient目前只支持到python3.4,所以若是使用的更高版本的python,须要修改以下:
经过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql
这个路径里的文件把
1
2
|
if
version < (
1
,
3
,
3
):
raise
ImproperlyConfigured(
"mysqlclient 1.3.3 or newer is required; you have %s"
%
Database.__version__)
|
注释掉 就OK了。
注意4: 若是想打印orm转换过程当中的sql,须要在settings中进行以下配置:
添加表纪录
方式1
1
2
|
book_obj
=
Book.objects.create(title
=
"python葵花宝典"
,state
=
True
,price
=
100
,publish
=
"苹果出版社"
,pub_date
=
"2012-12-12"
)
|
方式2
1
2
|
book_obj
=
Book(title
=
"python葵花宝典"
,state
=
True
,price
=
100
,publish
=
"苹果出版社"
,pub_date
=
"2012-12-12"
)
book_obj.save()
|
查询表纪录
查询API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<
1
>
all
(): 查询全部结果
<
2
>
filter
(
*
*
kwargs): 它包含了与所给筛选条件相匹配的对象
<
3
> get(
*
*
kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,
若是符合筛选条件的对象超过一个或者没有都会抛出错误。
<
4
> exclude(
*
*
kwargs): 它包含了与所给筛选条件不匹配的对象
<
5
> order_by(
*
field): 对查询结果排序
<
6
> reverse(): 对查询结果反向排序
<
8
> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<
9
> first(): 返回第一条记录
<
10
> last(): 返回最后一条记录
<
11
> exists(): 若是QuerySet包含数据,就返回
True
,不然返回
False
<
12
> values(
*
field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后获得的并非一系列
model的实例化对象,而是一个可迭代的字典序列
<
13
> values_list(
*
field): 它与values()很是类似,它返回的是一个元组序列,values返回的是一个字典序列
<
14
> distinct(): 从返回结果中剔除重复纪录
|
基于双下划线的模糊查询
1
2
3
4
5
6
7
8
|
Book.objects.
filter
(price__in
=
[
100
,
200
,
300
])
Book.objects.
filter
(price__gt
=
100
)
Book.objects.
filter
(price__lt
=
100
)
Book.objects.
filter
(price__range
=
[
100
,
200
])
Book.objects.
filter
(title__contains
=
"python"
)
Book.objects.
filter
(title__icontains
=
"python"
)
Book.objects.
filter
(title__startswith
=
"py"
)
Book.objects.
filter
(pub_date__year
=
2012
)
|
删除表纪录
删除方法就是 delete()。它运行时当即删除对象而不返回任何值。例如:
你也能够一次性删除多个对象。每一个 QuerySet 都有一个 delete() 方法,它一次性删除 QuerySet 中全部的对象。
例如,下面的代码将删除 pub_date 是2005年的 Entry 对象:
1
|
Entry.objects.
filter
(pub_date__year
=
2005
).delete()
|
在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:
1
2
3
|
b
=
Blog.objects.get(pk
=
1
)
b.delete()
|
要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 自己。这是一种保护机制,是为了不意外地调用 Entry.objects.delete() 方法致使 全部的 记录被误删除。若是你确认要删除全部的对象,那么你必须显式地调用:
1
|
Entry.objects.
all
().delete()
|
若是不想级联删除,能够设置为:
1
|
pubHouse
=
models.ForeignKey(to
=
'Publisher'
, on_delete
=
models.SET_NULL, blank
=
True
, null
=
True
)
|
修改表纪录
1
|
Book.objects.
filter
(title__startswith
=
"py"
).update(price
=
120
)
|
此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你能够同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。
章节做业
1 图书管理系统
实现功能:book单表的增删改查
2 查询操做练习
1
2
3
4
5
6
7
8
9
|
1
查询老男孩出版社出版过的价格大于
200
的书籍
2
查询
2017
年
8
月出版的全部以py开头的书籍名称
3
查询价格为
50
,
100
或者
150
的全部书籍名称及其出版社名称
4
查询价格在
100
到
200
之间的全部书籍名称及其价格
5
查询全部人民出版社出版的书籍的价格(从高到低排序,去重)
|
实例:咱们来假定下面这些概念,字段和关系
做者模型:一个做者有姓名和年龄。
做者详细模型:把做者的详情放到详情表,包含生日,手机号,家庭住址等信息。做者详情模型和做者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个做者,一个做者也能够写多本书,因此做者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,因此出版商和书籍是一对多关联关系(one-to-many)。
模型创建以下:
from django.db import models
# Create your models here.
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",on_delete=models.CASCADE)
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()
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=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表创建多对多的关系,ManyToManyField能够建在两个模型中的任意一个,自动建立第三张表
authors=models.ManyToManyField(to='Author',)
生成表以下:





注意事项:
- 表的名称
myapp_modelName
,是根据 模型中的元数据自动生成的,也能够覆写为别的名称
id
字段是自动添加的
- 对于外键字段,Django 会在字段名上添加"_id" 来建立数据库中的列名
- 这个例子中的
CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
- 定义好模型以后,你须要告诉Django _使用_这些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加
models.py
所在应用的名称。
- 外键字段 ForeignKey 有一个 null=True 的设置(它容许外键接受空值 NULL),你能够赋给它空值 None 。
添加表纪录
操做前先简单的录入一些数据:
publish表:

author表:

authordetail表:

一对多
1
2
3
4
5
6
|
方式
1
:
publish_obj
=
Publish.objects.get(nid
=
1
)
book_obj
=
Book.objects.create(title
=
"金瓶眉"
,publishDate
=
"2012-12-12"
,price
=
100
,publish
=
publish_obj)
方式
2
:
book_obj
=
Book.objects.create(title
=
"金瓶眉"
,publishDate
=
"2012-12-12"
,price
=
100
,publish_id
=
1
)
|

核心:book_obj.publish与book_obj.publish_id是什么?
多对多
# 当前生成的书籍对象
book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)
# 为书籍绑定的作做者对象
yuan=Author.objects.filter(name="yuan").first() # 在Author表中主键为2的纪录
egon=Author.objects.filter(name="alex").first() # 在Author表中主键为1的纪录
# 绑定多对多关系,即向关系表book_authors中添加纪录
book_obj.authors.add(yuan,egon) # 将某些特定的 model 对象添加到被关联对象集合中。 ======= book_obj.authors.add(*[])
数据库表纪录生成以下:
book表

book_authors表

核心:book_obj.authors.all()是什么?
多对多关系其它经常使用API:
1
2
3
|
book_obj.authors.remove()
book_obj.authors.clear()
book_obj.authors.
set
()
|
more
基于对象的跨表查询
一对多查询(Publish 与 Book)
正向查询(按字段:publish):
1
2
3
4
|
book_obj
=
Book.objects.
filter
(pk
=
1
).first()
print
(book_obj.publish.city)
|
反向查询(按表名:book_set):
1
2
3
4
5
|
publish
=
Publish.objects.get(name
=
"苹果出版社"
)
book_list
=
publish.book_set.
all
()
for
book_obj
in
book_list:
print
(book_obj.title)
|
一对一查询(Author 与 AuthorDetail)
正向查询(按字段:authorDetail):
1
2
|
egon
=
Author.objects.
filter
(name
=
"egon"
).first()
print
(egon.authorDetail.telephone)
|
反向查询(按表名:author):
1
2
3
4
5
|
authorDetail_list
=
AuthorDetail.objects.
filter
(addr
=
"beijing"
)
for
obj
in
authorDetail_list:
print
(obj.author.name)
|
多对多查询 (Author 与 Book)
正向查询(按字段:authors):
1
2
3
4
5
6
|
book_obj
=
Book.objects.
filter
(title
=
"金瓶眉"
).first()
authors
=
book_obj.authors.
all
()
for
author_obj
in
authors:
print
(author_obj.name,author_obj.authorDetail.telephone)
|
反向查询(按表名:book_set):
1
2
3
4
5
6
|
author_obj
=
Author.objects.get(name
=
"egon"
)
book_list
=
author_obj.book_set.
all
()
for
book_obj
in
book_list:
print
(book_obj.title)
|
注意:
你能够经过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,若是 Article model 中作一下更改:
1
|
publish
=
ForeignKey(Book, related_name
=
'bookList'
)
|
那么接下来就会如咱们看到这般:
1
2
3
4
|
publish
=
Publish.objects.get(name
=
"人民出版社"
)
book_list
=
publish.bookList.
all
()
|
基于双下划线的跨表查询
Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要作跨关系查询,就使用两个下划线来连接模型(model)间关联字段的名称,直到最终连接到你想要的model 为止。
'''
正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表
'''
一对多查询
# 练习: 查询苹果出版社出版过的全部书籍的名字与价格(一对多)
# 正向查询 按字段:publish
queryResult=Book.objects
.filter(publish__name="苹果出版社")
.values_list("title","price")
# 反向查询 按表名:book
queryResult=Publish.objects
.filter(name="苹果出版社")
.values_list("book__title","book__price")
多对多查询
# 练习: 查询alex出过的全部书籍的名字(多对多)
# 正向查询 按字段:authors:
queryResult=Book.objects
.filter(authors__name="yuan")
.values_list("title")
# 反向查询 按表名:book
queryResult=Author.objects
.filter(name="yuan")
.values_list("book__title","book__price")
一对一查询
# 查询alex的手机号
# 正向查询
ret=Author.objects.filter(name="alex").values("authordetail__telephone")
# 反向查询
ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")
进阶练习(连续跨表)
# 练习: 查询人民出版社出版过的全部书籍的名字以及做者的姓名
# 正向查询
queryResult=Book.objects
.filter(publish__name="人民出版社")
.values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects
.filter(name="人民出版社")
.values_list("book__title","book__authors__age","book__authors__name")
# 练习: 手机号以151开头的做者出版过的全部书籍名称以及出版社名称
# 方式1:
queryResult=Book.objects
.filter(authors__authorDetail__telephone__regex="151")
.values_list("title","publish__name")
# 方式2:
ret=Author.objects
.filter(authordetail__telephone__startswith="151")
.values("book__title","book__publish__name")
related_name
反向查询时,若是定义了related_name ,则用related_name替换表名,例如:
1
|
publish
=
ForeignKey(Blog, related_name
=
'bookList'
)
|
# 练习: 查询人民出版社出版过的全部书籍的名字与价格(一对多)
# 反向查询 再也不按表名:book,而是related_name:bookList
queryResult=Publish.objects
.filter(name="人民出版社")
.values_list("bookList__title","bookList__price")
聚合查询与分组查询
聚合
aggregate(*args, **kwargs)
1
2
3
4
|
>>>
from
django.db.models
import
Avg
>>> Book.objects.
all
().aggregate(Avg(
'price'
))
{
'price__avg'
:
34.35
}
|
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。若是你想要为聚合值指定一个名称,能够向聚合子句提供它。
1
2
|
>>> Book.objects.aggregate(average_price
=
Avg(
'price'
))
{
'average_price'
:
34.35
}
|
若是你但愿生成不止一个聚合,你能够向aggregate()子句中添加另外一个参数。因此,若是你也想知道全部图书价格的最大值和最小值,能够这样查询:
1
2
3
|
>>>
from
django.db.models
import
Avg,
Max
,
Min
>>> Book.objects.aggregate(Avg(
'price'
),
Max
(
'price'
),
Min
(
'price'
))
{
'price__avg'
:
34.35
,
'price__max'
: Decimal(
'81.20'
),
'price__min'
: Decimal(
'12.99'
)}
|
分组
###################################--单表分组查询--#######################################################
查询每个部门名称以及对应的员工数
emp:
id name age salary dep
1 alex 12 2000 销售部
2 egon 22 3000 人事部
3 wen 22 5000 人事部
sql语句:
select dep,Count(*) from emp group by dep;
ORM:
emp.objects.values("dep").annotate(c=Count("id")
###################################--多表分组查询--###########################
多表分组查询:
查询每个部门名称以及对应的员工数
emp:
id name age salary dep_id
1 alex 12 2000 1
2 egon 22 3000 2
3 wen 22 5000 2
dep
id name
1 销售部
2 人事部
emp-dep:
id name age salary dep_id id name
1 alex 12 2000 1 1 销售部
2 egon 22 3000 2 2 人事部
3 wen 22 5000 2 2 人事部
sql语句:
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id
ORM:
dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")
View Code
annotate()为调用的QuerySet中每个对象都生成一个独立的统计值(统计方法用聚合函数)。
总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。
查询练习
(1) 练习:统计每个出版社的最便宜的书
1
2
3
|
publishList
=
Publish.objects.annotate(MinPrice
=
Min
(
"book__price"
))
for
publish_obj
in
publishList:
print
(publish_obj.name,publish_obj.MinPrice)
|
annotate的返回值是querySet,若是不想遍历对象,能够用上valuelist:
queryResult= Publish.objects
.annotate(MinPrice=Min("book__price"))
.values_list("name","MinPrice")
print(queryResult)
'''
SELECT "app01_publish"."name", MIN("app01_book"."price") AS "MinPrice" FROM "app01_publish"
LEFT JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id")
GROUP BY "app01_publish"."nid", "app01_publish"."name", "app01_publish"."city", "app01_publish"."email"
'''
(2) 练习:统计每一本书的做者个数
ret=Book.objects.annotate(authorsNum=Count('authors__name'))
(3) 统计每一本以py开头的书籍的做者个数:
queryResult=Book.objects
.filter(title__startswith="Py")
.annotate(num_authors=Count('authors'))
(4) 统计不止一个做者的图书:
queryResult=Book.objects
.annotate(num_authors=Count('authors'))
.filter(num_authors__gt=1)
(5) 根据一本图书做者数量的多少对查询集 QuerySet进行排序:
1
|
Book.objects.annotate(num_authors
=
Count(
'authors'
)).order_by(
'num_authors'
)
|
(6) 查询各个做者出的书的总价格:
# 按author表的全部字段 group by
queryResult=Author.objects
.annotate(SumPrice=Sum("book__price"))
.values_list("name","SumPrice")
print(queryResult)
F查询与Q查询
F查询
在上面全部的例子中,咱们构造的过滤器都只是将字段值与某个常量作比较。若是咱们要对两个字段的值作比较,那该怎么作呢?
Django 提供 F() 来作这样的比较。F() 的实例能够在查询中引用字段,来比较同一个 model 实例中两个不一样字段的值。
1
2
3
4
|
from
django.db.models
import
F
Book.objects.
filter
(commnetNum__lt
=
F(
'keepNum'
))
|
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操做。
1
2
|
Book.objects.
filter
(commnetNum__lt
=
F(
'keepNum'
)
*
2
)
|
修改操做也可使用F函数,好比将每一本书的价格提升30元:
1
|
Book.objects.
all
().update(price
=
F(
"price"
)
+
30
)
|
Q查询
filter() 等方法中的关键字参数查询都是一块儿进行“AND” 的。 若是你须要执行更复杂的查询(例如OR 语句),你可使用Q 对象。
1
2
|
from
django.db.models
import
Q
Q(title__startswith
=
'Py'
)
|
Q 对象可使用& 和| 操做符组合起来。当一个操做符在两个Q 对象上使用时,它产生一个新的Q 对象。
1
|
bookList
=
Book.objects.
filter
(Q(authors__name
=
"yuan"
)|Q(authors__name
=
"egon"
))
|
等同于下面的SQL WHERE 子句:
1
|
WHERE name
=
"yuan"
OR name
=
"egon"
|
你能够组合& 和| 操做符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可使用~ 操做符取反,这容许组合正常的查询和取反(NOT) 查询:
1
|
bookList
=
Book.objects.
filter
(Q(authors__name
=
"yuan"
) & ~Q(publishDate__year
=
2017
)).values_list(
"title"
)
|
查询函数能够混合使用Q 对象和关键字参数。全部提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一块儿。可是,若是出现Q 对象,它必须位于全部关键字参数的前面。例如:
1
2
3
|
bookList
=
Book.objects.
filter
(Q(publishDate__year
=
2016
) | Q(publishDate__year
=
2017
),
title__icontains
=
"python"
)
|