一、准备
1、下载、安装
2、django-admin startproject mysite 创建项目
3、django-admin startapp blog 创建应用
3、 修改sittings.py
3.1 INSTALLED_APPS中添加'blog'
3.2修改时区 TIME_ZONE = 'Asia/Shanghai'
3.3 修改语言 LANGUAGE_CODE = 'zh-hans' //django在1.9 版本后不支持zh-cn
4、修改urls.py
from blog import views
url(r'^blog/index/$',views.index) //新版本django只支持先倒入,后引用
5、处理 视图blog/views.py
from django.http import HttpResponse
def index(req):
return HttpResponse('<h1>hello world</h1>');
6、启动服务器
python manager.py runserver
//访问链接 127.0.0.1:8000/blog/index
二、加载模版文件
1、在blog文件夹下新建一个文件夹,templates文件夹,然后在里面新建一个html名字为index.html
2、在blog/views/index.py加入代码如下:
from django.shortcuts import render;
#request为http请求对象
def index(request):
return render(request,'index.html',{})
三、views向模板传递变量
views.py/index方法
def index(request):
title = 'no66'
user = {'name':'张三','age':23}
book_list = ['python','java','php','c#']
return render(request,'index.html',{'title':title,'user':user,'book_list':book_list})
index.html中
{{ title }}
{{ user }}
{{ user.name }}
{{ user.age }}
{{ book_list}}
{{ book_list.0}}
{{ book_list.1}}
{{ book_list.2}}
{{ book_list.3}}
模板渲染,如果对象中的属性、方法等重复,有优先级,
1、字典
2、属性
3、方法
四、模板
1、if标签
{% if user %}
{{ user.name }}
{{ user.age }}
{% else %}
no exists
{% endif %}
注意点:不能再if中同时使用 and和or 、不能再if中使用()
2、for标签
2.1 循环列表
{% if book_list %}
{% for book in book_list %}
{{ book }}
{% endfor %}
{% else %}
no exists
{% endif %}
2.2 循环字典
2.2.1循环出key
{% if user %}
{% for k in user %}
{{ k }}
{% endfor %}
{% else %}
no exists
{% endif %}
2.2.2循环出key、value
{% for k,v in user2.items %}
{ forloop_}{{ k }}__{{v}}
{% endfor %}
{% else %}
no exists
{% endif %}
2.2.3 带序号循环
{% if user2 %}
{% for k,v in user2.items %}
#forloop_counter从1开始,forloop_counter0从0开始,
#forloop_revcounter,forloop_revcounter0反向
{{forloop_counter}}_{{ k }}__{{v}}
{% endfor %}
{% else %}
no exists
{% endif %}
2.2.4 for...empty
{% for k,v in user2.items %}
{{ k }}__{{ v }}
{% empty %}
no exists
{% endfor %}
2.2.5
forloop.counter | 索引从 1 开始算 |
forloop.counter0 | 索引从 0 开始算 |
forloop.revcounter | 索引从最大长度到 1 |
forloop.revcounter0 | 索引从最大长度到 0 |
forloop.first | 当遍历的元素为第一项时为真 |
forloop.last | 当遍历的元素为最后一项时为真 |
forloop.parentloop | 用在嵌套的 for 循环中, 获取上一层 for 循环的 forloop |
3、页面模版重用性高,有两个方案include、模版继承
3.1include:在模版html中包含另一个模版{% include 'moban.html'%},变量不需要传递
3.2模版继承:
第一步,先创建基础模版base.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
{% block content %} {% endblock %}
{% block footer %} <hr>
<p>Thanks for visiting my site.</p>
{% endblock %}
</body>
</html>
第二步,让其他模版页面继承此模版,index.html
{% extends "base.html" %}
{% block title %}The current time {% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
注意:
模板一般放在app下的templates中,Django会自动去这个文件夹中找。但 假如我们每个app的templates中都有一个 index.html,当我们在views.py中使用的时候,直接写一个 render(request, 'index.html'),Django 能不能找到当前 app 的 templates 文件夹中的 index.html 文件夹呢?(答案是不一定能,有可能找错)
Django 模板查找机制: Django 查找模板的过程是在每个 app 的 templates 文件夹中找(而不只是当前 app 中的代码只在当前的 app 的 templates 文件夹中找)。各个 app 的 templates 形成一个文件夹列表,Django 遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就停止,所有的都遍历完了还找不到指定的模板的时候就是 Template Not Found (过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是一个app可以用另一个app的模板文件,弊是有可能会找错了。所以我们使用的时候在 templates 中建立一个 app 同名的文件夹,这样就好了。
:
# views.py
defadd(request, a, b):
c =int(a) +int(b)
returnHttpResponse(str(c))
# urls.py
urlpatterns =patterns('',
url(r'^add/(\d+)/(\d+)/$', 'app.views.add', name='add'),
)
# template html
{%url 'add'45%}
这样网址上就会显示出:/add/4/5/ 这个网址,假如我们以后修改 urls.py 中的
r'^add/(\d+)/(\d+)/$'
这一部分,改成另的,比如:
r'^jiafa/(\d+)/(\d+)/$'
还可以使用 as 语句将内容取别名(相当于定义一个变量),多次使用(但视图名称到网址转换只进行了一次)
{% url 'some-url-name' arg arg2 as the_url %}
<ahref="{{ the_url }}">链接到:{{ the_url }}</a>
获取当前用户:
{{ request.user }}
如果登陆就显示内容,不登陆就不显示内容:
{%ifrequest.user.is_authenticated %}
{{ request.user.username }},您好!
{%else%}
请登陆,这里放登陆链接
{%endif %}
获取当前网址:
{{ request.path }}
获取当前 GET 参数
{{ request.GET.urlencode }}
合并到一起用的一个例子:
<a href="{{ request.path }}?{{ request.GET.urlencode }}&delete=1">当前网址加参数 delete</a>
比如我们可以判断 delete 参数是不是 1 来删除当前的页面内容。
csrf与from
<formmethod='post'>
{% csrf_token %}
{{ form }}
<inputtype="submit"value="提交">
</form>
五、定义模型
1、settings.py中设置数据库mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',#数据库引擎
'NAME': 'test',#数据库名
'USER':'root',#数据库用户名
'PASSWORD':'root',#数据库密码
'HOST':'localhost',#数据库地址,默认localhost
'PORT':'3306',#数据库端口,默认3306
},
}
2、编辑blog/models.py
from django.db import models
class Article(models.Model):
"""文章"""
title = models.CharField(max_length=20)
def __unicode__(self): #打印article对象时候会调用此方法
return self.title;
3、启动服务器
python manage.py makemigrations blog #生产sql文件
python manage.py migrate #执行生产的sql文件创建表
【问题1】:ImportError: No module named 'MySQLdb'
django python3.0 安装mysql报错
解决办法:安装pymsql
【问题2】:,再次运行 python manage.py migrate报错“mysqlclient 1.3.3 or newer is required”
下载最新的mysqlclient,一般在linux下是好了
【问题3】:如果在windows上出现需要vc ++ 10...
解决办法:去此网站
http://www.lfd.uci.edu/~gohlke/pythonlibs/找到对应的扩展
运行 pip install 对应的扩展(原文链接:
)
4、queryset操作数据库
在views.py/index
4.1添加数据
new = Article()
new.title = 'hah'
new.save()
4.2添加数据
new = Article(title='heihie')
new.save()
4.3添加数据
Article.objects.create(title='hoho')
4.4 查找不存在添加
Person.objects.get_or_create(name="WZT", age=23
) //
这种方法是防止重复很好的方法,但是速度要相对慢些,返回一个元组,第一个为Person对象,第二个为True或False, 新建时返回的是True, 已经存在时返回False.
4.5 更新不存在添加:
update_or_create()
5、修改数据
article = Article.objects.get(id=1);
article.title = '666'
article.save()
批量修改
Person.objects.filter(name__contains="abc").update(name='xxx') # 名称中包含 "abc"的人 都改成 xxx
6、删除数据
article = Article.objects.get(id=1);
article.delete()
批量删除
Person.objects.all().delete() # 删除所有 Person 记录
7 、查询数据
Person.objects.count()
Person.objects.all()
Person.objects.all().exists()
Person.objects.all()[:10] 切片操作,获取10个人,不支持负索引,切片可以节约内存
# 名称为 name 的一条,多条会报错
#get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter
Person.objects.filter(name="abc") # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人
Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件
Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人
Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写
Person.objects.filter(name__regex="^abc") # 正则表达式查询
Person.objects.filter(name__iregex="^abc") # 正则表达式不区分大小写
#filter是找出满足条件的,当然也有排除符合某条件的
Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象
Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的
打印queryset 操作语句的sql:
print
str(Author.objects.all().query),在语句后架上query,
所以,当不知道Django做了什么时,你可以把执行的 SQL 打出来看看,也可以借助 django-debug-toolbar 等工具在页面上看到访问当前页面执行了哪些SQL,耗时等。还有一种办法就是修改一下 log 的设置。
8、关系型查询:原文链接
8.1 一对一:
model中
8.1.1差
正查(通过当前对象信息查询关联对象信息):
ball = Ball.objects.get(description='红球')
clolors = ball.color.colors #查询红球对应的颜色,一种球一个颜色,一对一
反查(通过已知关联对象的属性查询当前对象信息):
color = Colors.objects.get(ball__description='红球') #括号内饰双下划线
clolors = color.colors
8.1.2 添加数据
color_obj = Colors.objects.create(colors='黑色')#先创建黑色
Ball.objects.create(color=color_obj,description='黑球')#再把黑色关联到球的表中
#增添数据的三种写法:
#写法1补充:
color_id=models.Colors.objects.create(colors="黑").id
models.Ball.objects.create(color_id=color_id,description="黑球")
#写法2:
color_obj=models.Colors.objects.create(colors="黑")
ball_obj=models.Ball(color=color_obj,description="黑球")
ball_obj.save()
#写法3(字典导入):
color_obj=models.Colors.objects.create(colors="黑")
ball_dic={'description':"黑球"}
models.Ball.objects.create(color=color_obj,**ball_dic)
8.1.3 修改数据
color_obj=models.Colors.objects.get(colors="黑") #.get()等同于.filter().first()
color_obj.colors="灰"
color_obj.save()
models.Ball.objects.filter(description="黑球").update(color=color_obj,description="灰球") #update(),delete()是QuerySet的
再添几种方式:
#更新一条数据
color_obj=models.Colors.objects.get(colors="黑色")
color_obj.colors="灰色"
color_obj.save()
#更新多条数据,把满足条件的球的description都变为灰球
#写法1:
models.Ball.objects.filter(color__colors="红色").update(description="灰球")
#写法2:
up_dic={"description":"灰球"}
models.Ball.objects.filter(id__gt=0).update(**up_dic)
8.1.4 删除数据
#删除一条
models.Ball.objects.get(description="灰球").delete() #对象和QuerySet都有方法delete()
models.Colors.objects.filter(colors="灰").delete()
#删除多条
Colors.objects.filter(colors='红色').delete() #删除所有colors为红色的数据
Colors.objects.all().delete() #清空一张表
8.2 一对多(外键):
8.2.1 查询数据
#外键表联合查询:
#外键子表查询母表,与一对一子表查询母表形式一致
#找到红裤衩所属的颜色表中的颜色--返回:红
#写法1:
print(models.Clothes.objects.get(description="小虎哥").color.colors) #返回红,通过子表查询母表,写法:"子表对象.母表表名的小写.母表字段名" ;通过Clothes表查到description为"小虎哥",查找到对应colors
#写法2,反向从母表入手:
print(models.Colors.objects.get(clothes__description="小虎哥").colors) #返回红,通过子表查询母表,但形式上是从母表对象自身直接获取字段,写法:"母表.objects.get(子表名小写__子表字段="xxx").母表字段名" ;效果和上边完全一致,另一种形式
#外键母表查询子表,与一对一形式不同,因为母表为"多",不能像一对一一样通过.get().子表.子表字段的方式获取,但与多对多母表查询子表一致
#找到颜色为红的所有服装--返回:[<Clothes: 大美女>, <Clothes: 小虎哥>]
#写法1:
color_obj=models.Colors.objects.get(colors="红")
print(color_obj.clothes_set.all()) #注意:子表小写_set的写法,它实际上是一个QuerySet,可以用update,delete,all,filter等方法
#写法2:
print(models.Clothes.objects.filter(color=models.Colors.objects.get(colors="红")))
#写法2简便写法(推荐):
print(models.Clothes.objects.filter(color__colors="红")) #写法:filter(子表外键字段__母表字段='过滤条件')
#写法3:
color_id=models.Colors.objects.get(colors="红").id #通过母表获取到颜色为红的id
print(models.Clothes.objects.filter(color_id=color_id)) #filter得到QuerySet,写法:filter(子表外键字段_母表主键=母表主键对象)
备注:通过QuerySet的.values()方法,将QuerySet转化为ValuesQuerySet
print(models.Clothes.objects.filter(color=models.Colors.objects.get(colors="红")).values('color__colors','description')) #获取子表的description字段,和母表的colors字段,获取母表字段写法: 子表外键字段名__母表字段名--适用于values()或filter()
#简写形式补充:
print(models.Clothes.objects.filter(color__colors="红").values('color__colors','description'))
#返回:
[{'description': u'\u7ea2\u5185\u8863', 'color__colors': u'\u7ea2'}, {'description': u'\u7ea2\u5185\u88e4', 'color__colors': u'\u7ea2'}]
#如果不加values(),返回的是[<Clothes: 大美女>, <Clothes: 小虎哥>]这样一个QuerySet集合,通过values可以形成一个列表,列表中的每一个元素是一个字典,可以通过list()将ValuesQeurySet转化为列表,之后返回给templates
#另外可通过.values_list()将QuerySet转化为ValuesListQuerySet。返回:[(u'\u7ea2', u'\u7ea2\u889c\u5b50'), (u'\u7ea2', u'\u7ea2\u889c\u5b50')]
#得到的是一个列表,列表中是多个元组,每个元组是ValuesQuerySet中字典的value,常用于从models里将数据取出后动态添加到前端模板中的select选项中。
#通过forms.py从models取值传给前端select选项,需重启django后,select选项才能更新,可在定义form时,添加如下关键字保障动态更新select选项
#forms.py
from django import forms
from test1 import models
class ClothesForm(forms.Form):
color=forms.IntegerField(required=True,widget=forms.Select(),)
def __init__(self,*args,**kwargs): #定义这个关键字段,当使用form时,colors表新增了颜色,前端ClothesForm的color字段的选项会自动更新
super(ClothesForm, self).__init__(*args,**kwargs)
self.fields['color'].widget.choices=models.Colors.objects.all().order_by('-id').values_list('id','colors') #-id: 按照id到排序
8.2.2 添加数据
#增添子表数据,形式与一对一一致
#添加颜色为绿的服装:小帅哥
#方法1:
models.Clothes.objects.create(color=models.Colors.objects.get(colors="绿"),description="小帅哥")
#方法1补充:
models.Clothes.objects.create(color_id=models.Colors.objects.get(colors="绿").id,description="小帅哥")
#方法2:
c_obj=models.Clothes(color=models.Colors.objects.get(colors="绿"),description="小帅哥")
c_obj.save()
#方法3:字典方式录入..参考一对一
8.2.3 修改数据
#颜色为红的服装,description都更新为大美女
#写法1:
models.Clothes.objects.filter(color__colors="红").update(description="大美女")
#写法2:
models.Clothes.objects.filter(color_id=models.Colors.objects.get(colors="红").id).update(description="大美女")
#写法3:
colors_obj=models.Colors.objects.get(colors="红")
colors_obj.clothes_set.filter(id__gte=1).update(description="大美女")
#其他写法参照一对一的修改和外键的查询
8.2.4删除数据
models.Clothes.objects.get(description="灰裙子").delete() #对象和QuerySet都有方法
delete()models.Colors.objects.filter(colors=
"
灰
").delete()
8.3 多对多:
8.3.1 查询
#多对多子表查询母表,查找小明喜欢哪些颜色--返回:[<Colors: 红>, <Colors: 黄>, <Colors: 蓝>]
#与一对多子表查询母表的形式不同,因为一对多,查询的是母表的“一”;多对多,查询的是母表的“多”
#写法1:
child_obj=models.Child.objects.get(name="小明") #写法:子表对象.子表多对多字段.过滤条件(all()/filter())
print(child_obj.favor.all())
#写法2,反向从母表入手:
print(models.Colors.objects.filter(child__name="小明")) #母表对象.filter(子表表名小写__子表字段名="过滤条件")
#多对多母表查询子表,查找有哪些人喜欢黄色--返回:[<Child: 小明>, <Child: 丫蛋>]
#与一对多母表查询子表的形式完全一致,因为查到的都是QuerySet,一对多和多对多,都是在查询子表的“多”
#写法1:
color_obj=models.Colors.objects.get(colors="黄")
print(color_obj.child_set.all())
#写法2:
print(models.Child.objects.filter(favor=models.Colors.objects.get(colors="黄")))
#写法2简便写法(推荐):
print(models.Child.objects.filter(favor__colors="黄")) #写法:filter(子表外键字段__母表字段='过滤条件')
#写法3:
color_id=models.Colors.objects.get(colors="黄").id #通过母表获取到颜色为红的id
print(models.Child.objects.filter(favor=color_id)) #filter得到QuerySet,写法:filter(子表外键字段=母表主键对象),此处和一对多略有不同,是子表外键字段而不是外键字段_母表主键
8.3.2 增与改
#添加子表关联关系
#添加小虎并让他喜欢所有颜色
#写法1:
child_obj=models.Child.objects.create(name="小虎") #如果是已有用户,使用.get()
colors_obj=models.Colors.objects.all() #创建颜色表的所有颜色QuerySet对象
child_obj.favor.add(*colors_obj) #添加对应关系,将小虎和所有颜色进行关联,写法:子表对象.子表多对多字段.add(*QuerySet对象)
#写法2:
child_obj=models.Child.objects.get(name="小虎")
colors_obj=models.Colors.objects.all()
child_obj.favor=colors_obj
child_obj.save()
#让小虎喜欢黄色和蓝色(2种写法和上边一致,只展示一种写法)
child_obj=models.Child.objects.get(name="小虎")
colors_obj=models.Colors.objects.filter(colors__in=["蓝","黄"]) #models默认只能用这种方式得到并集,如需更复杂的过滤逻辑,需使用模块Q
child_obj.favor.clear() #清空小虎已经喜欢的颜色
child_obj.favor.add(*colors_obj) #add是追加模式,如果当前小虎已经喜欢绿色,那么执行后,小虎会额外喜欢蓝,黄
#让小虎喜欢绿色(2种写法和上边一致,只展示一种写法)
child_obj=models.Child.objects.get(name="小虎")
colors_obj=models.Colors.objects.get(colors="绿")
child_obj.favor.clear()
child_obj.favor.add(colors_obj) #此处没有*
#添加母表关联关系
#让喜欢蓝色的人里添加小虎,可以用上边的方法,一个效果,让小虎喜欢蓝色,下边介绍反向插入(从母表入手)的写法
child_obj=models.Child.objects.get(name="小虎")
colors_obj=models.Colors.objects.get(colors="蓝")
colors_obj.child_set.add(child_obj) #从colors表插入小虎,写法:母表对象.子表名小写_set.add(子表对象)。 让喜欢蓝色的child_set集合添加name="小虎"
#让所有人都喜欢蓝色
children_obj=models.Child.objects.all()
colors_obj=models.Colors.objects.get(colors="蓝")
colors_obj.child_set.add(*children_obj)
#关于_set写法,是否已经有些晕了,究竟什么时候使用_set,简单记忆,只有子表才有"子表名小写_set"的写法,得到的是一个QuerySet集合,后边可以接.add(),.remove(),.update(),.delete(),.clear()
#另外备注一下,colors_obj.child_set.clear()是让所有人喜欢的颜色里去掉蓝色,colors_obj.child_set.all().delete()是删除.child_set的所有人
8.3.3 删除多对多表关系
#删除子表与母表关联关系
#让小虎不喜欢任何颜色
#写法1:
child_obj=models.Child.objects.get(name="小虎")
colors_obj=models.Colors.objects.all()
child_obj.favor=''
child_obj.save()
#写法2:
child_obj=models.Child.objects.get(name="小虎")
colors_obj=models.Colors.objects.all()
child_obj.favor.remove(*colors_obj)
#写法3:
child_obj=models.Child.objects.get(name="小虎")
child_obj.favor.clear()
#其他例子参照多对多的增与改案例,这里不做举例
#删除母表与子表关联关系
#让所有人不再喜欢蓝色
#写法1:
children_obj=models.Child.objects.all()
colors_obj=models.Colors.objects.get(colors="蓝")
colors_obj.child_set.remove(*children_obj)
#写法2:
colors_obj=models.Colors.objects.get(colors="蓝")
colors_obj.child_set.clear()
8.3.4删除多对多表数据
#删除子表数据
#喜欢蓝色的所有人都删掉
colors_obj=models.Colors.objects.get(colors="蓝")
colors_obj.child_set.all().delete() #注意有.all()
#删除所有child
models.Child.objects.all().delete()
删除母表数据:
默认情况下,如此例中,删除“红”色,那么子表与颜色表是一对一或外键关系的,子表对应数据会自动删除,如:红球,小虎哥
与颜色表是多对多关系的话,不会自动删除喜欢红色的人,而是去掉红色已选
如果想让与母表外键关联的子表在删除外键之后依旧可以保留子表数据,需要子表建表时加入以下字段:
class Clothes(models.Model):
color=models.ForeignKey("Colors",null=True,on_delete=models.SET_NULL)) #可为空,如果外键被删后,子表数据此字段置空而不是直接删除这条数据,同理也可以SET_DEFAULT,需要此字段有默认值
description=models.CharField(max_length=10)
8.4 choice
#choices相当于实现一个简化版的外键,外键的选项不能动态更新,如可选项目较少,可以采用
#先在models添加choices字段
class Child(models.Model):
sex_choice=((0,"男"),(1,"女"))
name=models.CharField(max_length=10) #姓名
favor=models.ManyToManyField('Colors') #与颜色表为多对多
sex=models.IntegerField(choices=sex_choice,default=0)
def __unicode__(self):
return self.name
#在views.py中调用
child_obj=models.Child.objects.get(name="小虎")
print(child_obj.sex) #返回0或1
print(child_obj.get_sex_display()) #返回男或女
8.5 django查询条件补充
条件选取querySet的时候,filter表示=,exclude表示!=
querySet.distinct() 去重复
__exact 精确等于 like 'aaa'
__iexact 精确等于 忽略大小写 ilike 'aaa'
__contains 包含 like '%aaa%'
__icontains 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains。
__gt 大于
__gte 大于等于
__lt 小于
__lte 小于等于
__in 存在于一个list范围内
__startswith 以...开头
__istartswith 以...开头 忽略大小写
__endswith 以...结尾
__iendswith 以...结尾,忽略大小写
__range 在...范围内
__year 日期字段的年份
__month 日期字段的月份
__day 日期字段的日
__isnull=True/False
extra 实现 别名,条件,排序等
extra 中可实现别名,条件,排序等,后面两个用 filter, exclude 一般都能实现,排序用 order_by 也能实现。我们主要看一下别名这个
比如 Author 中有 name, Tag 中有 name 我们想执行
SELECT name AS tag_name FROM blog_tag;
这样的语句,就可以用 select 来实现,如下:
In [44]: tags = Tag.objects.all().extra(select={'tag_name': 'name'})
In [45]: tags[0].name
Out[45]: u'Django'
In [46]: tags[0].tag_name
Out[46]: u'Django'
我们发现 name 和 tag_name 都可以使用,确认一下执行的 SQL
In [47]: Tag.objects.all().extra(select={'tag_name': 'name'}).query.__str__()
Out[47]: u'SELECT (name) AS "tag_name", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag"'
(name) AS "tag_name" 和 "blog_tag"."name"
如果我们只想其中一个能用,可以用 defer 排除掉原来的 name (后面有讲)
In [49]: Tag.objects.all().extra(select={'tag_name': 'name'}).defer('name').query.__str__()
Out[49]: u'SELECT (name) AS "tag_name", "blog_tag"."id" FROM "blog_tag"'
也许你会说为什么要改个名称,最常见的需求就是数据转变成 list,然后可视化等,我们在下面一个里面讲。
annotate 聚合 计数,求和,平均数等
计数
我们来计算一下每个作者的文章数(我们每个作者都导入的Article的篇数一样,所以下面的每个都一样)
In [66]: from django.db.models import Count
In [66]: Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id', 'count')
Out[66]: <QuerySet [{'count': 20, 'author_id': 1}, {'count': 20, 'author_id': 2}, {'count': 20, 'author_id': 4}]>
这是怎么工作的呢?
In [67]: Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id', 'count').query.__str__()
Out[67]: u'SELECT "blog_article"."author_id", COUNT("blog_article"."author_id") AS "count" FROM "blog_article" GROUP BY "blog_article"."author_id"'
SELECT author_id, COUNT(author_id) AS count FROM blog_article GROUP BY author_id
我们也可以获取作者的名称 及 作者的文章数
In [72]: Article.objects.all().values('author__name').annotate(count=Count('author')).values('author__name', 'count')
Out[72]: <QuerySet [{'count': 20, 'author__name': u'WeizhongTu'}, {'count': 20, 'author__name': u'twz915'}, {'count': 20, 'author__name': u'xiaoming'}]>
这时候会查询两张表,细心的同学会发现,因为作者名称中 blog_author 这张表中,author_id 在 blog_article 表中本身就有的
求和 与 平均值
求一个作者的所有文章的得分(score)平均值
In [6]: from django.db.models import
In [7]: Article.objects.values('author_id').annotate(avg_score=Avg('score')).values('author_id', 'avg_score')
Out[7]: <QuerySet [{'author_id': 1, 'avg_score': 86.05}, {'author_id': 2, 'avg_score': 83.75}, {'author_id': 5, 'avg_score': 85.65}]>
执行的SQL
In [8]: Article.objects.values('author_id').annotate(avg_score=Avg('score')).values('author_id', 'avg_score').qu
...: ery.__str__()
Out[8]: u'SELECT "blog_article"."author_id", AVG("blog_article"."score") AS "avg_score" FROM "blog_article" GROUP BY "blog_article"."author_id"'
求一个作者所有文章的总分
In [12]: from django.db.models import
In [13]: Article.objects.values('author__name').annotate(sum_score=Sum('score')).values('author__name', 'sum_score')
Out[13]: <QuerySet [{'author__name': u'WeizhongTu', 'sum_score': 1721}, {'author__name': u'twz915', 'sum_score': 1675}, {'author__name': u'zhen', 'sum_score': 1713}]>
执行的SQL
In [14]: Article.objects.values('author__name').annotate(sum_score=Sum('score')).values('author__name', 'sum_sco
...: re').query.__str__()
Out[14]: u'SELECT "blog_author"."name", SUM("blog_article"."score") AS "sum_score" FROM "blog_article" INNER JOIN "blog_author" ON ("blog_article"."author_id" = "blog_author"."id") GROUP BY "blog_author"."name"'
配置打印sql到日志
开始之前我们修改一个 settings.py 让Django打印出在数据库中执行的语句
settings.py 尾部加上
LOGGING ={
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG'ifDEBUG else'INFO',
},
},
}
这样当 DEBUG 为 True 的时候,我们可以看出 django 执行了什么 SQL 语句
tu@pro ~/zqxt $
In [1]: from blog.models import
In [2]: Author.objects.all()
Out[2]: (0.001) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" LIMIT 21; args=()
<QuerySet [<Author: WeizhongTu>, <Author: twz915>, <Author: dachui>, <Author: zhe>, <Author: zhen>]>
标记背景为 黄色的部分就是打出的 log。
select_related 优化一对一,多对一查询
tu@pro ~/zqxt $
In [1]: from blog.models import
In [2]: Author.objects.all()
Out[2]: (0.001) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" LIMIT 21; args=()
<QuerySet [<Author: WeizhongTu>, <Author: twz915>, <Author: dachui>, <Author: zhe>, <Author: zhen>]>
标记背景为 黄色的部分就是打出的 log。
假如,我们取出10篇Django相关的文章,并需要用到作者的姓名
In [13]: articles = Article.objects.all()[:10]
In [14]: a1 = articles[0] # 取第一篇
(0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 1; args=()
In [15]: a1.title
Out[15]: u'Django \u6559\u7a0b_1'
In [16]: a1.author_id
Out[16]: 5
In [17]: a1.author.name # 再次查询了数据库,注意!!!
(0.000) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" WHERE "blog_author"."id" = 5; args=(5,)
Out[17]: u'zhen'
这样的话我们遍历查询结果的时候就会查询很多次数据库,能不能只查询一次,把作者的信息也查出来呢?
当然可以,这时就用到 select_related,我们的数据库设计的是一篇文章只能有一个作者,一个作者可以有多篇文章。
文章”和“作者”的关系就是多对一,换句说说,就是一篇文章只可能有一个作者。
In [18]: articles = Article.objects.all().select_related('author')[:10]
In [19]: a1 = articles[0] # 取第一篇
(0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score", "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_article" INNER JOIN "blog_author" ON ("blog_article"."author_id" = "blog_author"."id") LIMIT 1; args=()
In [20]: a1.title
Out[20]: u'Django \u6559\u7a0b_1'
In [21]: a1.author.name # 嘻嘻,没有再次查询数据库!!
Out[21]: u'zhen'
prefetch_related 优化一对多,多对多查询
和 select_related 功能类似,但是实现不同。
select_related 是使用 SQL JOIN 一次性取出相关的内容。
prefetch_related 用于 一对多,多对多 的情况,这时 select_related 用不了,因为当前一条有好几条与之相关的内容。
prefetch_related是通过再执行一条额外的SQL语句,然后用 Python 把两次SQL查询的内容关联(joining)到一起
“文章”与“标签”是多对多的关系。
In [24]: articles = Article.objects.all().prefetch_related('tags')[:10]
In [25]: articles
Out[25]: (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 10; args=()
(0.001) SELECT ("blog_article_tags"."article_id") AS "_prefetch_related_val_article_id", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); args=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
<QuerySet [<Article: Django 教程_1>, <Article: Django 教程_2>, <Article: Django 教程_3>, <Article: Django 教程_4>, <Article: Django 教程_5>, <Article: Django 教程_6>, <Article: Django 教程_7>, <Article: Django 教程_8>, <Article: Django 教程_9>, <Article: Django 教程_10>]>
遍历查询的结果:
不用 prefetch_related 时
In [9]: articles = Article.objects.all()[:3]
In [10]: for a in
print
...:
(0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 3; args=()
(0.000) SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" = 1 LIMIT 21; args=(1,)
Django 教程_1 <QuerySet [<Tag: Django>]>
(0.000) SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" = 2 LIMIT 21; args=(2,)
Django 教程_2 <QuerySet [<Tag: Django>]>
(0.000) SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" = 3 LIMIT 21; args=(3,)
Django 教程_3 <QuerySet [<Tag: Django>]>
用 prefetch_related 我们看一下是什么样子
In [11]: articles = Article.objects.all().prefetch_related('tags')[:3]
In [12]: for a in
print
...:
(0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 3; args=()
(0.000) SELECT ("blog_article_tags"."article_id") AS "_prefetch_related_val_article_id", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" IN (1, 2, 3); args=(1, 2, 3)
Django 教程_1 <QuerySet [<Tag: Django>]>
Django 教程_2 <QuerySet [<Tag: Django>]>
Django 教程_3 <QuerySet [<Tag: Django>]>
我们可以看到第二条 SQL 语句,一次性查出了所有相关的内容。
defer 排除不需要的字段
在复杂的情况下,表中可能有些字段内容非常多,取出来转化成 Python 对象会占用大量的资源。
这时候可以用 defer 来排除这些字段,比如我们在文章列表页,只需要文章的标题和作者,没有必要把文章的内容也获取出来(因为会转换成python对象,浪费内存)
In [13]: Article.objects.all()
Out[13]: (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content","blog_article"."score" FROM "blog_article" LIMIT 21; args=()
<QuerySet [<Article: Django 教程_1>, <Article: Django 教程_2>, <Article: Django 教程_3>, <Article: Django 教程_4>, <Article: Django 教程_5>, <Article: Django 教程_6>, <Article: Django 教程_7>, <Article: Django 教程_8>, <Article: Django 教程_9>, <Article: Django 教程_10>, <Article: Django 教程_11>, <Article: Django 教程_12>, <Article: Django 教程_13>, <Article: Django 教程_14>, <Article: Django 教程_15>, <Article: Django 教程_16>, <Article: Django 教程_17>, <Article: Django 教程_18>, <Article: Django 教程_19>, <Article: Django 教程_20>, '...(remaining elements truncated)...']>
In [14]: Article.objects.all().defer('content')
Out[14]: (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."score" FROM "blog_article" LIMIT 21; args=() # 注意这里没有查 content 字段了
<QuerySet [<Article: Django 教程_1>, <Article: Django 教程_2>, <Article: Django 教程_3>, <Article: Django 教程_4>, <Article: Django 教程_5>, <Article: Django 教程_6>, <Article: Django 教程_7>, <Article: Django 教程_8>, <Article: Django 教程_9>, <Article: Django 教程_10>, <Article: Django 教程_11>, <Article: Django 教程_12>, <Article: Django 教程_13>, <Article: Django 教程_14>, <Article: Django 教程_15>, <Article: Django 教程_16>, <Article: Django 教程_17>, <Article: Django 教程_18>, <Article: Django 教程_19>, <Article: Django 教程_20>, '...(remaining elements truncated)...']>
only 仅选择需要的字段
只需要查出 作者的名称
In [15]: Author.objects.all().only('name')
Out[15]: (0.000) SELECT "blog_author"."id", "blog_author"."name" FROM "blog_author" LIMIT 21; args=()
<QuerySet [<Author: WeizhongTu>, <Author: twz915>, <Author: dachui>, <Author: zhe>, <Author: zhen>]>
细心的同学会发现,我们让查 name , id 也查了,这个 id 是 主键,能不能没有这个 id 呢?
试一下原生的 SQL 查询
In [26]: authors = Author.objects.raw('select name from blog_author limit 1')
In [27]: author = authors[0]
(0.000) select name from blog_author limit 1; args=()
---------------------------------------------------------------------------
InvalidQuery Traceback (most recent call last)
<ipython-input-27-51c5f914fff2> in <module>()
----> 1author = authors[0]
/usr/local/lib/python2.7/site-packages/django/db/models/query.pyc in __getitem__(self, k)
1275
1276
-> 1277 return list(self)[k]
1278
1279
/usr/local/lib/python2.7/site-packages/django/db/models/query.pyc in __iter__(self)
1250
1251
-> 1252 raise InvalidQuery('Raw query must include the primary key')
1253
1254
InvalidQuery: Raw query must include the primary key
报错信息说 非法查询,原生SQL查询必须包含 主键!
再试试直接执行 SQL
tu@pro ~/zqxt $
SQLite version 3.14.0 2016-07-26 15:17:14
Enter ".help" for usage hints.
sqlite> select name from blog_author limit 1;
WeizhongTu <--- 成功!!!
虽然直接执行SQL语句可以这样,但是 django queryset 不允许这样做,一般也不需要关心,反正 only 一定会取出你指定了的字段。
自定义聚合功能
我们前面看到了 django.db.models 中有 Count, Avg, Sum 等,但是有一些没有的,比如 GROUP_CONCAT,它用来聚合时将符合某分组条件(group by)的不同的值,连到一起,作为整体返回。
我们来演示一下,如果实现 GROUP_CONCAT 功能。
新建一个文件 比如 my_aggregate.py
fromdjango.db.models importAggregate, CharField
classGroupConcat(Aggregate):
function ='GROUP_CONCAT'
template ='%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'
def__init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
super(GroupConcat, self).__init__(
expression,
distinct='DISTINCT 'ifdistinct else'',
ordering=' ORDER BY %s'%ordering ifordering isnotNoneelse'',
separator=' SEPARATOR "%s"'%separator,
output_field=CharField(),
**extra )
代码来自:http://stackoverflow.com/a/40478702/2714931(我根据一个回复改写的增强版本)
使用时先引入 GroupConcat 这个类,比如聚合后的错误日志记录有这些字段 time, level, info
我们想把 level, info 一样的 聚到到一起,按时间和发生次数倒序排列,并含有每次日志发生的时间。
ErrorLogModel.objects.values('level', 'info').annotate(
count=Count(1), time=GroupConcat('time', ordering='time DESC', separator=' | ')
).order_by('-time', '-count')
自定义field
这个ListField继承自 TextField,代码如下:
fromdjango.db importmodels
importast
classListField(models.TextField):
__metaclass__ =models.SubfieldBase
description ="Stores a python list"
def__init__(self, *args, **kwargs):
super(ListField, self).__init__(*args, **kwargs)
defto_python(self, value):
ifnotvalue:
value =[]
ifisinstance(value, list):
returnvalue
returnast.literal_eval(value)
defget_prep_value(self, value):
ifvalue isNone:
returnvalue
returnunicode(value) # use str(value) in Python 3
defvalue_to_string(self, obj):
value =self._get_val_from_obj(obj)
returnself.get_db_prep_value(value)
使用它很简单,首先导入 ListField,像自带的 Field 一样使用:
classArticle(models.Model):
labels =ListField()
在终端上尝试(运行 python manage.py shell 进入):
>>> from app.models importArticle
>>> d = Article()
>>> d.labels
[]
>>> d.labels = ["Python", "Django"]
>>> d.labels
["Python", "Django"]
9、admin使用
9.1
python manage.py createsuperuser
127.0.0.1:8000/admin #进行登陆
9.2
到blog下admin.py中注册在model/py中新添加的User:
admin.site.register(User) #后台就可以看到User模块
9.3
user添加后显示的列表显示的都是object,在User类中添加方法:
def __unicode__(self):
return self.name
此时列表显示名字
10、表单
10.1关于csrf
1、 settings.py中MIDDLEWARE中的csrf注释掉
2、在模板中
{% csrf_token %}
10.2
11、文件上传
11.1 一般form上传
11.2 后台admin 的model上传
model.py中
同步数据库字段
urls.py中
后台直接上传
12、会话cookie
12.1 设置cookie
response = HttpResponseRedirect('/blog/index')
response.set_cookie('username',username,3600) //key val expire path domain
return response
12.2 获取cookie
request.COOKIE.get('username','')
13.3 删除cookie
response = HttpResponse()
response.delete_cookie('username') //key path domain
13、会话session
13.1 设置session
request.session['username'] = username
13.2 获取session
request.session.get('username','defaultval')
13.2 删除session
del request.session['username']
14、接受参数
def test(request):
if
request.method ==
'POST'
:
a = request.GET.get('a','defaultVal')
else:
a = request.POST.get('a','defaultVal')
15、auth系统使用