1.Django系列之web应用于HTTP协议
1.2 最简单的web应用程序
web应用程序指供浏览器访问的程序,通常也简称为web应用。应用程序有两种模式C/S、B/S。
C/S是客户端/服务端程序,也就是说这类程序一般独立运行。而B/S就是浏览器端/服务端应用程序,这类应用程序一般借助浏览器来运行。
web应用程序一般就是B/S模式。web应用程序首先是应用程序,和用标准的程序语言,如java,PHP,python等编写出来的程序没有什么本质上的不同。
基于socket实现一个简单的web应用程序
import socket
sock=socket.socket()
sock.bind(("127.0.0.1",8800))
sock.listen(5)
while 1:
print("server is working...")
conn,addr=sock.accept()
recv_data=conn.recv(1024)
conn.send(b"HTTP/1.1 200 OK\r\n\r\n<h1>welcom to Web!</h1>")
conn.close()
sock.close()
2 http协议
2.1 简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
2.2 http协议特性
1.基于TCP/IP协议
http协议是基于TCP/IP协议之上的应用层协议
2.基于请求-响应模式
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并返回。换句话说,肯定是先从客户端开始建立通信的,服务端在没有收到请求之前不会发送响应。
3.无状态保存
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不做持久化处理。
使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。
可是,随着Web的不断发展,因无状态而导致业务处理变得棘手 的情况增多了。比如,用户登录到一家购物网站,即使他跳转到该站的 其他页面后,也需要能继续保持登录状态。针对这个实例,网站为了能 够掌握是谁送出的请求,需要保存用户的状态。HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能, 于是引入了Cookie技术。有了Cookie再用HTTP协议通信,就可以管 理状态了。有关Cookie的详细内容稍后讲解。
4.无连接
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
2.3.http请求协议与响应协议
http协议包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的请求协议。用于HTTP协议交互的信被为HTTP报文。请求端(客户端)的HTTP报文 做请求报文,响应端(服务器端)的 做响应报文。HTTP报文本身是由多行数据构成的字文本。
1.请求协议
请求方式: get与post请求
- GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
- GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
2.响应协议
响应状态码:
状态码的职 是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户可以知道服务器端是正常 理了请求,还是出 现了 。状态码如200 OK,以3位数字和原因 成。数字中的 一位指定了响应 别,后两位无分 。响应 别有以5种。
2.Django的下载与基本命令
1.1 下载Django:
pip3 install django
#Django会安装在当前python所在的位置,如果有多个python可以安装多个Django
1.2 创建一个Django 项目
//格式
django-admin startproject 项目名称
//示例
django-admin startproject mysite
当前目录下会生成一个mysite的项目
manage.py --- Django项目里面的工具,通过它可以调用django shell和数据库等
settings.py --- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量
urls.py --- 负责把URL模式映射到应用程序
1.3 在mysite目录下创建应用
//格式
python manage.py startapp 应用名称
//示例
python manage.py startapp blog
1.4 启动Django项目
//进入到manage.py所在的文件夹下
python manage.py runserver 端口(可以不加默认8000)
//示例
python manage.py runserver
这样我们的Django项目就启动起来了,当我们访问:http://127.0.0.1:8000 时可以看到界面Django界面了
2 IED(pycharm)创建Django项目
3.基于Django实现一个简单示例
创建一个app01的项目
- 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),
]
- view视图
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})
-templates模板
//创建一个HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h4>当前时间:{{ ctime }}</h4>
</body>
</html>
执行效果如下
3.Django-urls路由层
url配置(urlconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于客户端发来的有个url调用那一段逻辑代码对应执行。
1.1 简单的路由配置
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('timer/',views.timer),
# path('articles/2003',views.specical_case_2003), #完全匹配,
re_path(r'^articles/2003/$',views.specical_case_2003), #完全匹配
#分组
re_path(r'^articles/(\d{4})/$',views.year_articles), #正则匹配,传递变量,匹配年份的变量
re_path(r'^articles/(\d{4})/(\d{1,2})/$',views.year_month_articles), #正则匹配url,传递变量,匹配年份和月份
]
'''
re_path === re.search
1.若要从url中捕获一个值,只需要在它周围放一对圆括号
2.不需要添加一个前导的反斜杠,因为每个URL都有,例如,应该是^articles 而不是^/articles.
3.每个正则表达式前面的‘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')。
1.2 有名分组
没有命名的正则表达式组(通过圆括号)来捕获url中的值并以位置参数传递给视图。在更高级的语法中,可以使用命名的正则表达式组来捕获url中的值并以关键字参数传递给视图。
在python正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。
使用命名组的重写:
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会更加明晰且不容易产生参数顺序问题的错误 -- 你可以在你的视图函数定义中重新安排参数的顺序。
1.3 分发
include()分发函数
'''
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')),
]
1.4 反向解析
在使用Django项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人民强烈希望不要硬编码这些url(费力,不可扩展且容易产生错误)或者设计一种与URLconf毫不相关的专门的URL生成机制,因为这样容易导致一定程序上产生过期的URL。
在需要URL的地方,对于不同层阶,Django提供不同的工具用于URL反查:
- 在模板中:使用url模板标签
- 在python代码中:使用from django.urls import reverse()函数
使用反向解析后,后面再修改urls里面的地址的时候,就不需要在修改其他地方的地址了,直接配置反向解析的名称
1.4.1 urls.py:中配置反向解析
#name字段标识反向解析的标识
from django.conf.urls import url
from . import views
urlpatterns = [
#...
url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
#...
]
1.4.2 应用之在模板中反向解析:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
<a href="/articles/2012/">2012 Archive</a>
1.4.3 应用之在py文本中反向解析:
from django.shortcuts import redirect
from django.urls import reverse
def redirect_to_year(request):
year = 2006
reverse_path=reverse('news-year-archive', args=(year,))
return redirect(reverse_path) # 等效 redirect("/articles/2006/")
4.views视图
1.请求对象
1.1 request对象的属性
Django将请求报文中的请求行、首部信息、内容主题封装层request类中的属性。除了特殊说明的之外,其他均为只读的。
1.request.GET
一个类似于字典的对象,包含 HTTP GET 的所有参数
1.1 request.GET.get("key")
获取请求数据的值,返回字符串
1.2 request.GET.getlist("hobby")
获取请求数据的所有值,值为一个分组
2.requestt.POST
一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成QueryDict 对象。
注意:键值对的值是多个的时候,比如chenckbox类型的input标签,select标签的,需要用request.POST.getlist("hobby")
2.1 request.POST.get("name")
获取请求数据的值,返回字符串
2.2 request.POST.getlist("hobby")
获取请求数据的所有值,值为一个分组
3.request.body
一个字符串,代表请求报文的请求体的原数据
4.request.path
一个字符串,表示请求的路劲组件(不包含域名)
例如:"/music/bands/the_beatles/"
5.request.method
一个字符串,表示请求使用的HTTP方法。必须使用大写
例如:“GET”、“POST”
6.request.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 键。
7.request.FILES
一个类似于字典的对象,包含所有的上传文件信息
FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才
会包含数据。否则,FILES 将为一个空的类似于字典的对象。
1.2 request对象的方法
1. request.get_full_path()
返回path,如果可以加上查询字符串
例如:"/music/bands/the_beatles/?print=true"
2.request.is_ajax()
如果请求是通过XMLHttpRequest 发起的,则返回True,否则返回False
3.request.get_host()
获取请求的ip+端口
4.request.get_port()
获取请求的端口
示例
from django.shortcuts import render,redirect,HttpResponse
# Create your views here.
def index(request):
#请求对象 request
#获取请求方式
print(request.method) #GET
print(request.body)
#获取请求方式请求到的数据
print("GET",request.GET) #GET <QueryDict: {'name': ['li'], 'pwd': ['1234'], 'hobby': ['lanqui', 'zuqui']}>
print("POST",request.POST)
#获取请求数据的第一个值,值为一个字符串
name = request.GET.get("name")
print("name:",name) #name: li
name = request.POST.get("name")
print("name:",name)
#获取请求数据的所有值,值为一个分组
hobby = request.GET.getlist("hobby")
print("hobby:",hobby) #hobby: ['lanqui', 'zuqui']
hobby = request.POST.getlist("hobby")
print("hobby:",hobby)
#获取请求路径
#path:获取请求的路径
print("request.path",request.path) #request.path /index
#get_full_path:获取请求路径+数据
print("request.get_full_path",request.get_full_path()) #/index?name=li&pwd=1234&hobby=lanqui&hobby=zuqui
#请求的ip+端口
print(request.get_host()) #127.0.0.1:8000
#请求的端口
print(request.get_port()) #8000
user = "li"
return render(request,"index.html",{"user":user})
def login(request):
if request.method == "GET":
return render(request,"login.html")
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
print(user,pwd)
if user == "li" and pwd == "123":
return redirect("/index/")
else:
return redirect("/login/")
2.响应对象
相应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
2.1 HttpResponse()
HttpResponse() 括号内直接跟一个具体的字符串作为响应体,比较直接
HttpResponse("123444")
2.2 render()
render(request,template_name,{,context})
#结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的HttpResponse对象。
参数
request:用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字段,
默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
render方法就是讲一个模板页面中的语法进行渲染,最终渲染成一个HTML页面最为响应体。
2.3 redirect()方法
当您使用Django框架构建python web应用程序时,您在某些时候必须将用户从一个URL重定向到另一个URL。
通过redirect方法实现重定向。
参数
一个绝对的或者相对的URL,将原封不动的作为重定向的位置。
一个url的别名:可以使用reverse来反向解析url。
传递要重定向到的一个具体的网址
def login(request):
....
....
return redirect("/login/")
传递要重定向到的一个完整的网址
def login(request):
...
...
return redirect("http://www.baidu.com")
传递一个视图的名称
def login(request):
...
return redirect(reverse("url的别名"))
示例
from django.shortcuts import render,redirect,HttpResponse
# Create your views here.
def index(request):
#请求对象 request
#获取请求方式
print(request.method) #GET
print(request.body)
#获取请求方式请求到的数据
print("GET",request.GET) #GET <QueryDict: {'name': ['li'], 'pwd': ['1234'], 'hobby': ['lanqui', 'zuqui']}>
print("POST",request.POST)
#获取请求数据的第一个值,值为一个字符串
name = request.GET.get("name")
print("name:",name) #name: li
name = request.POST.get("name")
print("name:",name)
#获取请求数据的所有值,值为一个分组
hobby = request.GET.getlist("hobby")
print("hobby:",hobby) #hobby: ['lanqui', 'zuqui']
hobby = request.POST.getlist("hobby")
print("hobby:",hobby)
#获取请求路径
#path:获取请求的路径
print("request.path",request.path) #request.path /index
#get_full_path:获取请求路径+数据
print("request.get_full_path",request.get_full_path()) #/index?name=li&pwd=1234&hobby=lanqui&hobby=zuqui
#请求的ip+端口
print(request.get_host()) #127.0.0.1:8000
#请求的端口
print(request.get_port()) #8000
user = "li"
return render(request,"index.html",{"user":user})
def login(request):
if request.method == "GET":
return render(request,"login.html")
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
print(user,pwd)
if user == "li" and pwd == "123":
return redirect("/index/")
else:
return redirect("http://www.baidu.com")
5.Django的模板层
为什么不将HTML硬编码到视图里呢?
- 对页面设计进行的任何改变都必须对python代码进行相应的修改。站点设计的修改往往比底层python代码的修改要频繁得多,因此如果可以在不进行python代码修改的情况下变更设计,那将会方便的多。
- python代码编写和HTML设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同的部门)来完成。设计者和HTML/CSS的编码人员不应该被要求去编写python的代码来完成他们的工作,
- 程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式。
python的模板:HTML代码+模板语法
def current_time(request):
# ================================原始的视图函数
# 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.1 模板语法----变量
在Django模板中遍历复杂数据结构的关键是句点符号,语法:
{{ var_name }}
示例
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
"""
<h4>{{s}}</h4> #字符串:hello
<h4>列表:{{ l.0 }}</h4> #列表:111
<h4>列表:{{ l.2 }}</h4> #列表:222
<h4>字典:{{ dic.name }}</h4> #字典:li
<h4>日期:{{ date.year }}</h4> #日期:1992
<h4>类对象列表:{{ person_list.1.name }}</h4> #类对象列表:yuan
"""
注意:句点符也可以用来引用对象的方法(无参数的方法):
<h4>字典:{{ dic.name.upper }}</h4> {# 内容变大写 #}
1.2 模板之过滤器
语法
{{obj|filter__name:param}}
default
如果一个变量时false或者为空,使用给定的默认值。否则,使用变量的值。
{{ value|default:"nothing" }}
lenth
返回值的长度。它对字符串和列表都起作用。
{{ value|length }}
filesizeformat
将值格式化为一个“人类可读的”文件尺寸(例如‘13KB’,'3.2MB')
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB
date
格式化时间
如果 value=datetime.datetime.now()
{{ value|date:"Y-m-d" }}
slice
切片取值(),最后一位取不到
{{ "hello world"|slice:"2:-1" }} #llo worl
{{ "hello world"|slice:"2:6" }} #llo
truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号需要("...")结尾。
参数:要截断的字符数
例如:
{{ value|truncatechars:9 }}
safe
Django模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义:
value="<a href="">点击</a>"
{{ value|safe}}
这里简单介绍一些常用的模板的过滤器,
1.3 模板之标签
标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或者逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模板中。一些标签需要开始和结束标签(例如{% tag %}...标签 内容...{% endtag %}).
1.3.1 for标签
//遍历每一个元素
{% for lis in lis_lis %}
<p>{{ lis.name }}</p>
{% endfor %}
//反向完成循环
{% for li in lis reversed %}
<p>{{ lis.name }}</p>
{% endfor %}
//遍历一个字典:
{% 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 %}
1.3.2 if标签
{% if %}会对一个变量求值,如果他的值是“True”(存在、不为空、且不是Boolean类型的false值),对应的内容块会输出。
num = 100
{% if num > 100 %}
<p>无效</p>
{% elif num > 80 %}
<p>优秀</p>
{% else %}
<p>一般</p>
{% endif %}
1.3.3 with
使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
1.3.4 csrf_token
这个标签用于跨站请求伪造保护
1.4 自定义标签和过滤器
1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
2、在app中创建templatetags模块(模块名只能是templatetags)
3、创建任意 .py 文件,如:my_tags.py
from django import template
from django.utils.safestring import mark_safe
register = template.Library() #register的名字是固定的,不可改变
@register.filter
def filter_multi(v1,v2):
return v1 * v2
<br>
@register.simple_tag
def simple_tag_multi(v1,v2):
return v1 * v2
<br>
@register.simple_tag
def my_input(id,arg):
result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
return mark_safe(result)
4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
{% load my_tags %}
5、使用simple_tag和filter(如何调用)
-------------------------------.html
{% load xxx %}
# num=12
{{ num|filter_multi:2 }} #24
{{ num|filter_multi:"[22,333,4444]" }}
{% simple_tag_multi 2 5 %} 参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}
注意:filter可以用在if等语句后,simple_tag不可以
{% if num|filter_multi:30 > 100 %}
{{ num|filter_multi:30 }}
{% endif %}
1.5 模板继承(extend)
Django模板引擎中最强大的也是最复杂的部分就是模板继承了。模板继承可以让您创建一个基本的“骨架”模板,它包含您站点中的全部元素,并且可以定义能够被子模板覆盖的blocks。
模板继承-示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
Title
{% endblock %}
</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
margin: 0;
padding: 0;
}
.header {
width: 100%;
height: 40px;
background-color: #369;
}
.content .left, .content .right {
float: left;
}
.content .left {
width: 20%;
background-color: lightcyan;
height: 600px;
}
.content .right {
width: 80%;
background-color: lightgray;
height: 600px;
}
.content .right .con{
padding: 10px 30px;
}
</style>
</head>
<body>
<div class="header"></div>
<div class="content">
<div class="left">
<div><a href="">图书列表</a></div>
<div><a href="">订单列表</a></div>
<div><a href="">首页</a></div>
</div>
<div class="right">
<div class="con">
{% block content %}
<h3>hello world</h3>
{% endblock content %}
</div>
</div>
</div>
</body>
</html>
这个模板,我们把它叫做base.html,它定义了一个可以用于两列排版页面的简单HTML骨架。“子模板”的工作是用他们的内容填充空的blocks.
在这个例子中,block标签定义了三个可以被字幕版内容填充的block。block告诉模板引擎:字幕版可能会覆盖掉模板中的这些位置。
子模板可能看起来是这样子的:
调用模板:{% extends "base.html" %} 必须写在首行
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
<ul>
<li>书籍1</li>
<li>书籍2</li>
<li>书籍3</li>
</ul>
{% endblock %}
extends标签是这里的关键。他告诉模板引擎,这个模板“继承”了另一个模板。当模板系统处理这个模板时,首先,它将定位父模板中--在此列中,就是“base.html”。
那时,模板引擎将注意到base.html中的三个block标签,并用子模板中的内容来替换这些block.
输出看可能是这样子的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
My amazing blog
</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
margin: 0;
padding: 0;
}
.header {
width: 100%;
height: 40px;
background-color: #369;
}
.content .left, .content .right {
float: left;
}
.content .left {
width: 20%;
background-color: lightcyan;
height: 600px;
}
.content .right {
width: 80%;
background-color: lightgray;
height: 600px;
}
.content .right .con{
padding: 10px 30px;
}
</style>
</head>
<body>
<div class="header"></div>
<div class="content">
<div class="left">
<div><a href="">图书列表</a></div>
<div><a href="">订单列表</a></div>
<div><a href="">首页</a></div>
</div>
<div class="right">
<div class="con">
<ul>
<li>书籍1</li>
<li>书籍2</li>
<li>书籍3</li>
</ul>
</div>
</div>
</div>
</body>
</html>
在子模板中没有被定义的block,系统默认使用父模板中的值。父模板标签中的内容总是被用作备选内容
这种方式使代码得到最大程度的复用,并且使得添加内容到共享的区域更加简单。例如,部分范围内的导航。
一些导航的提示
- 如果你在模板中使用{% extends %}标签,它必须是模板中的第一个标签。其他的任何情况下,模板继承都将无法工作。
- 在base模板中设置越多的{% block %}标签约好。请记住,子模板不必定义全部父模板中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的哪一个。多一点钩子总比少一点好。
- 如果你发现你自己在大量的模板中复制内容,那可能意味着你应该把内容移动到父模板中的一个{% block %}中。
- 为了更好的阅读性,你也可以给你的{% endblock %}标签一个名字。
{% block content %}
...
{% endblock content %}
在大型模板中,这个方法帮你清楚的看到哪一个{% block %}标签被关闭了
- 不能在一个模板中定义多个相同名字的block标签。
6.Django的模型层
https://www.cnblogs.com/pyedu/p/10289338.html
1.1 ORM简介
简介:
- MVC或者MVC框架中包含一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动。
- ORM是“对象-关系-映射”的简称。
使用SQL语句操作数据库
**SQL创建表**
CREATE TABLE employee(
id INT PRIMARY KEY auto_increment ,
name VARCHAR (20),
gender BIT default 1,
birthday DATE ,
department VARCHAR (20),
salary DECIMAL (8,2) unsigned,
);
**SQL中的表记录**
--插入单挑表记录
INSERT employee (name,gender,birthday,salary,department)
VALUES ("alex",1,"1985-12-12",8000,"保洁部");
python类操作
--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()
总结:ORM强调的四种映射
- python的类映射SQL中表
- 类的属性映射表的字段
- 属性赋值的约束对象映射表的字段约束
- 类对象映射表记录(这一点很关键)
1.2 单表操作
1.2.1 创建表
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)
更多字段和参数
每个字段有一些特定的参数,例如,charfield需要max_length参数来指定VACHAR
数据库字段的大小。还有一些适用于所有字段的通用参数。
<1> charField
字符串字段,用于较短的字符串。
CharField 要求必须有一个参数 maxlength
用于从数据库层和Django校验层限制该字段所允许的最大字符数。
<2> IntegerField
#用于保存一个整数
<3> FloatFiled
一个浮点数。必须 提供两个参数
参数 描述
max_digits 总位数(不包含小数点和符号)
decimal_places 小数位数
举例来说,要保留最大值为 999 (小数点后保留2位)要这样定义
models.FloatField(...,max_digits=5, decimal_places=2)
要保存最大值一百万(小数点后保存5位)的话,要这样定义:
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-modifed”时间戳
auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间。通常用于表示对象创建时间。
(仅仅在admin中有意义)
<9> DateTimeField
一个日期时间字段。类似DateField支持同样的附加选项。
更多参数
(1) null
如果为True,Django将用NULL 来数据库中存储空值。默认值是 False.
(2) blank
如果为True,该字段允许不填。默认为False。
要注意,这与null不同。null纯粹是数据库范畴的,而blank是数据验证范畴的。
如果一个字段的blank=True,单表的验证将允许该字段是空值。如果该字段的blank=False,该字段就是必填的。
(3)default
字段的默认值。可以是一个值或者可调用对象。如果可调用,每有新对象被创建他都会被调用。
(4)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django就会自动添加一个integerfield字段作为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True
(5)unique
如果改值设置为True,这个数据字段的值在整张表中必须是唯一的。
(6)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。如果设置了choices,默认的表单将是一个选择框而不是一个标准的文本框,而且这个选择框的选项就是choices中的选项。
3 setting配置
若想将木星转为mysql数据库中的表,需要在setting中配置
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__,在里面写入;
import pymysql
pymysql.install_as_MySQLdb()
最后通过两条数据库迁移命令即可在指定的数据库中创建表
`python manage.py makemigrations``python manage.py migrate`
注意2:确保配置文件中的INSTALLED_APPS中写入的使我们创建的APP名称,在主项目的
settings.py
中引入数据库所在项目的**apps.py文件的类
INSTALLED_APPS = [
...
...
"book.apps.bookConfig"
]
注意3:如果想打印orm转换过程中的SQL,需要在setting中进行如下配置:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
2.2 添加表记录
from django.shortcuts import render,HttpResponse,redirect
# Create your views here.
from app01.models import Book
def addbook(request):
#添加单条记录
# 方式一
book = Book(title="西游记",state="True",pub_date="2014-12-12",price=300)
book.save()
#方式二
#create方法的返回值book_obj就是插入表中的插入数据的记录对象
book = Book.objects.create(title="三国演义",state="True",pub_date='2020-12-21',price=1000)
print(book) #Book object (128)
print(book.title) #三国演义
print(book.pub_date) #2020-12-21
#插入多条记录
#方式一
for i in range(1,101):
Book.objects.create(title="BOOK%s"%i,state="True",pub_date="2000-12-12",price=200)
#方式二
booklist = []
for i in range(101,201):
book = Book(title="BOOK%s"%i,state="True",pub_date="2014-12-12",price=300)
booklist.append(book)
Book.objects.bulk_create(booklist)
return HttpResponse("添加成功")
# return redirect("/index/")
2.3 查询集:queryset
queryset是一种类似于列表一样的,可遍历的数据类型,一个queryset对象可以包含一个或者多个元素,每个元素都一个model类实例对象。queryset类型封装了很多方法,用来管理model实例对象。
#BOOK是关于数据的模型类
queryset=Book.objects.all() #返回值就是一个queryset对象
print(quertset) #<QuerySet [<Book: python>, <Book: linux>]>
可以看出,queryset对象很像python的列表数据库类型,但是列表类封装append,sort等方法是用来管理列表中的元素的,而queryset类封装的方法是用来管理模型类对象的,可以排序,过滤,分组等等。
2.3.1 queryset特性之可切片
使用python的切片语法来限制查询集
记录的数目。它等同于SQL的LIMIT
和OFFSET
字句。
>>> Entry.objects.all()[:5] # (LIMIT 5)
>>> Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5)
不支持负的索引(例如Entry.objects.all()[-1])。通常,查询集的切片返回一个新的查询集对象。
2.3.2 queryset特性之可迭代
queryset=Book.objects.all() # 返回值就是一个QuerySet对象
for book in queryset: # queryset对象中包含的是查询出来的每一个书籍模型类对象,即书籍表中的每一条记录
print(book.title,book.price)
2.4 查询集的API
1. all():
查询模型表所有记录 返回queryset对象
2. filter(**kwargs):
查询与所给筛选条件相匹配的对象>>>返回queryset对象
3. get(**kwagrs):
返回与所给筛选条件相匹配的对象,返回结果有且只有一个,
如果符合筛选条件的对象超过一个或者没有都会抛出错误。
>>>返回模型类对象
4.exclude(**kwargs):
它包含了与所给筛选条件不匹配的对象>>>返回queryset对象
5.order_by(*field):
对查询结果排序>>>返回queryset对象
6.reverse():
对查询结果反向排序>>>返回queryset对象
7.count():
返回数据库中匹配查询(queryset)的对象数量。>>>返回int对象
8.first():
返回第一条记录>>>返回模型类对象
9.last():
返回最后一条记录>>>返回模型类对象
10.values(*field):
返回一个valuequeryset:一个特殊的queryset,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列>>>返回queryset对象
11.values_list(*field):
它与values()非常相似,它返回的是一个元组序列,values返回的值是字典序列 >>> 返回一个queryset对象
12. distinct():
从返回结果中剔除重复记录>>>返回queryset对象。
关键点:区分queryset与模型类对象的关系,每一个API的调用对象和返回对象;最后queryset支持链式操作。
示例
def select(request):
"""
#(1) all() :返回queryset
#查询所有书籍
booklist = Book.objects.all()
print(booklist,type(booklist)) #<QuerySet [<Book: Book object (127)>, <Book: Book object (128)>, <Book: Book object (129)>, <Book: Book object (130)>]> <class 'django.db.models.query.QuerySet'>
for book in booklist:
print(book) #Book object (127)
print(book.title) #西游记
# (2) filter():返回queryset对象
#查询某一本书籍
booklist = Book.objects.filter(price=300)
print(booklist[0].title) #西游记
#(3)first() 返回model对象
# 查询第一条数据
book = Book.objects.first()
print(book.title) #西游记
#(4) last() 返回model对象
#查询最后一条记录
book = Book.objects.last()
print(book.title) #BOOK2
#(5) get() :等同于filter,返回model对象
#返回与所给筛选条件相似的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误
book = Book.objects.get(price=200)
print(book.title) #西游记
#(6)exclude() :排除符合条件的;返回queryset对象
booklist = Book.objects.exclude(price=300)
print(booklist[0].title) #三国演义
#(7) order_by :queryset对象调用 返回queryset对象
#排序,加-为倒序
booklist = Book.objects.all().order_by("-price","id")
print(booklist[0].title)
`
#(8) reverse() queryset对象调用 返回queryset对象
#翻转
#(9) count() :返回int类型
#统计查询出来的条数
count = Book.objects.count()
print(count) #4
#(10) value() :返回queryset对象 [{},{},{}]
booklist = Book.objects.all().values("title","price")
print(booklist) #<QuerySet [{'title': '西游记', 'price': Decimal('300.00')}, {'title': '三国演义', 'price': Decimal('1000.00')}, {'title': 'BOOK1', 'price': Decimal('100.00')}, {'title': 'BOOK2', 'price': Decimal('200.00')}]>
#(11) values_list() :返回queryset对象
booklist = Book.objects.all().values_list("title","price")
print(booklist) #<QuerySet [('西游记', Decimal('300.00')), ('三国演义', Decimal('1000.00')), ('BOOK1', Decimal('100.00')), ('BOOK2', Decimal('200.00'))]>
#(12) distinct()
#从返回结果中剔除重复纪录
booklist = Book.objects.values("pub_date").distinct()
print(booklist)
"""
return HttpResponse("查询成功")
2.5 模糊查询
1. __in=[x,y] #或者
2. __gt= #大于
__gte #大于等于
3. __lt #小于
4. __range=[x,y] #匹配x到y之间的数字
5. __contains="" #包含
6. __icontains="" #包含,不区分大小写
7. __startswith="" #以...开头的字符串
8. __endswith="" #以...结尾的字符串
9. __date__year= #匹配年份的字符串
10. 逗号(,) —#并且,and的意思
示例
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book
def select2(request):
#模糊查询
booklist = Book.objects.filter(price__gte=200)
print("大于等于:",booklist)
#__range=[] 范围查找
booklist = Book.objects.filter(price__range=[199,301])
print("范围:",booklist)
#__in=[] 匹配列表中包含的值
booklist = Book.objects.filter(price__in=[100,150,300,350])
print("包含的值",booklist)
#__startswith= 查询以...开头的字符串
booklist = Book.objects.filter(title__startswith="西")
print("以..开头",booklist)
#__endswith= 查询以...结尾的字符串
booklist = Book.objects.filter(title__endswith="记")
print("以..结尾:",booklist)
#__contains 查询包含该字符串的值
booklist = Book.objects.filter(title__contains="西")
print("包含字段",booklist)
#__year 查询该年份的数据
booklist = Book.objects.filter(pub_date__year=2014)
print("年份",booklist)
#SQL中and用逗号(,)隔开表示 并且
booklist = Book.objects.filter(price__gt=100,title__startswith="西")
print("并且:",booklist) #[<Book: Book object (127)>]>
return HttpResponse("查询成功")
2.6 更新表记录
def update(request):
#更新表数据
#(1) 方式1
book = Book.objects.get(title="西厢记")
book.title = "xxx"
book.price = 666
book.save()
# (2) 方式2
n = Book.objects.filter(price=100).update(price=999)
print("n",n) #n 1 返回修改的状态
return HttpResponse("修改成功")
此外,update()方法对于任何结果集(queryset)均有效,这就意味着可以同时更新多条记录 update()方法会返回一个整型数值,表示受影响的记录条数
2.7 删除表记录
删除方法就是delete(),它运行时立刻删除对象而不返回任何值。
model_obj.delete()
也可以一次性删除多个对象。每个queryset都有一个delete()方法。他一次性删除queryset中所有的对象。
Entry.objects.filter(pub_date__year=2005).delete()
在Django删除对象时,会模仿SQL约束ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与他相关联的外键对象
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
要注意的是:delete()方法是queryset上的方法,但并不适合于manage本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致所有的记录被误删除。如果你确定要删除所有的对象,那么必须显示的调用:
Entry.objects.all().delete()
如果不想级联删除,可以设置为:
pubHouse = models.ForeignKey(to='Publisher',
on_delete=models.SET_NULL,
blank=True,
null=True
)
示例
def delete(request):
#(1) queryset.delete() 删除多个内容,返回删除的个数
n = Book.objects.filter(title__startswith="BOOK").delete()
print("n",n)
#(2) modelobj.delete() 删除单挑数据,返回产出的个数
ret = Book.objects.get(pk=128).delete()
print("ret",ret) #ret (1, {'app01.Book': 1})
return HttpResponse('删除成功!!!')
练习
在web界面对书籍进行增删改查操作
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)
pub_date = models.DateField()
price = models.DecimalField(max_digits=8, decimal_places=2)
init 调用PyMySQL
import pymysql
pymysql.install_as_MySQLdb() #将MySQLdb转换为pymysql
urls.py - url控制层
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path("",views.index),
path("addbook/",views.addbook),
re_path("book/delete/(\d+)/",views.delete_book),
re_path("book/edit/(\d+)/",views.edit_book),
]
views.py - 视图层
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book
#首页
def index(request):
booklist = Book.objects.all()
return render(request,"index.html",{"booklist":booklist})
#添加表记录
def addbook(request):
if request.method == "GET":
return render(request, "addbook.html")
else:
# 获取用户提交数据
# title = request.POST.get("title")
# price = request.POST.get("price")
# pub_date = request.POST.get("pub_date")
# 添加记录
# Book.objects.create(title=title,price=price,pub_date=pub_date)
print("request.POST", request.POST.dict())
Book.objects.create(**request.POST.dict()) #需要与数据库字段保持一致
return redirect("/")
#编辑数据,更新数据
def edit_book(request,edit_id):
if request.method == "GET":
edit_book = Book.objects.get(pk=edit_id)
return render(request,"edit_book.html",{"edit_book":edit_book})
else:
#获取提交数据
title = request.POST.get("title")
price = request.POST.get("price")
pub_date = request.POST.get("pub_date")
Book.objects.filter(pk=edit_id).update(title=title,state=1,price=price,pub_date=pub_date)
return redirect("/")
#删除数据
def delete_book(request,del_id):
print(del_id)
Book.objects.get(pk=del_id).delete()
return redirect("/")
templates - 模板层
index.html - 首页模板
{% extends "base.html" %}
{% block content %}
<a href="/addbook/" class="btn btn-primary">添加书籍</a>
<p>
<table class="table table-striped table-bordered ">
<tr>
<th>序号</th>
<th>书籍名称</th>
<th>价格</th>
<th>出版日期</th>
<th>操作</th>
</tr>
{% for book in booklist %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ book.title }}</td>
<td>{{ book.price }}</td>
<td>{{ book.pub_date|date:'Y-m-d' }}</td>
<td>
<a href="/book/edit/{{ book.pk }}">编辑</a>
<a href="/book/delete/{{ book.pk }}">删除</a>
</td>
</tr>
{% endfor %}
</table>
</p>
{% endblock %}
base.html 继承模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<div class="panel panel-warning">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-success">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
<div class="col-md-9">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
addbook.html 添加模板
{% extends "base.html" %}
{% block content %}
<h3>添加页面</h3>
<div class="row">
<div class="col-md-6">
<form action="/addbook/" method="post">
<div class="form-group">
<label for="title">书籍名称</label>
<input type="text" name="title" class="form-control" id="title">
</div>
<div class="form-group">
<label for="price">书籍价格 </label>
<input type="text" name="price" class="form-control" id="price">
</div>
<div class="form-group">
<label for="date">出版日期 </label>
<input type="date" name="pub_date" class="form-control" id="date">
</div>
<div class="pull-right">
<input type="submit" class="btn btn-success">
</div>
</form>
</div>
</div>
{% endblock %}
edit_book.html 修改模板
{% extends "base.html" %}
{% block content %}
<h3>编辑页面</h3>
<div class="row">
<div class="col-md-6">
<form action="" method="post">
<div class="form-group">
<label for="title">书籍名称</label>
<input type="text" name="title" class="form-control" id="title" value="{{ edit_book.title }}">
</div>
<div class="form-group">
<label for="price">书籍价格 </label>
<input type="text" name="price" class="form-control" id="price" value="{{ edit_book.price }}">
</div>
<div class="form-group">
<label for="date">出版日期 </label>
<input type="date" name="pub_date" class="form-control" id="date" value="{{ edit_book.pub_date|date:'Y-m-d' }}">
</div>
<div class="pull-right">
<input type="submit" class="btn btn-success">
</div>
</form>
</div>
</div>
{% endblock %}
7.Django-model
https://www.cnblogs.com/yuanchenqi/articles/7552333.html
ORM
创建表(建立模型)
添加表记录
查询表记录
修改表记录
删除表记录
1. ORM
映射关系
表名 --- 类名
字段 --- 属性
表记录 --- 类实例对象
2实例
2.1 字段选项
每个字段有一些特有的参数,例如,charfile需要max_length参数来指定varchar数据库字段的大小。还有一些使用与所有字段的通用参数。这些参数在文档中详细的定义。
(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 ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。
这是一个关于 choices 列表的例子:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
一旦建立好数据模型后,Django会自动生成一套数据库抽象的API,可以让你执行关于表记录的增删改查的操作。
1、模型图解
创建表(跨表)
from django.db import models
#学生类
class Student(models.Model):
id = models.AutoField(primary_key=True)
sname = models.CharField(max_length=20)
# 一对多外键设置,'多'的模型类设置外键,注意需要带参数on_delete
cid = models.ForeignKey('Class',on_delete=models.CASCADE)
# 一对一外键设置,哪个模型设置外键都可以,注意需要带参数on_delete
detail = models.OneToOneField('StudentDetail',on_delete=models.CASCADE)
#学生信息类
class StudentDetail(models.Model):
id = models.AutoField(primary_key=True)
height = models.IntegerField()
email = models.EmailField()
memo = models.TextField(max_length=100)
#班级类
class Class(models.Model):
id = models.AutoField(primary_key=True)
cname = models.CharField(max_length=20)
cdata = models.DateField()
#老师类
class Teacher(models.Model):
id = models.AutoField(primary_key=True)
tname = models.CharField(max_length=20)
#多对多外键设置,哪个模型类创建外键都可以,注意没有on_delete参数
cid = models.ManyToManyField(Class)
#注意:设置外键的方法中,第一个参数为外键对应的类名,一般加上引号。如果不加引号,那该类必须创建在主表前面
一对一、一对多、多对多的正向反向查询
from django.shortcuts import render,HttpResponse
from django.db.models import F,Q #测试学习F,Q查询
from app1.models import Student,StudentDetail,Class,Teacher
# Create your views here.
def index(request):
#一对一 Student ---> StudentDetail
#正向
print(Student.objects.filter(id__gt=2)[0].detail) #调用学生对象的detail属性,该属性表示外键映射副表中对应的对象
print(StudentDetail.objects.filter(student__id__gt=1)) #过滤学生信息表查询数据,过滤参数为 "主表名__条件"
#反向
print(StudentDetail.objects.get(id=1).student) #这个同上--但是副表要调用对应主表的信息对象,需要使用 .主表名(全小写)
print(Student.objects.filter(detail__id__exact=1)) #同上 -- 主表调用副表直接使用添加外键的属性detail就行,这个
# 属性就是副表中对应的一条数据对象
#一对多 Student ---> Class
#正向:
print(Student.objects.get(id=1).cid) #使用主表添加的外键属性,获取副表中的对应数据对象
print(Class.objects.filter(student__id=1)) #副表过滤主表条件的参数,使用student可以调用主表对象,student__id
# 是调用的student对象的id进行筛选
# 反向
print(Class.objects.get(id=1).student_set.all()) #副表调用主表信息,使用 主表名(全小写)__set ,这个其实是
# 一个副表对象在主表中的映射管理器,类似Student.objtects中的objects,这儿objects是控制管理器
print(Student.objects.filter(cid__id=1)) #这个同上
#多对多 Teacher --> Class
# 正向:
print(Teacher.objects.get(id=1).cid.all()) #多对多其实可以对应一对多,两者大体一致,只不过主表的外键属性和副表
# 的teacher__set都是相应的映射管理器,它内部其实都是对应的中间表的进行的关联映射
print(Class.objects.filter(teacher__id=1))
#反向
print(Class.objects.get(id=1).teacher_set.all())
print(Teacher.objects.filter(Q(cid__id=1) | ~Q(cid__id=2))) #这儿使用到了Q查询,当使用或的条件查询语句时使用,并
# 且F,Q查询与关键字参数一起使用时,F,Q查询必须写到关键字参数前面
#补充============
#多对多关系模型添加移除中间表关系 Teacher -- >Class
#本质是给中间表添加,移除数据
#正向:
# 添加
Teacher.objects.get(id=5).cid.add(*Class.objects.filter(id__gt = 3))
#移除
Teacher.objects.get(id=5).cid.remove(*Class.objects.filter(id__gt=1))
#清空对象关系
Teacher.objects.get(id=5).cid.clear()
#重新设置关系 #添加新关系,删除多余关系
Teacher.objects.get(id=5).cid.set(list(Class.objects.filter(id__gt=5)) )#参数为一个可迭代对象就可以
return HttpResponse('success')
2. 添加表记录
一对一,一对多添加
def add(request):
#单表添加
# AuthorDetail.objects.create(birthday="1990-2-2",telephone=1233,addr="bj")
# AuthorDetail.objects.create(birthday="2012-12-12",telephone=110,addr="bj")
#一对多添加,给书记表添加一条数据,并且绑定出版商
#方式1 基于对象去添加
# pub_obj = Publish.objects.get(pk=2)
# Book.objects.create(title="西游记",price = 122,publishDate="2012-12-12",publish=pub_obj)
#
# #方式2 #直接添加
# Book.objects.create(title="三国演义", price=345, publishDate="2012-12-12", publish_id=1)
# Book.objects.create(title="西厢记", price=777, publishDate="2020-12-12", publish_id=2)
#跨表 一对一添加
# Author.objects.create(name="alvin",age=23,ad_id=4)
return HttpResponse("添加成功")
多对多添加 add(obj1[,obj2...])
把指定的模型对象添加到关联对象集中
>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.add(e)
在上面的例子中,对于foreignkey关系,e.sace()有关联器调用,执行更新操作。然而,在多对多关系中使用add()并不会调用任何save()方法,而是由QuerySet.bulk_create()创建关系。
1. *[] 的使用
>>> book_obj = Book.objects.get(id=1)
>>> author_list = Author.objects.filter(id__gt=2)
>>> book_obj.authors.add(*author_list)
2. 直接绑定主键
book_obj.authors.add(*[1,3]) # 将id=1和id=3的作者对象添加到这本书的作者集合中
# 应用: 添加或者编辑时,提交作者信息时可以用到.
示例
#多对多添加记录
#给西游记添加yuan和alvin两个做个作者
# author1 = Author.objects.get(name="yuan")
# author2 = Author.objects.get(name="alvin")
# book = Book.objects.get(title="西游记")
# book2 = Book.objects.get(title="三国演义")
# book.authors.add(author1,author2)
# book2.authors.add(*[author1,author2])
创建 create(**kwargs)
创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象
示例
#一对多添加,给书记表添加一条数据,并且绑定出版商
#方式1 基于对象去添加
# pub_obj = Publish.objects.get(pk=2)
# Book.objects.create(title="西游记",price = 122,publishDate="2012-12-12",publish=pub_obj)
#
# #方式2 #直接添加
# Book.objects.create(title="三国演义", price=345, publishDate="2012-12-12", publish_id=1)
# Book.objects.create(title="西厢记", price=777, publishDate="2020-12-12", publish_id=2)
#跨表 一对一添加
# Author.objects.create(name="alvin",age=23,ad_id=4)
删除 remove(obj[,obj,...])
从关联对象集中移除执行的模型对象。
对于Foreignkey对象,这个方法仅在null=True时存在
多对多删除
### 清空 clear()
从关联对象集中移除一切对象
注意:这样不会删除对象,只会删除他们之间的关联。
就像 remove() 方法一样,clear()只能在 null= True的foreignkey上被调用
### set()方法
先清空,再设置,编辑书籍时即可用到。
注意:
对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
直接赋值
通过赋值一个新的可迭代的对象,关联对象集可以被整体替换掉
>>> new_list = [obj1, obj2, obj3]
>>> e.related_set = new_list
如果外键关系满足null=True,关联管理器会添加new_list中的内容之前,首先调用clear()方法来解除关联集中一切已存在对象的关联。否则,new_list中的对象会在已存在的关联的基础上被添加。
3.查询表记录
查询相关API
<1> all()
查询所有结果
<2> filter(**kwargs)
它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs)
返回与所给筛选对象相匹配的对象,返回结果有且只有一个
如果符合筛选条件的对象超过一个或者也没有,都会跑出错误
<4> values(*field)
返回一个valuesqueryset一个特殊的queryset,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列。
<9> values_list(*field)
对查询结果排序
<5> exclude(**kwargs)
它包含了与所给筛选条件不匹配的对象
<6> order_by(*field)
对查询结果排序
<7> reverse()
对查询结果反向排序
<8> distinct()
从返回结果中剔除重复记录
<10> count()
返回数据库中匹配查询(queryset)的对象数量。
<12> first()
返回第一条记录
<13> exists()
如果queryset包含数据,就返回True,否则返回False
注意:一定区分object与queryset的区别!!
跨表查询-示例
def select(request):
'''
两个关联表之间的关联属性所在表查询另一张表信息,称之为正向查询,反之,反向查询
基于对象查询:
正向查询按字段,反向查询按表名。
'''
#查询主键为2的书籍对象
# book = Book.objects.get(pk=2) #返回查询对象
# print(book.title) #查询对象的子弹 三国演义
# print(book.price)
# print(book.publishDate)
# print(book.publish_id)
# print(book.publish) #打印关联字段的属性对象
# print(book.publish.name) #查询关联字段的属性的字段值
# ****基于对象查询(子查询)*****
#查询西游记的出版社的名字
# book = Book.objects.get(title="西游记")
# print(book.publish.name)
#查询西瓜出版社出版的书籍的名称
# pub = Publish.objects.get(name="西瓜出版社")
# books = pub.booklist.all()
# print(books.values("title")) #<QuerySet [{'title': '三国演义'}]>
#查询西游记的所有作者的名字
# book = Book.objects.get(title="西游记")
# books = book.authors.all()
# print(books.values("name")) #<QuerySet [{'name': 'yuan'}, {'name': 'alvin'}]>
#查询yuan出版过的所有书籍
# author = Author.objects.get(name="yuan")
# books = author.booklist.all()
# print(books.values("title")) #<QuerySet [{'title': '西游记'}, {'title': '三国演义'}]>
#查询yuan的手机号
# author = Author.objects.get(name="yuan")
# print(author.ad.telephone) #120
#查询手机号为110的作者名字
# ad = AuthorDetail.objects.get(telephone=110)
# print(ad.author.name) #li
#*****基于下划线查询(join查询)*********
#查询西游记
return HttpResponse("查询成功")
双下划线之单标查询(join查询)
双下划线 == join
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
startswith,istartswith, endswith, iendswith
示例
#*****基于下划线查询(join查询)*********
# #查询西游记的出版社的名字
# #正向查询
#
# ret = Book.objects.filter(title="西游记").values("publish__name")
# print(ret) #<QuerySet [{'publish__name': '菠萝出版社'}]>
# #反向查询
# ret = Publish.objects.filter(booklist__title="西游记").values("name")
# print(ret) #<QuerySet [{'name': '菠萝出版社'}]>
#
#查询id>1的出版社出版的书籍名称
#反向查询
# ret = Publish.objects.filter(pk__gte=1).values("booklist__title")
# print(ret) #<QuerySet [{'booklist__title': '三国演义'}, {'booklist__title': '西游记'}, {'booklist__title': '西厢记'}, {'booklist__title': None}]>
# #正向查询
# ret = Book.objects.filter(publish__pk__gte=1).values("title")
# print(ret) #<QuerySet [{'title': '三国演义'}, {'title': '西游记'}, {'title': '西厢记'}]>
# #查询西游记所有作者的名字
# ret = Book.objects.filter(title="西游记").values("authors__name")
# print(ret) #<QuerySet [{'authors__name': 'yuan'}, {'authors__name': 'alvin'}]>
# ret = Author.objects.filter(booklist__title="西游记").values("name")
# print(ret) #<QuerySet [{'name': 'yuan'}, {'name': 'alvin'}]>
#查询yuan出版过的所有书籍
# ret = Book.objects.filter(authors__name="yuan").values("title")
# print(ret) #<QuerySet [{'title': '西游记'}, {'title': '三国演义'}]>
# ret = Author.objects.filter(name="yuan").values("booklist__title")
# print(ret) #<QuerySet [{'booklist__title': '西游记'}, {'booklist__title': '三国演义'}]>
#查询yuan的手机号
# ret = Author.objects.filter(name="yuan").values("ad__telephone")
# print(ret) #<QuerySet [{'ad__telephone': 120}]>
# ret = AuthorDetail.objects.filter(author__name="yuan").values("telephone")
# print(ret) #<QuerySet [{'telephone': 120}]>
#查询手机号为110的作者的名字
# ret = Author.objects.filter(ad__telephone=110).values("name")
# print(ret) #<QuerySet [{'name': 'li'}]>
# ret = AuthorDetail.objects.filter(telephone=110).values("author__name")
# print(ret) #<QuerySet [{'author__name': 'li'}]>
#查询手机号1233的作者出版过的所有书籍以及出版社名称
# ret = Book.objects.filter(authors__ad__telephone=1233).values("title","publish__name")
# print(ret) #<QuerySet [{'title': '西游记', 'publish__name': '菠萝出版社'}, {'title': '三国演义', 'publish__name': '西瓜出版社'}]>
# Author.objects.filter(ad__telephone=1233).values("booklist__title","booklist__publish__name")
# print(ret) #<QuerySet [{'title': '西游记', 'publish__name': '菠萝出版社'}, {'title': '三国演义', 'publish__name': '西瓜出版社'}]>
聚合查询与分组查询
聚合:aggregate(*args,**kwargs)
# #查询书籍的最高价格
# ret = Book.objects.all().aggregate(m_price=Max("price"))
# print(ret) #{'m_price': Decimal('777')}
aggregate()是queryset的一个终止字句,意思就是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果想要为聚合值指定一个名称,可以向聚合字句提供它。
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
如果你希望生成不止一个聚合,可以向aggregate()字句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询
>>> 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')}
分组:annotate()
Count() 统计个数
Min() 最小值
Max() 最大值
Sum() 求和
Avg() 平均值
为调用的queryset
中的每一个对象都生成一个独立的统计值(统计方法用聚合函数)
统计每一本书的作者个数
bookList=Book.objects.annotate(authorsNum=Count('authors'))
for book_obj in bookList:
print(book_obj.title,book_obj.authorsNum)
解析:
'''
Book.objects.annotate(authorsNum=Count('authors'))
拆分解析:
Book.objects等同于Book.objects.all(),翻译成的sql类似于: select id,name,.. from Book
这样得到的对象一定是每一本书对象,有n本书籍记录,就分n个组,不会有重复对象,每一组再由annotate分组统计。'''
from django.db.models import Avg,Max,Min,Count,Sum
#
# #查询每一个出版社出版过的书籍的平均价格
# ret = Book.objects.all().values("publish_id").annotate(Avg("price"))
# print(ret) #<QuerySet [{'publish_id': 1, 'price__avg': Decimal('345')}, {'publish_id': 2, 'price__avg': Decimal('449.5')}]>
#
#
# #查询每一个出版社的名称以及出版过的书籍的平均价格
# #方式一
# ret = Book.objects.all().values("publish__name").annotate(avg_price=Min("price"))
# print(ret) #<QuerySet [{'publish__name': '菠萝出版社', 'avg_price': Decimal('122')}, {'publish__name': '西瓜出版社', 'avg_price': Decimal('345')}]>
# #方式二
# ret = Publish.objects.values(avg_price=Avg("booklist__price")).values("name","avg_price")
# print(ret) #<QuerySet [{'name': '西瓜出版社', 'avg_price': Decimal('345')}, {'name': '菠萝出版社', 'avg_price': Decimal('449.5')}, {'name': '猕猴桃出版社', 'avg_price': None}]>
# #查询每一个作者出版过的书籍的个数
# ret = Author.objects.all().annotate(c=Count("booklist")).values("name","c")
# print(ret) #<QuerySet [{'name': 'yuan', 'c': 2}, {'name': 'li', 'c': 0}, {'name': 'wang', 'c': 0}, {'name': 'alvin', 'c': 2}]>
#
# #查询出版书籍籍个数大于1的作者的名字以及出版书籍的个数
# ret = Author.objects.all().annotate(c=Count("booklist")).filter(c__gt=1).values("name","c")
# print(ret) #<QuerySet [{'name': 'yuan', 'c': 2}, {'name': 'alvin', 'c': 2}]>
#
# #统计每个出版社最便宜的书
# ret = Publish.objects.all().annotate(m=Min("booklist__price")).values("name","m")
# print(ret) #<QuerySet [{'name': '西瓜出版社', 'm': Decimal('345')}, {'name': '菠萝出版社', 'm': Decimal('122')}, {'name': '猕猴桃出版社', 'm': None}]>
#
# #统计每一本书籍的作者的人数
# ret = Book.objects.all().annotate(c=Count("authors")).values("title","c")
# print(ret) #<QuerySet [{'title': '三国演义', 'c': 2}, {'title': '西游记', 'c': 2}, {'title': '西厢记', 'c': 0}]>
#
# #统计不止一个作者的书籍
# ret = Book.objects.all().annotate(c=Count("authors")).filter(c__gt=1).values("title","c")
# print(ret) #<QuerySet [{'title': '三国演义', 'c': 2}, {'title': '西游记', 'c': 2}]>
#
# #根据一本图书作者数量的多少进行排序,queryset进行排序
# ret = Book.objects.all().annotate(c=Count("authors")).order_by("title","c")
# print(ret) #<QuerySet [<Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (1)>]>
#
# #查询各个作者的书的总价格
# ret = Author.objects.all().annotate(sum_price=Sum("booklist__price")).values("name","sum_price")
# print(ret) #<QuerySet [{'name': 'yuan', 'sum_price': Decimal('467')}, {'name': 'li', 'sum_price': None}, {'name': 'wang', 'sum_price': None}, {'name': 'alvin', 'sum_price': Decimal('467')}]>