15:31:14一.web框架
1.框架:即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演。对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
2.Web应用:就是先把HTML用文件保存好,用一个现成的HTTP服务器软件(apache/nginx),接收用户请求,从文件中读取HTML,返回。做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。
3.接口:是WSGI(Web服务器网关接口协议):Web Server Gateway Interface。
二.MVC和MTV模式
1.MVC模式:MVC就是把web应用分为模型(M),控制器(Controller),视图(V)三层;他们之间以一种插件似的,松耦合的方式连接在一起。
(1)模型(Model):模型负责业务对象与数据库的对象(ORM)
(2)页面(View):负责与用户的交互(页面),一个个html的文件
(3)控制器(Controller):接受用户的输入调用模型和视图完成用户的请求。
2.MTV模式(Django):本质上与MVC模式没有什么差别,也是各组件之间为了保持松耦合关系,只是定义上有些许不同代表:
(1)Model(模型):负责业务对象与数据库的对象(ORM)
(2)Template(模版):负责如何把页面展示给用户,一个个html的文件
(3)View(视图):负责业务逻辑,并在适当的时候调用Model和Template
(4)Django有一个url(控制器):它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
3.MTV模式访问流程:
用户输入URL--->根据URL匹配相应视图函数view.py,视图函数view.py可以去models数据库里取数据把要展示的东西返回成Template模版(HTML)给用户看,也可以直接把要展示东西返回成Template模版(HTML)给用户看
4.django的请求生命周期是什么
当用户在浏览器上点url之后在django后台都发生了什么。
第一步:客户端首先把请求(字符串包括请求头和请求体)建立起来发给服务器端
第二步:服务端根据发来的url来定位该做什么,如果传来的数据如果是GET方式传来数据只放在url上用reqeust.GET从请求头里取,请求体里是空的。如果是POST方式传来的就用reqeust.post方式从请求体里取,请求头url里也可以有数据。请求体默认是reqeust.body(原生字符串内容),django在内部把reqeust.body转换成requst.post(字典)。到达路由映射urls.py。
第三步:路由映射里进行匹配(从上到下匹配,如果第一个成功了就不进行匹配),匹配成功触发函数views.py,匹配不成功报错404。有俩种对应方式:1.一个URL对应一个函数这种模式叫FBV模式(视图views里用函数处理用户逻辑请求)。2.一个URL对应一个类这种模式叫CBV模式。如果是类模式在django里执行一个dispatch方法,dispatch方法再根据method通过反射执行GET或POST方法。
第四步:业务处理-根据个人需求自定比如操作数据库(原生SQL,Django ORM),产生结果返回给用户
第五步:返回给用户结果响应内容,包含响应头和响应体
三.django的流程和命令行工具
1.django项目实现流程
(1)安装django
命令:pip3 install django
(2)创建django项目
语法:django-admin startproject 项目名称
创建语句:django-admin startproject mysite
mysite项目目录里包括
---mysite(全局内容)
---settings.py(django相关的所有配置信息)
---url.py(路径分发)
---wsgi.py(协议)
---- manage.py(启动文件)
(3)创建一个应用
在项目目录mysite里创建应用
语法:python manage.py startapp 应用名称
创建语句:python manage.py startapp blog
blog应用目录里包括
---blog(blog应用)
---__init__.py
---admin.py(django后台管理系统)
---apps.py
---models.py(数据库相关-ORM框架内容)
---tests.py(测试用)
---views.py(视图,放函数)
---migrations
---__int__.py
(4)启动django项目:
命令:python manage.py runserver 8080
2.通过django实现一个简单静态应用
当输入http://127.0.0.1:8080/show_time/返回当前为红色字体的时间
(1)在mysite项目目录下blog应用里创建static目录(放静态文件jquery-3.1.1.js)
(2)在全局配置文件里mysite/settings.py配置:
INSTALLED_APPS = [ #所有应用的名称 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', "blog", #添加blog应用名称 ]
#让django能找到static目录的绝对路径取个别名叫static,用这个代替下面的这个 STATIC_URL = '/static/' #设定django能找到static目录的绝对路径 STATICFILES_DIRS=( os.path.join(BASE_DIR,"blog/static"),)
(3)静态文件交由Web服务器处理,Django本身不处理静态文件。简单的处理逻辑如下(以nginx为例):
URI请求----->按照Web服务器里面的配置规则先处理,以nginx为例,主要请求配置在nginx.conf里的location
----->如果是静态文件,则由nginx直接处理
----->如果不是则交由Django处理,Django根据urls.py里面的规则进行匹配
全局url(控制器)配置:mysite\urls.py
from django.conf.urls import url from django.contrib import admin from blog import views #导入blog应用下的views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^show_time/',views.show_time) #对应视图函数views.show_time处理/show_time这个路径 ]
(4)views视图函数配置:mysite\blog\views.py
from django.shortcuts import render,HttpResponse #引入HttpResponse对象(服务器把这个对象通过HTTP协议把它发给浏览器) import time def show_time(requset): #requset形参:浏览器发送给服务器,之后服务器打包成的信息对象 #return HttpResponse('hello') #HttpResponse字符串对象是打包发给服务器,让服务器发给前端 t=time.ctime() #获取当前时间赋值给t return render(requset,"index.html",{"time":t}) #render方法参数1:请求对象requset,参数2:返回的页面,参数3:把对象t时间点和"time"字符串组成键值对发送给前端
(5)index.html页面配置:mysite\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% load staticfiles %} <!--模版引入jquery-3.1.1.js用--> <title>时间</title> </head> <body> <h1>{{ time }}</h1> <!--通过俩个{{}}把time渲染出来--> <!--引入jquery-3.1.1.js方式1--> <!--<script src="/static/jquery-3.1.1.js"></script> --> <!--引入jquery-3.1.1.js方式2--> <script src="{% static 'jquery-3.1.1.js' %}"></script> <script> $("h1").css("color","red") </script> </body> </html>
访问:http://127.0.0.1:8080/show_time/
返回:Mon Dec 17 15:38:16 2018
四.django的url路由系统
1.URL配置(URLconf)
就像Django所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。
FBV模式语法:一个URL对应一个函数这种模式
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
(1)正则表达式
方式一:无命名分组(后端的形参可以随便写)
全局url(控制器)配置:mysite\urls.py
from django.conf.urls import url from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^article/(\d{4})/(\d{2})',views.article_year), #(\d{4})年份分组和(\d{2})月份分组的内容就是视图函数里的参数 ]
views视图函数配置:mysite\blog\views.py
from django.shortcuts import render,HttpResponse import time def article_year(request,year,month): #接收urls.py里传的参数request,year接收分组的年份,month接收分组的月份 return HttpResponse("年份:%s 月份:%s" %(year,month)) #
访问:http://127.0.0.1:8080/article/2018/12
显示:年份:2018 月份:12
正则表达式方式二:有名分组
全局url(控制器)配置:mysite\urls.py
from django.conf.urls import url from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^article/(?P<year>\d{4})/(?P<month>\d{2})',views.article_year), #有名(?P<year>\d{4})分组和有有名分组(?P<month>)的内容就是视图函数里的参数
views视图函数配置:mysite\blog\views.py
from django.shortcuts import render,HttpResponse
import time
def article_year(request,year,month): #接收urls.py里传的参数request,命名的year接收分组的年份,命名的month接收分组的月份
return HttpResponse("年份:%s 月份:%s" %(year,month)) #
访问:http://127.0.0.1:8080/article/2018/12
显示:年份:2018 月份:12
(2)别名
<1>全局url(控制器)配置:mysite\urls.py
from django.conf.urls import url from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"register",views.register,name="reg"), #别名=reg ]
<2>views视图函数配置:mysite\blog\views.py
from django.shortcuts import render,HttpResponse import time def register(request): #第一步:第一次通过URL进来GET请求进入到视图函数 #第三步:当用户输入信息通过form表单提交后第二次进来POST请求 if request.method == "POST": #第四步:如果post请求走这里 print(request.POST.get("user")) print(request.POST.get("age")) return HttpResponse("success!") #第五步:返回success return render(request,"register.html") #第二步:如果GET方式请求render访问register.html注册页面
<3>register.html页面配置
<!DOCTYPE html> <html lang="en"> <head> {% load staticfiles %} <meta charset="UTF-8"> <title>注册表单</title> </head> <body> <h1>学生注册</h1> <hr> <form action="{% url 'reg' %}" method="post"> <!--提交后注册到action=reg(别名),通过post方式发送--> <p>姓名<input type="text" name="user"></p> <p>年龄<input type="text" name="age"></p> <p><input type="submit"></p> </form> </body> </html>
访问:http://127.0.0.1:8080/register输入好用户名xixi和年龄18后
页面返回:success!
服务端返回:
xixi
18
(3)URL路由分发
分发单独bolg项目
<1>全局配置/mysite/urls.py做分发
from django.conf.urls import url,include #引入include做分发 from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^blog/', include('blog.urls')), #只要是开头以blog开头的都分发到blog.urls下面去找 ]
<2>项目局部配置/mysite/blog/urls.py
from django.conf.urls import url,include #引入include做分发 from django.contrib import admin from blog import views urlpatterns = [ url(r'article/(\d{4})/(\d{2})',views.article_year), #(\d{4})分组和(\d{2})分组的内容就是视图函数里的参数 ]
<3>views视图函数配置:mysite\blog\views.py
from django.shortcuts import render,HttpResponse import time def article_year(request,year,month): #接收urls.py里传的参数request,year,month return HttpResponse("年份:%s 月份:%s" %(year,month)) #
访问:http://127.0.0.1:8080/blog/article/2018/12
返回:年份:2018 月份:12
(4)CBV模式:一个URL对应一个类这种模式
全局url配置xixi\xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^cbv/' ,views.CBV.as_view()), #类方式 ]
视图函数xixi\app1\views.py配置
from django.shortcuts import render,HttpResponse from app1.models import * from django.views import View class CBV(View): #继承django的View类 def get(self,request): #get方法 return render(request,'index.html') #return HttpResponse('CBV.GET') def post(self,request): #post方法 return HttpResponse('CBV.POST')
页面配置:xixi\app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <form action="/cbv/" method="post"> <input type="text" /> <p><input type="submit"></p> </form> </body> </html>
访问:http://127.0.0.1:8080/cbv/
提交查询后返回:CBV.POST
五.Dango Views(视图函数)-- 处理逻辑代码
1.http请求中产生两个核心对象是HttpRequest请求对象和HttpResponse响应对象它们的所在位置在django.http
(1)http请求HttpRequest的属性和方法:
<1>属性包括
path:请求页面的全路径,不包括域名
method:请求中使用的HTTP方法的字符串表示。全大写表示。例如
if req.method=="GET":
do_something()
elseif req.method=="POST":
do_something_else()
GET:包含所有HTTP GET参数的类字典对象
POST:包含所有HTTP POST参数的类字典对象
服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用if req.POST来判断是否使用了HTTP POST 方法;应该使用if req.method=="POST"
FILES:包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name="" />标签中name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
filename:上传文件名,用字符串表示
content_type:上传文件的Content Type
content:上传文件的原始内容
user:是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你可以通过user的is_authenticated()方法来辨别用户是否登陆:if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware时该属性才可用
<2>方法包括
get_full_path():比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123
req.path:/index33
(2)http响应HttpResponse对象
对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。HttpResponse类在django.http.HttpResponse
2.在HttpResponse对象上扩展的常用返回方式(都是请求与响应的过程):
<1>返回字符串:HttpResponse("内容")
<2>页面渲染:render(request,'index.html',locals()) #打开index.html读取内容,含有特殊标记用locals()可以直接将函数中所有的变量传给模板替换(渲染),得到新的字符串,返回给用户
<3>页面跳转:redirect("url路径") #加了redirect这个响应到浏览器之后,会告诉其它该跳到那个url路径
3.结合HttpRequest请求对象和HttpResponse响应对象举例:
<1>注册页面:mysite/blog/templates/register.html
<!DOCTYPE html> <html lang="en"> <head> {% load staticfiles %} <meta charset="UTF-8"> <title>注册</title> </head> <body> <h1>注册</h1> <hr> <form action="{% url 'reg' %}" method="post"> <!--使用别名'reg'--> <p>姓名<input type="text" name="user"></p> <p>年龄<input type="text" name="age"></p> <p><input type="submit"></p> </form> </body> </html>
<2>登录页面:mysite/blog/templates/login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="jquery-3.1.1.js"></script> <title>Title</title> </head> <body> <h1>{{ name }} 登录</h1> <!--登陆后用户名--> </body> </html>
<3>全局url(控制器)配置:mysite\urls.py
from django.conf.urls import url from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"register", views.register,name="reg"), #注册首页 url(r'^login', views.login), #登录 ]
views视图函数配置:mysite\blog\views.py
from django.shortcuts import render,HttpResponse,redirect import time #注册函数 def register(request): #第一步:第一次进来GET请求 #第三步:当用户输入玩信息提交后第二次进来POST请求 print(request.path) #HttpRequest对象请求属性path请求页面的全路径,不包括域名 print(request.get_full_path()) #HttpRequest对象请求属性get_full_path()带着数据的全路径 #HttpRequest对象请求属性method请求中使用的HTTP方法的字符串表示 if request.method == "POST": #第四步:如果post请求走这里 print(request.POST.get("user")) print(request.POST.get("age")) user=request.POST.get("user") #接收输入的用户 if user=="xixi": #如果输入的用户是xixi return redirect("login/") #通过HttpResponse对象里的redirect方法跳转到urls.py里的login return HttpResponse("注册失败!") #HttpResponse对象的render方法做页面渲染 return render(request,"register.html") #第二步:如果GET请求render访问register.html页面 #登录函数 def login(req): name="xixi" #模拟从数据库把用户名取出来赋值到这里 #HttpResponse对象的render方法把内容渲染到login.html模版里面去 return render(req,"login.html",locals()) #HttpResponse对象上的locals()方法可以直接将函数中的变量name="xixi"传给模板login.html
访问:http://127.0.0.1:8080/register
当注册输入xixi后跳转到登录页面http://127.0.0.1:8080/login/并返回:
xixi 登录
4.Session与Cookie(键值对)
(1)cookie:不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生。
(2)cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地客户端浏览器上某个文件里面的键值对;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来判断这个是“谁”了。
(3)session:cookie虽然在一定程度上解决了“保持状态”的需求,但是由于cookie本身最大支持4096字节,以及cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器端的键值对,有较高的安全性。这就是session。
(4)Session与Cookie结合:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;但是cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过cookie识别不同的用户,对应的在session里保存私密的信息以及超过4096字节的文本。
(5)django里的Cookie:包含所有cookies的标准Python字典对象;keys和values都是字符串。
(6)django里的session:唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。
Session与Cookie结合使用:
<1>登录页面:xixi\app1\templates\login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> <style> * { margin: 0; padding: 0; } </style> </head> <body> <form action="/login/" method="post"> <p>姓名<input type="text" name="user"></p> <p>密码<input type="text" name="pwd"></p> <p><input type="submit"></p> </form> </body> </html>
<2>主页:xixi\app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>{{ name }}</h1> </body> </html>
<3>url:xixi\xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/' ,views.login), #登录 url(r'^index/',views.index), #首页 ]
<4>视图函数:xixi\app1\views.py
from django.shortcuts import render,redirect from app1.models import * def login(request): print("COKKIES",request.COOKIES) #获取COKKIES里键值对的键 #sessionid': 'l3889x2ae5fp2910zmjnr070jgh3s49s'对应数据库里存的那个key print("SESSION",request.session) #获取SESSION #SESSION <django.contrib.sessions.backends.db.SessionStore object at 0x0375E5D0> if request.method=="POST": name=request.POST.get("user") pwd=request.POST.get("pwd") if name=="xixi" and pwd=="123456": #设置session内部的字典内容 request.session["is_login"]=True #session是字典对象赋值["is_login"]是键,赋一个值True request.session["user"] = name return redirect("/index/") #登录成功转到主页 #登录不成功或第一次访问就停留在登录页面 return render(request,"login.html") def index(request): """ 这里必须用读取字典的get()方法把is_login的value缺省设置为False, 当用户访问backend这个url先尝试获取这个浏览器对应的session中的 is_login的值。如果对方登录成功的话,在login里就已经把is_login 的值修改为了True,反之这个值就是False的 """ if request.session.get("is_login",None): #如果有登陆过有值 获取字典的内容并传入页面文件 name=request.session.get("user",None) return render(request,"index.html",locals()) else: return redirect("/login") #否则返回登录页面
登录:http://127.0.0.1:8080/login/
跳转到:http://127.0.0.1:8080/index/
返回:xixi
六.Template(页面渲染)
1.模板系统的介绍
(1)通过后端视图函数实现前端页面渲染
<1>全局url(控制器)配置xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^show_time/', views.show_time), ]
<2>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def show_time(request): t=datetime.datetime.now() return HttpResponse("<html><body>当前时间是:%s</body></html>" %t)
访问:http://127.0.0.1:8080/show_time/
返回:当前时间是: 2018-12-19 11:27:56.686532
总结:上面这种方法尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。
(3)对页面设计进行的任何改变都必须对Python代码进行相应的修改。站点设计的修改往往比底层Python代码的修改要频繁得多,因此如果可以在不进行Python代码修改的情况下变更设计,那将会方便得多。
(4)Python代码编写和HTML设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
程序员编写Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含Python又包含HTML的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。我们可以使用Django的模板系统(Template System)来实现这种模式。
2.模板语法
2.1模版的组成:HTML代码+逻辑控制代码(python代码)
2.2逻辑控制代码的组成:
(1)变量(使用双大括号来引用变量):
1)变量语法格式:{{var_name}}
Template(模版)和Context()对象:通过一个Template(模版)对象调用它的render方法渲染一个Context(上下文)对象,最后得到的结果是一个HTML的内容传给浏览器
Template和Context的简单实现:
>>>python manage.py shell #进入当前django项目环境 >>>from django.template import Context, Template #引入Context,Template这俩个类 >>>t = Template("<h1>你好 {{name}}</h1>") #实例出Template模版对象 >>>c = Context({'name': 'xixi'}) #实例出Context上下文对象 >>>t.render(c) #渲染Template调用render(Context)方法
返回结果是一个html文件:'<h1>你好 xixi</h1>'
总结:同一模板,多个上下文,一旦有了模板对象,你就可以通过它渲染多个context,无论何时我们都可以像这样使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会更为高效
好处:Django模板解析非常快捷。 大部分的解析工作都是在后台通过对简短正则表达式一次性调用来完成。 这和基于XML的模板引擎形成鲜明对比,那些引擎承担了XML解析器的开销,且往往比Django模板渲染引擎要慢上几个数量级。
2)浅度变量的查询context传递参数值---字符串
<1>显示页面:app1\templates\show_time.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>时间</title> </head> <body> <h1>时间:{{ time }} </h1> </body> </html>
<2>全局通用URL:xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^show_time/', views.show_time), ]
<3>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def show_time(request): t=datetime.datetime.now() return render(request,"show_time.html",{"time":t} ) #render全给做好了,第一个是request对象,第二个是show_time.html你的Template对象,第三个是{"time":t}你的Context对象
访问:http://127.0.0.1:8080/show_time/
返回:时间:Dec. 20, 2018, 3:03 p.m.
3)深度变量的查询(万能的句点号)--context参数传递列表,字典,类(对象)
<1>显示页面:app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--列表部分--> <h1>列表:{{ l.1 }}</h1> <!--通过点取第二个值--> <h1>列表:{{ l.3 }}</h1> <!--通过点取第四个值--> <!--字典部分--> <h1>字典:{{ d.name }}</h1> <!--通过点取name--> <h1>字典:{{ d.age }}</h1> <!--通过点取age--> <!--类部分--> <h1>类:{{ c.name }}</h1> <!--通过点取name--> <h1>类:{{ c.sex }}</h1> <!--通过属性取sex--> </body> </html>
<2>全局通用URL:xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^query/' ,views.query), ]
<3>全局通用URL:xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^query/' ,views.query), ]
<4>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime class Animal(): #定义动物类 def __init__(self,name,sex): self.name=name #名字 self.sex=sex #性别 def query(requst): l=["东东","南南","西西","北北"] #定义列表对象 d={"name":"xixi","age":18} #定义字典对象 c=Animal("猫","母") #定义实例化类对象传俩个参数 return render(requst,"index.html",locals())
访问:http://127.0.0.1:8080/query/
列表:南南
列表:北北
字典:xixi
字典:18
类:cat
类:母
4)变量的过滤器(filter)的使用
-语法格式:{{obj(变量)|filter(过滤器对应那个函数做什么处理):param}}
-常用的内置函数:
add:给变量加上相应的值
addslashes:给变量中的引号前加上斜线
capfirst:首字母大写
cut:从字符串中移除指定的字符
date:格式化日期字符串
default:如果值是False,就替换成设置的默认值,否则就是用本来的值
default_if_none:如果值是None,就替换成设置的默认值,否则就使用本来的值
<1>显示页面:app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--add给变量加上相应的值--> <h1>add参数:{{ d.age|add:5 }}</h1> <!--capfirst给变量首字母大写--> <h1>capfirst参数:{{ test|capfirst }}</h1> <!--cut从字符串中移除指定的字符--> <h1>cut参数:{{ test2|cut:" " }}</h1> <!--date从字符串格式化日期字符串--> <h1>date参数:{{ t|date:"Y-m-d" }}</h1> <!--default如果值是False,就替换成设置的默认值,否则就是用本来的值--> <h1>default参数:{{ e|default:"空列表" }}</h1> <!--a标签参数跳转--> <h1>a标签参数:{{ a|safe }}</h1> <!--autoescape把正常安全机制关掉--> {% autoescape off %} a标签参数:{{ a }} {% endautoescape %} </body> </html>
<2>全局通用URL:xixi\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^query/' ,views.query), ]
<3>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def query(requst): d={"name":"xixi","age":18} #字典对象 test="hello xixi" #字符串 test2="hello xi xi" t=datetime.datetime.now() #时间字符串 e=[] a="<a href=''>标签</a>" #a标签 return render(requst,"index.html",locals())
访问:http://127.0.0.1:8080/query/
add参数:23
capfirst参数:Hello xixi
cut参数:helloxixi
date参数:2018-12-20
default参数:空列表
a标签参数:标签
a标签参数:标签
(2)标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)
标签语法:{% tags %}
1){% if %}的使用:{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的所有内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if num > 100 and 8 %} {% if num > 200 %} <p>num大于200</p> {% else %} <p>num大于100小于200</p> {% endif %} {% elif num < 100%} <p>num小于100</p> {% else %} <p>num等于100</p> {% endif %} </body> </html>
说明:
{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量
{% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义
2){% for %}的使用:{% for %}标签允许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容
<1>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def query(requst): l=["东东","南南","西西","北北"] #列表对象 return render(requst,"index.html",locals())
<2>页面:app1\templates\index.html
forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1: forloop.counter0类似于forloop.counter,但它是从0开始计数,第一次循环设为0 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for name in l %} <li>{{ forloop.counter }} : {{ name }} </li> {% endfor %} </ul> </body> </html>
访问:http://127.0.0.1:8080/query/
1 : 东东
2 : 南南
3 : 西西
4 : 北北
3)forloop.revcounter(倒序)
4)forloop.revcounter0
<1>页面:app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for name in l %} <li>{{ forloop.revcounter }} : {{ name }} </li> {% endfor %} </ul> </body> </html>
访问:http://127.0.0.1:8080/query/
4 : 东东
3 : 南南
2 : 西西
1 : 北北
5)forloop.first当第一次循环时值为True,在特别情况下很有用:
把循环出来的一个名字(东东)变红
<1>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def query(requst): l=["东东","南南","西西","北北"] #列表对象 return render(requst,"index.html",locals())
<2>页面:app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .first { color: red; } </style> </head> <body> <ul> {% for name in l %} {% if forloop.first %} <li class="first"> {% else %} <li> {% endif %} {{ name }} </li> {% endfor %} </ul> </body> </html>
访问:http://127.0.0.1:8080/query/
东东(红)
南南
西西
北北
总结:富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了,如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它,Django会在for标签的块中覆盖你定义的forloop变量的值,在其他非循环的地方,你的forloop变量仍然可用
6){% empty %}:遍历某一个元素为空的话执行
<1>views视图函数配置:app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def query(requst): l=[] #列表对象 return render(requst,"index.html",locals())
<2>页面:app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% for name in l %} {% if forloop.first %} <li class="first"> {% else %} <li> {% endif %} {{ name }} </li> {% empty %} <h1>列表为空</h1> {% endfor %} </body> </html>
访问:http://127.0.0.1:8080/query/
显示:列表为空
7){%csrf_token%}:csrf_token标签
8){% url %}:引用路由配置的地址
用于生成csrf_token的标签,用于防治跨站攻击验证。注意如果你在view的index里用的是render_to_response方法,不会生效其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。
<from action="{% url 'login' %}" method="post"> <p>姓名 <input type="text" name="user"></p> <p>密码 <input type="text" name="pwd"></p> <p><input type="submit"></p> {% csrf_token %} <!--server端在给你一张身份证,提交post数据必须带着身份证来才能提交--> </from>
9){% with %}:用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
10){% verbatim %}: 禁止render
{% verbatim %}
{{ hello }}
{% endverbatim %}
11){% load %}: 加载标签库
(3)自定义标签filter:
第一步:在app(应用中)中创建templatetags模块(目录)
第二步:在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag
第三步:创建任意 .py 文件,如:my_tag.py
第四步:编写xixi\app1\templatetags\my_tag.py
from django import template #引入 from django.utils.safestring import mark_safe register = template.Library() #注册一个工厂template.Library()实例赋值给register(名字是固定的,不可改变),通过register调filter装饰器,来装饰自己定义的函数 @register.filter #加上filter装饰器 def filter_multi(v1,v2): #v1是调用里左边的数字,v2是调用右边的数字 return v1 * v2 #返回v1*v2
第五步:在使用自定义simple_tag和filter的html文件中导入之前创建的my_tag.py :{% load my_tags %}
第六步:使用simple_tag和filter
<1>页面:xixi\app1\templates\index.html
<!--首行导入之前创建的 my_tags.py--> {% load my_tag %} <!--把filter标签加载进来--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--num=20做乘法--> {{ num|filter_multi:10}} <!--使用filter标签--> </body> </html>
访问:http://127.0.0.1:8080/query/
返回:200
特点:在filter里最多传参数(可以传列表)只能传一个,filter可以用在if等语句后
(4)自定义simple_tag过滤器
<1>xixi\app1\templatetags\my_tag.py
from django import template #引入 from django.utils.safestring import mark_safe register = template.Library() #注册一个工厂template.Library()赋值给register(名字是固定的,不可改变) @register.simple_tag #自定义simple_tag过滤器 def simple_tag_multi(v1,v2): return v1 * v2
<2>页面:xixi\app1\templates\index.html
<!--首行导入之前创建的 my_tags.py--> {% load my_tag %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--num=20做乘法--> {% simple_tag_multi num 5 %} <!--使用使用simple_tag过滤器--> </body> </html>
访问:http://127.0.0.1:8080/query/
返回:100
特点:参数不限,但不能放在if for语句中
(5)extend模板继承模板标签:来减少重复。
我们的模板范例都只是些零星的HTML片段,但在实际应用中,你将用Django模板系统来创建整个HTML页面。 这就带来一个常见的Web 开发问题:在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?
<1>URL:xixi\app1\views.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^backend/' ,views.backend), #主页 url(r'^student/' ,views.student), #学生页 ]
<2>函数:xixi\app1\views.py
from django.shortcuts import render,HttpResponse import time,datetime def backend(requst): num=20 return render(requst,"base.html",locals()) def student(requst): student_list=["东东","西西","南南","北北"] return render(requst,"student.html",locals())
<3>主页:xixi\app1\templates\base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin: 0; padding: 0 } .nav{ line-height: 40px; width: 100%; background-color: #2459a2; color: white; font-size: 20px; text-align: center; } .left{ width: 20%; min-height: 600px; overflow: auto; background-color: lightgrey; } .manage{ text-align: center; padding: 20px 0px; margin: 20px 0; font-size: 18px; } .left,.content{ float: left; } .content{ width: 70%; min-height: 600px; } a{ text-decoration: none; } h1,h2{ text-align: center; } </style> </head> <body> <div class="outer"> <div class="nav">标题</div> <!--左--> <div class="left"> <div class="student manage"><a href="/student/">学生管理</a></div> <div class="teacher manage"><a href="">老师管理</a></div> <div class="course manage"><a href="">课程管理</a></div> <div class="classes manage"><a href="">班里管理</a></div> </div> <!--右--> <div class="content"> <!--block相当于放了一个盒子--> {% block content%} <h1>欢迎访问首页</h1> {% endblock %} </div> </div> </body> </html>
<4>学生页面:xixi\app1\templates\student.html
<!--extends继承base.html首页模板--> {% extends "base.html" %} {% block content %} <!--拿到父类base.html的内容--> {{ block.super }} <!--遍历取出所有学生--> {% for student in student_list %} <h2>学生{{ student }}</h2> {% endfor %} {% endblock %}
访问:http://127.0.0.1:8080/backend/
点击:学生管理
这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。以下是使用模板继承的一些诀窍:
1>如果在模板中使用 {% extends %},必须保证其为模板中的第一个模板标记。否则,模板继承将不起作用。
2>一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。俗话说,钩子越多越好。
3>如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个{% block %}中。如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
4>不允许在同一个模板中定义多个同名的 {% block %}。存在这样的限制是因为block 标签的工作方式是双向的。也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的{% block %} 标签,父模板将无从得知要使用哪个块的内容。
七.models(数据库模型)
1.django默认支持sqlite,mysql, oracle,postgresql数据库。
(1)sqlite:django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3
(2)mysql引擎名称:django.db.backends.mysql
2.mysql驱动程序
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(纯python的mysql驱动程序)
3.ORM(object relation mapping)对象关系映射表:object相当于python里的类对象
用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。
(1)Django ORM优点:
<1>ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
<2>可以避免一些新手程序员写sql语句带来的性能问题。
<3>利用ORM创建Book表和Author表
(2)Django ORM语法:
1)mysql数据库创建表方法
<1>全局配置文件添加要连接的mysql库:xixi\xixi\settings.py
#引掉默认的db.sqlite
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# }
#连接自己的mysql DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'ORM', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '123456', #你的数据库密码 'HOST': '192.168.1.232', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
<2>django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以需要的驱动是PyMySQL,首先安装:pip install pymysql后需要找到项目名文件下的__init__。在xixi\xixi\__init__.py里面写入:
import pymysql pymysql.install_as_MySQLdb() #告诉django驱动引擎不要用MySQLdb替换成pymysql
<3>创建表:xixi\app1\models.py
from django.db import models class Book(models.Model): #创建Book表继承models.Model类 name=models.CharField(max_length=20) #name名字字段:字符串数字类型长度20 price=models.IntegerField() #price价格字段 author=models.CharField(max_length=20) #author作者字段 pub_data=models.DateField() #put_data时间字段 def __str__(self): #显示相关信息 return self.name class Author(models.Model): #创建作者信息表Author继承models.Model类 name=models.CharField(max_length=32)
<4>按照对应的类生成对应的表语句:python manage.py makemigrations
<5>创建表语句:python manage.py migrate
<6>当前ORM库创建了那些表:
+----------------------------+ | Tables_in_ORM | +----------------------------+ | app1_author | | app1_book | | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+
2)单表操作
<1>添加:通过对象去对应表,它们俩是一个映射关系,表名对应类名,里面的字段对应属性,每实例出一个类对象就是对应表里面的每一行记录
当前app1_book表字段:
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| price | int(11) | NO | | NULL | |
| author | varchar(20) | NO | | NULL | |
| pub_data | date | NO | | NULL | |
+----------+-------------+------+-----+---------+----------------+
方式一:通过一个Book类去实例化对象方式添加
xixi\app1\views.py配置:
from django.shortcuts import render,HttpResponse from app1.models import * #引入app1.models好调用book类 def addbook(request): b=Book(name="python",price=99,author="xixi",pub_data="2018-12-25") b.save() return HttpResponse("添加成功") #返回值
添加一条语句结果:
+----+--------+-------+--------+------------+ | id | name | price | author | pub_data | +----+--------+-------+--------+------------+ | 1 | python | 99 | xixi | 2018-12-25 | +----+--------+-------+--------+------------+
方式二:通过Book.objects调用create在数据库创建方法
xixi\app1\views.py配置:
from django.shortcuts import render,HttpResponse from app1.models import * def addbook(request): Book.objects.create(name="linux",price=66,author="dongdong",pub_data="2018-12-25") return HttpResponse("添加成功")
添加第二条语句结果:
+----+--------+-------+----------+------------+ | id | name | price | author | pub_data | +----+--------+-------+----------+------------+ | 1 | python | 99 | xixi | 2018-12-25 | | 2 | linux | 66 | dongdong | 2018-12-25 | +----+--------+-------+----------+------------+
<2>修改
方式一:通过update方法
xixi\app1\views.py配置:
from django.shortcuts import render,HttpResponse from app1.models import * def update(request): Book.objects.filter(author="xixi").update(price=250) #修改price为250 return HttpResponse("修改成功")
把author=xixi的price从99改为250结果:
+----+--------+-------+----------+------------+ | id | name | price | author | pub_data | +----+--------+-------+----------+------------+ | 1 | python | 250 | xixi | 2018-12-25 | | 2 | linux | 66 | dongdong | 2018-12-25 | +----+--------+-------+----------+------------+
方式二:通过update是QuerySet类方法修改(get方式只能改一条记录)
xixi\app1\views.py配置:
from django.shortcuts import render,HttpResponse from app1.models import * def update(request): u=Book.objects.get(author="dongdong") #找到book这个对象集合 u.price=200 #修改price为200 u.save() return HttpResponse("修改成功")
把author=dongdong的price从66改为200
+----+--------+-------+----------+------------+ | id | name | price | author | pub_data | +----+--------+-------+----------+------------+ | 1 | python | 250 | xixi | 2018-12-25 | | 2 | linux | 200 | dongdong | 2018-12-25 | +----+--------+-------+----------+------------+
<3>删除
xixi\app1\views.py配置:
from django.shortcuts import render,HttpResponse from app1.models import * def delete(request): Book.objects.filter(author="xixi").delete() return HttpResponse("删除成功")
<4>查询
当前app1_book表
+----+--------+-------+----------+------------+ | id | name | price | author | pub_data | +----+--------+-------+----------+------------+ | 1 | python | 250 | xixi | 2018-12-25 | | 2 | linux | 200 | dongdong | 2018-12-25 | | 3 | php | 262 | nannan | 2018-12-29 | | 4 | java | 180 | beibei | 2018-12-29 | +----+--------+-------+----------+------------+
主页:xixi\app1\templates\index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> <style> .head{ line-height: 40px; background-color: green; color: white; text-align: center; } </style> </head> <body> <div class="outer"> <div class="head">标题</div> <div class="content"> <a href="/select/">查询书籍</a> </div> <div class="queryResult"> {% for book in book_list %} <!--循环遍历book_list--> <div> <p>{{ book.name }} {{ book.author }} {{ book.price }} </p> <!--显示书籍名字,作者,价格--> </div> {% endfor %} </div> </div> </body> </html>
url配置:xixi\app1\urls.py
from django.conf.urls import url from django.contrib import admin from app1 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/' ,views.index), #主页 url(r'^select/',views.select), #查询 ]
访问主页:http://127.0.0.1:8080/index/
查询方法:
(1)filter(**kwargs):它包含了与所给筛选条件相匹配的对象
(2)all():查询所有结果
函数配置:xixi\app1\views.py
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list=Book.objects.all() #book_list是QuerySet集合对象,里面的每一个是book实例对象 return render(request,"index.html",{"book_list":book_list})
点击查询书籍跳转到http://127.0.0.1:8080/select/显示结果:
(3)QuerySet特点:可迭代,可切片
函数配置:xixi\app1\views.py
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all()[:3] #通过切面查询前三条记录 return render(request,"index.html",{"book_list":book_list})
点击查询书籍跳转到http://127.0.0.1:8080/select/显示结果
(4)first():返回第一条记录(取到的是一个实例对象,并非一个QuerySet的集合对象)
函数配置:xixi\app1\views.py
def select(request): book_list = Book.objects.first() return render(request,"index.html",{"book_list":book_list})
(5)last():返回最后一条记录(取到的是一个实例对象并非一个QuerySet的集合对象)
def select(request): book_list = Book.objects.last() return render(request,"index.html",{"book_list":book_list})
(6)get(**kwargs):返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
def select(request): book_list = Book.objects.get(id=2) #get只能取出一条记录的时候才不报错 return render(request,"index.html",{"book_list":book_list})
(7)values(*field):返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
取出作者是xixi所有书的名字和价格
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() ret1=Book.objects.filter(author="xixi").values("name","price") #values拿到想查询的name和price字典的集合 print(ret1) #<QuerySet [{'name': 'python', 'price': 250}]> return render(request,"index.html",{"book_list":book_list})
(8)values_list(*field):它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
取出作者是xixi所有书的名字和价格:
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() ret2 = Book.objects.filter(author="dongdong").values_list("name","price") #values拿到想查询的name和price内容以列表方式显示 print(ret2) #<QuerySet [('linux', 200)]> return render(request,"index.html",{"book_list":book_list})
(9)exclude(**kwargs):它包含了与所给筛选条件不匹配的对象
刨除作者是xixi的剩下书籍都显示出来
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() book_list=Book.objects.exclude(author="xixi") #除了author="xixi"的另外都显示出来 return render(request,"index.html",{"book_list":book_list})
查询结果:
(10)order_by(*field):对查询结果排序
(11)reverse():对查询结果反向排序
(12)distinct():从返回结果中剔除重复纪录
查询所有author字段去重:
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() book_list=Book.objects.all().values("author").distinct() #根据values拿到某一个字段author去重 return render(request,"index.html",{"book_list":book_list})
查询结果:
(13)count():返回数据库中匹配查询(QuerySet)的对象数量。
统计去重后一共有多少本书:
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() book_count=Book.objects.all().values("author").distinct().count() #根据values拿到某一个字段author去重后统计数量 print(book_count) #返回多少条行数:4 return render(request,"index.html",{"book_list":book_list})
(14)exists():如果QuerySet包含数据,就返回True,否则返回False。
(15)双下划线模糊查询:
1)查询价格大于200的书名字和价格查询出来
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() book_list=Book.objects.filter(price__gt=200).values("name","price") #查询price大于200的 return render(request,"index.html",{"book_list":book_list})
查询结果:
2)查询书名包含字母p的书名和价格查询出来
from django.shortcuts import render,HttpResponse from app1.models import * def index(request): return render(request,"index.html") def select(request): book_list = Book.objects.all() book_list=Book.objects.filter(name__contains="p").values("name","price") #查询name包含p的 return render(request,"index.html",{"book_list":book_list})
查询结果
3)多表操作一对多
创建表
<1>xixi\xixi\settings.py全局配置文件添加要连接的mysql库
#连接自己的mysql DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'XIXI', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '123456', #你的数据库密码 'HOST': '192.168.1.232', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
<2>django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以需要的驱动是PyMySQL,只需要找到项目名文件下的__init__,在xixi\xixi\__init__.py里面写入:
import pymysql pymysql.install_as_MySQLdb() #告诉django驱动引擎不要用MySQLdb替换成pymysql
<3>xixi\app1\models.py创建表
from django.db import models #外键表:外键建在多的一方 class Book(models.Model): #书籍表 name=models.CharField(max_length=20) price=models.IntegerField() pub_data=models.DateField() publish = models.ForeignKey("Publish",on_delete=models.CASCADE) #关联主键表Publish def __str__(self): return self.name #主键表 class Publish(models.Model): #出版社表 name=models.CharField(max_length=32) #出版社名字 city=models.CharField(max_length=32) #哪个城市 def __str__(self): return self.name
<4>按照对应的类生成对应的表语句:
python manage.py makemigrations
<5>创建表语句:
python manage.py migrate
<6>XIXI库创建了那些表:
+----------------------------+ | Tables_in_XIXI | +----------------------------+ | app1_book | | app1_publish | | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+
添加记录
主键表app1_publish插入数据:
+----+-----------------+--------+ | id | name | city | +----+-----------------+--------+ | 1 | 北京出版社 | 北京 | | 2 | 上海出版社 | 上海 | | 3 | 山东出版社 | 山东 | | 4 | 新华出版社 | 北京 | +----+-----------------+--------+
添加记录方式一:由于绑定一对多的字段,比如publish,存到数据库中的字段名叫publish_id,所以我们可以直接给这个字段设定对应值
def addbook(request): Book.objects.create(name='php', publish_id=2, #这里的2是指为该book对象绑定了Publisher表中id=2的行对象 pub_data='2018-7-7', price=99) return HttpResponse("添加成功")
添加记录方式二:获取要绑定的Publisher对象后添加数据
def addbook(request): publish_obj = Publish.objects.filter(name="北京出版社")[0] #根据publish表里的name=zuguo Book.objects.create(name="C++", price=88, pub_data="2018-12-12", publish=publish_obj) return HttpResponse("添加成功")
俩种方式插入数据后表app1_book数据:
+----+------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+------+-------+------------+------------+ | 1 | php | 99 | 2018-07-07 | 2 | | 2 | C++ | 88 | 2018-12-12 | 1 | +----+------+-------+------------+------------+
记录查询:
通过对象查询
当前主键表app1_publish
+----+-----------------+--------+ | id | name | city | +----+-----------------+--------+ | 1 | 北京出版社 | 北京 | | 2 | 上海出版社 | 上海 | | 3 | 山东出版社 | 山东 | | 4 | 新华出版社 | 北京 | +----+-----------------+--------+
当前外键表app1_book
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 99 | 2018-07-07 | 2 | | 2 | C++ | 88 | 2018-12-12 | 1 | | 3 | linux | 55 | 2018-12-14 | 4 | | 4 | python | 69 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
正向查询:通过对象直接取外键
通过name=php书找出版社的name和city
def addbook(request): book_obj = Book.objects.get(name="php") print(book_obj.name) #打印name:php print(book_obj.pub_data) #打印pub_data:2018-07-07 #一对多:book_obj.publish关联表外键字段查询一定是一个对象 print(type(book_obj.publish)) #打印Publish类的实例对象:<class 'app1.models.Publish'> print(book_obj.publish.name) #打印外键表nmae:上海出版社 print(book_obj.publish.city) #打印外键表city:上海 return HttpResponse("查询成功")
反向查询:找主表对象
查询上海出版社出过的所有书籍名字和价格
方式一:
def addbook(request): pub_obj = Publish.objects.filter(name="上海出版社")[0] #拿到name="上海出版社"的行记录对象 ret = Book.objects.filter(publish=pub_obj).values("name", "price") #过滤Book表里的publish属性等于这个对象 print(ret) #打印查询结果:<QuerySet [{'name': 'php', 'price': 99}]> return HttpResponse("查询成功")
方式二:通过pub_obj.表_set.all()找到所有书的关联对象,针对所有关联对象取内容
def addbook(request): pub_obj = Publish.objects.filter(name="上海出版社")[0] #拿到name="上海出版社"的行记录对象 print(type(pub_obj.book_set.all())) #打印集合对象:class 'django.db.models.query.QuerySet'> print(pub_obj.book_set.all().values("name", "price")) #打印查询结果:<QuerySet [{'name': 'php', 'price': 99}]> return HttpResponse("查询成功")
通过双下划线查询上海出版社出过的所有书籍名字和价格
def addbook(request): ret = Book.objects.filter(publish__name="上海出版社").values("name", "price") #publish__name外键字段的名字 print(ret) #查询结果:<QuerySet [{'name': 'php', 'price': 99}]> return HttpResponse("查询成功")
查出python这本书出版社的名字打印出来
方式一:基于出版社对象查询
def addbook(request): ret = Publish.objects.filter(book__name="python").values("name") print(ret) #查询结果:<QuerySet [{'name': '山东出版社'}]> return HttpResponse("查询成功")
方式二:基于book对象查询
def addbook(request): ret = Book.objects.filter(name="python").values("publish__name") print(ret) #查询结果:<QuerySet [{'publish__name': '山东出版社'}]> return HttpResponse("查询成功")
查出北京出版社出过的书
def addbook(request): ret = Book.objects.filter(publish__city="北京").values("name") print(ret) #查询结果:<QuerySet [{'name': 'C++'}, {'name': 'linux'}]> return HttpResponse("查询成功"))
多表操作之多对多:双向一对多就是多对多
创建表
<1>xixi\app1\models.py创建表
from django.db import models #外键表:外键建在多的一方 class Book(models.Model): #创建Book表继承models.Model类 name=models.CharField(max_length=20) #字符串数字类型长度20 price=models.IntegerField() # pub_data=models.DateField() #时间类型 publish = models.ForeignKey("Publish",on_delete=models.CASCADE) #一对多关系表Publish authors = models.ManyToManyField("Author") #多对多关系表Author def __str__(self): return self.name #主键表 class Publish(models.Model): #出版社表 name=models.CharField(max_length=32) city=models.CharField(max_length=32) def __str__(self): return self.name #跟Book表建立多对多关系表Author class Author(models.Model): #作者信息表 name=models.CharField(max_length=32) age=models.IntegerField(default=20) def __str__(self): return self.name
<2>按照对应的类生成对应的表语句:
python manage.py makemigrations
<3>创建表语句:
python manage.py migrate
<4>给XIXI库创建了那些表:
+----------------------------+ | Tables_in_XIXI | +----------------------------+ | app1_author | | app1_book | | app1_book_authors | | app1_publish | | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+
<5>绑定多对多关系:让作者跟书籍绑定关系
当前出版社主键表数据:app1_publish
+----+-----------------+--------+ | id | name | city | +----+-----------------+--------+ | 1 | 北京出版社 | 北京 | | 2 | 上海出版社 | 上海 | | 3 | 山东出版社 | 山东 | | 4 | 新华出版社 | 北京 | +----+-----------------+--------+
书籍外表数据:app1_book
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 99 | 2018-07-07 | 2 | | 2 | C++ | 88 | 2018-12-12 | 1 | | 3 | linux | 55 | 2018-12-14 | 4 | | 4 | python | 69 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
作者信息表数据:app1_author
+----+--------+-----+ | id | name | age | +----+--------+-----+ | 1 | 东东 | 22 | | 2 | 西西 | 25 | | 3 | 南南 | 26 | | 4 | 北北 | 26 | +----+--------+-----+
通过对象的方式绑定关系
方式一:绑定一个作者信息和一本书
把id等于3的书绑定id=2的作者
def addbook(request): book_obj = Book.objects.get(id=3) #取出id=3的书 author_objs = Author.objects.get(id=2) #取出id=2的作者信息 book_obj.authors.add(author_objs) #add方法加作者对象信息 return HttpResponse("绑定成功")
绑定后app1_book_authors表:
+----+---------+-----------+ | id | book_id | author_id | +----+---------+-----------+ | 1 | 3 | 2 | +----+---------+-----------+
方式二:绑定多个作者信息和一本书
把id=4的书绑定给所有作者
def addbook(request): book_obj = Book.objects.get(id=4) #取出id=4的书 author_objs = Author.objects.all() #取出所有作者信息 book_obj.authors.add(*author_objs) #add方法加作者对象信息 return HttpResponse("绑定成功")
绑定后app1_book_authors表:
+----+---------+-----------+ | id | book_id | author_id | +----+---------+-----------+ | 1 | 4 | 1 | | 2 | 4 | 2 | | 3 | 4 | 3 | | 4 | 4 | 4 | +----+---------+-----------+
通过对象的方式解除多对多的关系
方式一:解除id=2的书的作者信息
def addbook(request): book_obj = Book.objects.get(id=3) #取出id=3的书 author_objs = Author.objects.all() #取出所有作者信息 book_obj.authors.remove(2) return HttpResponse("解除成功")
方式二:解除id=4的书所有作者信息
def addbook(request): book_obj = Book.objects.get(id=4) #取出id=4的书 author_objs = Author.objects.all() #取出所有作者信息 book_obj.authors.remove(*author_objs) return HttpResponse("解除成功")
通过filter values (双下换线)进行多对多的关联查询(形式和一对多)
当前app1_book_authors表
+----+---------+-----------+ | id | book_id | author_id | +----+---------+-----------+ | 4 | 1 | 1 | | 2 | 2 | 3 | | 1 | 3 | 4 | | 3 | 4 | 2 | +----+---------+-----------+
查出西西出过的书籍名称及价格
def addbook(request): ret2 = Book.objects.filter(authors__name="西西").values("name", "price", "authors__name") print(ret2) #<QuerySet [{'name': 'python', 'price': 69, 'authors__name': '西西'}]> return HttpResponse("查询成功")
聚合查询和分组查询
出版社主键表数据:app1_publish
+----+-----------------+--------+ | id | name | city | +----+-----------------+--------+ | 1 | 北京出版社 | 北京 | | 2 | 上海出版社 | 上海 | | 3 | 山东出版社 | 山东 | | 4 | 新华出版社 | 北京 | +----+-----------------+--------+
书籍外表数据:app1_book
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 99 | 2018-07-07 | 2 | | 2 | C++ | 88 | 2018-12-12 | 1 | | 3 | linux | 55 | 2018-12-14 | 4 | | 4 | python | 69 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
作者信息表数据:app1_author
+----+--------+-----+ | id | name | age | +----+--------+-----+ | 1 | 东东 | 22 | | 2 | 西西 | 25 | | 3 | 南南 | 26 | | 4 | 北北 | 26 | +----+--------+-----+
<1> aggregate(*args,**kwargs):
views.py里要引入:from django.db.models import Avg,Min,Sum,Max
所有书的总价格的平均值(单表查询)
def addbook(request): ret = Book.objects.all().aggregate(Avg("price")) print(ret) #查询结果:{'price__avg': 77.75} return HttpResponse("查询成功")
西西出过的书的总价格(关联查询)
def addbook(request): ret = Book.objects.filter(authors__name="西西").aggregate(xixi_money=Sum("price")) print(ret) #查询结果:{'xixi_money': 69} return HttpResponse("查询成功")
<2> annotate(*args,**kwargs):
每个作者出过的书的总价格(分组查询)
def addbook(request): ret = Book.objects.values("authors__name").annotate(Sum("price")) #按作者名字authors__name分组 print(ret) #查询结果:<QuerySet [{'authors__name': '东东', 'price__sum': 99}, {'authors__name': '西西', 'price__sum': 69}, {'authors__name': '南南', 'price__sum': 88}, {'authors__name': '北北', 'price__sum': 55}]> return HttpResponse("查询成功")
查每一个出版社最便宜的书的价格
def addbook(request): ret = Publish.objects.values("name").annotate(价格=Min("book__price")) #按出版社名字name分组 print(ret) #查询结果:<QuerySet [{'name': '上海出版社', '价格': 99}, {'name': '北京出版社', '价格': 88}, {'name': '新华出版社', '价格': 55}, {'name': '山东出版社', '价格': 69}]> return HttpResponse("查询成功")
F查询和Q查询
仅仅靠单一的关键字参数查询已经很难满足查询要求。此时Django为我们提供了F和Q查询:
views.py里要引入:from django.db.models import Q,F
出版社主键表数据:app1_publish
+----+-----------------+--------+ | id | name | city | +----+-----------------+--------+ | 1 | 北京出版社 | 北京 | | 2 | 上海出版社 | 上海 | | 3 | 山东出版社 | 山东 | | 4 | 新华出版社 | 北京 | +----+-----------------+--------+
书籍外表数据:app1_book
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 99 | 2018-07-07 | 2 | | 2 | C++ | 88 | 2018-12-12 | 1 | | 3 | linux | 55 | 2018-12-14 | 4 | | 4 | python | 69 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
作者信息表数据:app1_author
+----+--------+-----+ | id | name | age | +----+--------+-----+ | 1 | 东东 | 22 | | 2 | 西西 | 25 | | 3 | 南南 | 26 | | 4 | 北北 | 26 | +----+--------+-----+
修改
给所有书价格price加30
def addbook(request): Book.objects.all().update(price=F("price") + 30) return HttpResponse("修改成功")
给所有书价格加30后结果:
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 129 | 2018-07-07 | 2 | | 2 | C++ | 118 | 2018-12-12 | 1 | | 3 | linux | 85 | 2018-12-14 | 4 | | 4 | python | 99 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
查找
找name=php或者price=129的筛选出来
def addbook(request): ret = Book.objects.filter(Q(price=129) | Q(name="php")) print(ret) #<QuerySet [<Book: php>]> return HttpResponse("查询成功")
找到name里有p字母的筛选出来
def addbook(request): ret = Book.objects.filter(Q(name__contains="p")) print(ret) #<QuerySet [<Book: php>, <Book: python>]> return HttpResponse("查询成功")
querySet集合对象的特性
<1>Django的queryset是惰性的
Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得
到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,
这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。
<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql.为了验证这些,需要在settings里加入 LOGGING(验证方式)
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 129 | 2018-07-07 | 2 | | 2 | C++ | 118 | 2018-12-12 | 1 | | 3 | linux | 85 | 2018-12-14 | 4 | | 4 | python | 99 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
def addbook(request): ret = Book.objects.filter(price=129) for i in ret: print(i.price) #129 return HttpResponse("查询成功")
<3>queryset是具有cache的
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行(evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。
def addbook(request): ret = Book.objects.filter(price=129) for i in ret: print(i.price) #129 for i in ret: print(i.price) #129 return HttpResponse("查询成功")
<4>简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据!为了避免这个,可以用exists()方法来检查是否有数据:
查询price是否有129如果有就不放到缓存里
def addbook(request): ret = Book.objects.filter(price=129) if ret.exists(): print("ok") return HttpResponse("查询成功")
<5>当queryset非常巨大时,cache会成为问题处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。
def addbook(request): ret = Book.objects.filter(price=129) ret = ret.iterator() #生成iterator对象 print(ret) for i in ret: print(i.name) #第一遍有值:php for i in ret: #第二遍没有值了 print(i.name) return HttpResponse("查询成功")
总结:queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。
多表操作多对多---手动建立第三张表
手动自己建立第三张表方式: 可以插入值方式绑定
创建表
<1>xixi\app1\models.py创建表
from django.db import models #外键表:外键建在多的一方 class Book(models.Model): #创建Book表继承models.Model类 name=models.CharField(max_length=20) #字符串数字类型长度20 price=models.IntegerField() # pub_data=models.DateField() #时间类型 publish = models.ForeignKey("Publish",on_delete=models.CASCADE) #一对多关系表Publish def __str__(self): return self.name #主键表 class Publish(models.Model): name=models.CharField(max_length=32) city=models.CharField(max_length=32) def __str__(self): return self.name #自己建立第三张表用于多对多关系(俩个一对多凑成多对多) class Book_Author(models.Model): book=models.ForeignKey("Book",on_delete=models.CASCADE) #book表和Book_Author表就是一对多的关系 author=models.ForeignKey("Author",on_delete=models.CASCADE) #author表和Book_Author表是一对多的关系 #跟Book表建立多对多关系表Author class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField(default=20) def __str__(self): return self.name
出版社主键表数据:app1_publish
+----+-----------------+--------+ | id | name | city | +----+-----------------+--------+ | 1 | 北京出版社 | 北京 | | 2 | 上海出版社 | 上海 | | 3 | 山东出版社 | 山东 | | 4 | 新华出版社 | 北京 | +----+-----------------+--------+
书籍外表数据:app1_book
+----+--------+-------+------------+------------+ | id | name | price | pub_data | publish_id | +----+--------+-------+------------+------------+ | 1 | php | 99 | 2018-07-07 | 2 | | 2 | C++ | 88 | 2018-12-12 | 1 | | 3 | linux | 55 | 2018-12-14 | 4 | | 4 | python | 69 | 2018-12-15 | 3 | +----+--------+-------+------------+------------+
作者信息表数据:app1_author
+----+--------+-----+ | id | name | age | +----+--------+-----+ | 1 | 东东 | 22 | | 2 | 西西 | 25 | | 3 | 南南 | 26 | | 4 | 北北 | 26 | +----+--------+-----+
给关联表插入数据:
def addbook(request): #创建第三张表 Book_Author.objects.create(book_id=2,author_id=3) #给第二本书绑定第三个作者 return HttpResponse("创建成功")
+----+-----------+---------+ | id | author_id | book_id | +----+-----------+---------+ | 1 | 3 | 2 | +----+-----------+---------+
通过关联查询查出第二本的对应作者:
def addbook(request): #创建第三张表 obj = Book.objects.get(id=2) #通过book找到第三张表id=2的 print(obj.book_author_set.all()[0].author) #通过关联找到作者名字:南南 return HttpResponse("查询成功")
西西出过的书籍名称及价格
def addbook(request): ret = Book.objects.filter(book_author__author__name="西西").values("name", "price") #第三张表book_author__author__name="西西"找到对应的作者 print(ret) #<QuerySet [{'name': 'php', 'price': 99}]> return HttpResponse("查询成功")