day_16_Django进阶
一、上节内容总结
Django
urls.py------url控制器 引导进入views.py中的视图函数
views.py------视图函数 request,httpResponse
template 模板 渲染 template对象.render(context对象)
models(orm)
表-----类
字段-----类属性
表的记录----类的实例化对象
setings下的项目名称 install_apps
二、html引入静态文件
静态文件包含:html js css 图片
静态文件存放位置,把各个的静态文件放在自己项目下
在settings.py中配置的,发送到客户端时,还是别名
# urls.py
from app01 import views url(r'^timer/', views.timer),
# view.py
import datetime def timer(request): timer = datetime.datetime.now() return render(request,"timer.html",locals())
# settings.py
STATIC_URL = '/static/' #引用名 STATICFILES_DIRS=( ("hello",os.path.join(BASE_DIR,"app01","static")), #statics文件夹写在不同的app下,静态文件的调用 )
# timer.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>time is {{ timer }}</h1> </body> <script src="/static/hello/jquery-3.1.1.js"></script> <script> $("h1").css("color","red") </script> </html>
三、django模板解析(模板语言)
render渲染详解
Django模板解析效率非常高
django中万能的逗点号,就是对引入变量对象的属性调用
# render渲染实例:
# urls.py
from app01 import views url(r'^timer/', views.timer),
# views.py
from django.shortcuts import render,HttpResponse from django.template import Template, Context def timer(request): t=datetime.datetime.now() templates=Template("<h1>the current time:{{t}}</h1>") # 制作模板文件对象 contexts=Context({"t":t}) # 制作上下文对象 ret=templates.render(contexts) # 使用render进行渲染 return HttpResponse(ret)
四、标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)
{% if %} 的使用
{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的所有内容.
{% for %}的使用
{% for %}标签允许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容.
csrf_token标签
用于生成csrf_token的标签,用于防治跨站攻击验证。 其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的.
{% url %}的使用
引用路由配置的地址
<form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
{% with %}的使用
用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
{% verbatim %}的使用
禁止render,返回就是该字符串本身
{% verbatim %}
{{ hello }}
{% endverbatim %}
五.变量的过滤器(filter)的使用
语法格式:{{obj|filter:param}}
加载标签库:自定义filter和simple_tag
在app中创建templatetags模块(必须的)
自定义标签库
# tags.py
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 @register.filter # 只能接受两个参数 def mul(x,y): return x*y @register.simple_tag def mul2(x,y,z): 参数不限,但不能放在if for语句中 return x*y*z
# views.py
def timer(request): x=10 y=[11,22] a="<a href='#'>跳转</a>" s="hello asdgsdf gsFGSDA GSFDGDSF" s2="hello world python python python python python python python" value8 = 'http://www.baidu.com/?a=1&b=3' L=[[1,2,3],[4,5,6],[7,8,9]] obj_list=[] z=10 return render(request,"timer.html",locals())
在首行加载tags.py
# timer.html
{% load tags %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>it is {{ times|date:"Y-m-d" }}</h1> <h1>x= {{ x|add:20 }}</h1> <h1>y= {{ y|default:"空" }}</h1> <h1>y= {{ a|safe}}</h1> <h1>y= {{ value8|urlencode}}</h1> <h1>{{ s|truncatechars:2 }}</h1> <h1>{{ s2|truncatewords:5 }}</h1> <h1>{{ z|mul:"[1,2,3,4]"}}</h1> <h1>{% mul2 3 4 5 %}</h1> <h1>{% mul2 z 4 5 %}</h1> {% if z|mul:30 > 100 %} {{ z|mul:30 }} {% endif %} {% for obj in L %} <p>{{ forloop.counter0 }}----{{ obj }}</p> #从0开始显示索引 <p>{{ forloop.revcounter0 }}----{{ obj }}</p> {% endfor %} {% for obj in obj_list %} {{ obj.name }} {% empty %} <h1>没有符合条件的书籍</h1> # 如果该可循环对象为空,返回该内容 {% endfor %} </body> <script src="/static/hello/jquery-3.1.1.js"></script> <script> $("h1").css("color","red") </script> </html>
六. extend模板继承
到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。
这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?
Django 解决此类问题的首选方法是使用一种优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
第一步是定义 基础模板,该框架之后将由子模板所继承,这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。
子模板的作用就是重载、添加或保留那些块的内容。 (如果你一直按顺序学习到这里,保存这个文件到你的template目录下,命名为 base.html .)
我们使用模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。
每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
extend模板实例:
# urls.py
from app01 import views url(r'^$', views.index), url(r'classes', views.classes),
# views.py
def index(req): #return render(req,"index.html") return render(req,"index_new.html") def classes(req): #return render(req,"classes.html") return render(req,"classes_new.html")
# base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .header{ height: 40px; line-height: 40px; background-color: slateblue; color: white; } .left{ float: left; width: 20%; min-height: 760px; height: auto; background-color: cadetblue; text-align: center; } .right{ float: left; width: 80%; min-height: 760px; height: auto; background-color: gray; } .left p{ padding:20px 0; } </style> </head> <body> <div class="header"> {% block header_title%} oldboy {% endblock %} </div> <div class="outer"> <div class="left"> <p><a href="/classes/?id=16">python_s16</a></p> <p><a href="/classes/?id=15">python_s15</a></p> <p><a href="/classes/?id=16">python_s14</a></p> </div> {% block content %} {% endblock %} </div> </body> </html>
# index_new.html
{% extends "base.html" %} {% block content %} <div class="right content"> <h1>瓜子哥</h1> <h1>背头哥</h1> <h1>睡觉哥</h1> <h1>二强</h1> <h1>永三</h1> <h1>永三</h1> <h1>永三</h1> <h1>永三</h1> <h1>永三</h1> <h1>永三</h1> </div> {% endblock %}
# classes_new.html
{% extends "base.html" %} {% block header_title %} {{ block.super }}老衲 {% endblock %} {% block content %} <div class="right content"> <h1>s16</h1> <h1>s16</h1> <h1>s16</h1> <h1>s16</h1> <h1>s16</h1> <h1>s16</h1> </div> {% endblock %}
模板继承工作方式:
在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。
此时,模板引擎注意到 base.html 中的两个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block header_title %} 中定义的标题,对 {% block content %} 也是如此。
所以,网页标题一块将由{% block header_title %}替换,同样地,网页的内容一块将由 {% block content %}替换。
注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到模板的上下文。
换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
<1>如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
<2>一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,
因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
<3>如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。
如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
<4>不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。
也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
六、数据库和ORM
1.数据库的配置(连接mysql)
# settings.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 's16day16', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': 'sungaohui1990', #你的数据库密码 'HOST': '192.168.0.102', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
注意:
NAME即数据库的名字,在mysql连接前该数据库必须已经创建
项目名文件下的__init__.py,在里面写入:
import pymysql pymysql.install_as_MySQLdb()
2.ORM表模型
表(模型)的创建:通过创建类的方式进行创建
实 例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名。
作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),
在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。
出版商模型:出版商有名称,地址,所在城市,省,国家和网站。
书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),
一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键
# 一对多的关系

# 多对多的关系

# models.py
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=100) publication_date = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2, default=10) publisher=models.ForeignKey("Publisher") # 创建一对多时,指定外键django框架会自动在该列上加上id author=models.ManyToManyField("Author") # 创建多对多 def __str__(self): return self.title #关于一对多关系 外键在哪一张表? # 谁的信息多在谁那 class Publisher(models.Model): name = models.CharField(max_length=30, verbose_name="名称") city = models.CharField('城市', max_length=60) country = models.CharField(max_length=50) def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) def __str__(self): return self.name
分析代码:
<1> 每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
<2> 每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。
属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。
<3> 模型之间的三种关系:一对一,一对多,多对多。
一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
一对多:就是主外键关系;(foreign key)
多对多:(ManyToManyField) 自动创建第三张表(当然我们也可以自己创建第三张表:两个foreign key)
3.初始化数据库和创建表
python manager.py makemigrations # python创建表
python manager.py migrate # 生成表
(每次数据库的字段做任何更改,都要运行上面的创建表和生成表的命令)
# urls.py
from app01 import views url(r'addbook', views.addbook), url(r'query', views.query),
4.一对多和多对多的数据插入方式(先插入依赖的表)
# views.py
def addbook(req): # 一对多的插入方式一 # 先插入依赖的表 Book.objects.create(title="python葵花宝典",publication_date="2017-5-13",price=10,publisher_id=2) # 一对多的插入方式二 publisher = obj obj=Publisher.objects.filter(name="北京大学出版社")[0] # filter的queryset的集合对象 [obj1,] Book.objects.create(title="python葵花宝典2",publication_date="2017-5-14",price=10,publisher=obj) # 多对多 添加 #实现: 对一本书对象绑定一个或者多个作者对象 author_obj=Author.objects.filter(name="yuan")[0] book_obj=Book.objects.filter(id=1)[0] #book_obj.author:id=3的book对象所有关联的作者集合 print("*****",book_obj.author.all()) # ***** <QuerySet [<Author: alex>, <Author: egon>]> book_obj.author.add(author_obj) #实现: 对一个作者对象对象绑定一个或者多个书对象 author_obj = Author.objects.filter(name="yuan")[0] book_list = Book.objects.filter(id__lte=2) print("------",author_obj.book_set.all()) author_obj.book_set.add(*book_list)# return HttpResponse("添加成功")
双下划线的神奇之处
5.ORM之删(delete)
# views.py
Book.objects.filter(id=1).delete() (3, {'app01.Book_authors': 2, 'app01.Book': 1})
我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。
6.ORM之改(update和save)
update方法直接设定对应属性
save方法会将所有属性重新设定一遍,效率低
models.Book.objects.filter(id=3).update(title="PHP") ##sql: ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)
此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。
注意,这里因为update返回的是一个整形,所以没法用query属性;对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:
# settings.py
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
7.ORM之查(filter,value)
查询API:
ValueQueryset是可迭代的字典序列
# 查询相关API: filter(**kwargs): # 它包含了与所给筛选条件相匹配的对象 all(): # 查询所有结果 get(**kwargs): # 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 # -----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- values(*field): # 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 exclude(**kwargs): # 它包含了与所给筛选条件不匹配的对象 order_by(*field): # 对查询结果排序 reverse(): # 对查询结果反向排序 distinct(): # 从返回结果中剔除重复纪录 values_list(*field): # 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 count(): # 返回数据库中匹配查询(QuerySet)的对象数量。 first(): # 返回第一条记录 last(): # 返回最后一条记录 exists(): # 如果QuerySet包含数据,就返回True,否则返回False
了不起的双下划线(__)之单表条件查询:
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,
8.QuerySet与惰性机制
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的
<2> 可切片
objs=models.Book.objects.all()#[obj1,obj2,ob3...] # QuerySet: 可迭代 for obj in objs:#每一obj就是一个行对象 print("obj:",obj) # QuerySet: 可切片 print(objs[1]) print(objs[1:4]) print(objs[::-1])
QuerySet的高效使用
queryset是具有cache的
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行(evaluation).
这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。
# views.py
def query(req): # 测试该函数是否执行 print("okokkok") # 使用values和values_list获取数据 ret=Book.objects.filter(id__gt=1).values("price","title") print("ret",ret) # ret <QuerySet [{'price': Decimal('10.00')}, {'price': Decimal('10.00')}, {'price': Decimal('100.00')}]> #ret <QuerySet [{'price': Decimal('10.00'), 'title': 'python葵花宝典2'}, {'price': Decimal('10.00'), 'title': 'linux'}, {'price': Decimal('100.00'), 'title': '大数据'}]> ret = Book.objects.filter(id__gt=1).values_list("price", "title") print(ret) # [(Decimal('10.00'), 'python葵花宝典2'), (Decimal('10.00'), 'linux'), (Decimal('100.00'), '大数据')]> # 测试queryset是具有cache的 ret=Book.objects.filter(id__gt=1) for obj in ret: #[obj,obj] print(obj) Book.objects.filter(id__gt=1).update(title="yuan") for obj in ret: #[obj,obj] print(obj) # 优化缓存方法 exists iterator if ret.exists(): print("ok") iter=ret.iterator() for i in iter: print(i) for i in iter: print(i) return HttpResponse(".....")
总结:queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。
9.对象查询,单表条件查询,多表条件关联查询
多表联查时,使用双下划线后,就相当于是这两个表组合成一个大表,可以根据相应的字段进行查找。
对象形式的查找
了不起的双下划线(__)之单表条件查询
了不起的双下划线(__)之多表条件关联查询
#--------------------对象形式的查找-------------------------- # 正向查找 ret1=models.Book.objects.first() print(ret1.title) print(ret1.price) print(ret1.publisher) print(ret1.publisher.name) #因为一对多的关系所以ret1.publisher是一个对象,而不是一个queryset集合 # 反向查找 ret2=models.Publish.objects.last() print(ret2.name) print(ret2.city) #如何拿到与它绑定的Book对象呢? print(ret2.book_set.all()) #ret2.book_set是一个queryset集合 #---------------了不起的双下划线(__)之单表条件查询---------------- 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, #----------------了不起的双下划线(__)之多表条件关联查询--------------- # 正向查找(条件) ret3=models.Book.objects.filter(title='Python').values('id') print(ret3)#[{'id': 1}] # 正向查找(条件)之一对多 ret4=models.Book.objects.filter(title='Python').values('publisher__city') print(ret4) #[{'publisher__city': '北京'}] # 正向查找(条件)之多对多 ret5=models.Book.objects.filter(title='Python').values('author__name') print(ret5) ret6=models.Book.objects.filter(author__name="alex").values('title') print(ret6) #注意 #正向查找的publisher__city,author__name中的publisher,author是book表中绑定的字段 #一对多和多对多在这里用法没区别 # 反向查找(条件) #反向查找之一对多: ret8=models.Publisher.objects.filter(book__title='Python').values('name') print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors') print(ret9)#[{'book__authors': 1}, {'book__authors': 2}] #反向查找之多对多: ret10=models.Author.objects.filter(book__title='Python').values('name') print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}] #注意 #正向查找的book__title中的book是表名Book #一对多和多对多在这里用法没区别
注意:条件查询即与对象查询对应,是指在filter,values等方法中的通过__来明确查询条件。
# views.py
def query(req): # 对象形式之正向查找 ret=Book.objects.get(id=3) print(ret.title) print(ret.price) print(ret.publisher_id) print(ret.publisher) # 正向查询之一对多 ret=Book.objects.filter(title="python葵花宝典").values("publisher__city") print(ret) # 正向查询之多对多 ret=Book.objects.filter(author__name="alex").values("title","price") print(ret) # 反向查询之一对多 ret=Publisher.objects.filter(book__title="python葵花宝典").values("city") print(ret) return HttpResponse(".....")
10.聚合查询和分组查询
<1> aggregate(*args,**kwargs):
通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
from django.db.models import Avg,Min,Sum,Max 从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有 图书的集合。 >>> Book.objects.all().aggregate(Avg('price')) {'price__avg': 34.35} aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值 aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的 标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定 一个名称,可以向聚合子句提供它: >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} 如果你也想知道所有图书价格的最大值和最小值,可以这样查询: >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

浙公网安备 33010602011771号