接着第七章的数据查询
这里写数据‘排除’查询
在SQL语句中是 NOT
这里是
在Q查询前使用~即可
# sql语句:SELECT * FROM index_vocation where not(job='网站设计')
In [4]: from django.db.models import Q
In [5]: v = Vocation.objects.filter(~Q(job='网站设计'))
In [6]: v
Out[6]: <QuerySet [<Vocation: 2>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>
In [7]: v[3].job
Out[7]: 'zz'
还可以使用exclude实现不等于查询
In [8]: v = Vocation.objects.exclude(job='网站设计')
In [9]: v
Out[9]: <QuerySet [<Vocation: 2>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>
In [10]: v[3].job
Out[10]: 'zz'
使用count方法统计查询数据的数据量
In [12]: v = Vocation.objects.filter(job='网站设计').count()
In [13]: v
Out[13]: 1
去重查询
distinct方法无需设置参数,去重方法根据values设置的字段执行
sql语句:select distinct job from index_vocation where job=''网站设计
In [16]: v = Vocation.objects.values('job').filter(job='网站设计').distinct()
In [17]: v
Out[17]: <QuerySet [{'job': '网站设计'}]>
In [18]: v = Vocation.objects.values('job').filter(job='网站设计')
In [19]: v
Out[19]: <QuerySet [{'job': '网站设计'}]>
更具字段id降序排列,降序只要在order_by里面的字段前面加'-'即可
order_by还可设置多字段排列,在它的参数中传入多个字段即可
In [21]: v = Vocation.objects.order_by('-id')
In [22]: v
Out[22]: <QuerySet [<Vocation: 12>, <Vocation: 11>, <Vocation: 8>, <Vocation: 7>, <Vocation: 5>, <Vocation: 3>, <Vocation: 2>]>
聚合查询(不理解实现)
主要是下方的sum何用;求和,对job进行group by 分组(sql语句,django的实现更简洁)
可参考该资料https://www.cnblogs.com/jingfengling/p/5962182.html
实现对数据值求和,求平均值等。由annotate和aggregate方法实现
annotate类似于SQL里面的GROUP BY方法
如果不设置values,默认对主键进行GROUP BY分组
# sql:select job,sum(id) as 'id_sum' from index_vocation group by job
In [23]: from django.db.models import Sum,Count
In [24]: v = Vocation.objects.values('job').annotate(Sum('id'))
In [25]: print(v.query)
SELECT "index_vocation"."job", SUM("index_vocation"."id") AS "id__sum" FROM "index_vocation" GROUP BY "index_vocation"."job"
aggregate是计算某个字段的值并只返回计算结果
# sql: select count(id) as 'id_count' from index_vocation
In [35]: v = Vocation.objects.aggregate(id_count=Count('id'))
In [36]: v
Out[36]: {'id_count': 7}
In [37]: v['id_count']
Out[37]: 7
union、intersection和difference语法
每次查询结果的字段必须相同,第一次查询为v1,第二次为v2
In [48]: v1 = Vocation.objects.filter(payment=6666)
In [49]: v1
Out[49]: <QuerySet [<Vocation: 2>, <Vocation: 5>]>
In [50]: v2 = Vocation.objects.filter(payment=6667)
In [51]: v2
Out[51]: <QuerySet [<Vocation: 3>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>
使用sql的union来组合两个或多个查询结果的并集
获取两次查询结果的并集
In [52]: v1.union(v2)
Out[52]: <QuerySet [<Vocation: 2>, <Vocation: 3>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>
使用sql的intersect来获取两个或多个查询结果的交集
获取两次查询结果的交集
In [53]: v1.intersection(v2)
Out[53]: <QuerySet []>
使用sql语句的except来获取两个或多个查询结果的差
以v2为共同数据,去除v1和v2的共同数据
In [54]: v2.difference(v1)
Out[54]: <QuerySet [<Vocation: 3>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>
若想使用不等于,大于等模糊的查询,可以使用如下的匹配符
只要在查询字段的末端使用相应的匹配符,就能实现不同的数据查询方式
匹配符
可作用在filter和get上
__exact 精确等于,如sql的like'xxx'
__iexact 精确等于并忽略大小写
__contains 模糊匹配,如sql的like'%荣耀%'
__icontains 模糊匹配,忽略大小写
__gt 大于
__gte 大于等于
__lt 小于
__lte小于等于
__in 判断是否在列表内
startswith 以...开头
istartwith 以...开头并忽略大小写
__endswith 以...结尾
__iendswith 以...结尾并忽略大小写
__range 在...范围内
__year 日期字段的年份
__month 日期字段的月份
__day 日期字段的天数
__isnull 判断是否为空
查询小于等于payment为6667的数据
In [56]: v2 = Vocation.objects.filter(payment__lte=6667)
In [57]: v2
Out[57]: <QuerySet [<Vocation: 2>, <Vocation: 3>, <Vocation: 5>, <Vocation: 7>, <Vocation: 8>, <Vocation: 11>, <Vocation: 12>]>
多表查询
正向查询
一对多里面的多的外键查询
In [65]: v = Vocation.objects.filter(id=2).first()
In [66]: v
Out[66]: <Vocation: 2>
## 通过外键name去查询模型PersonInfo所对应的数据
In [67]: v.name.hireDate
Out[67]: datetime.date(2018, 9, 18)
反向查询
查询主体是一对多模型中的一,通过一查询到与他关联到的模型多的数据
查询模型PersonInfo某行数据对象p
这里一对多数据库一为 PersonInfo,多为Vocation
In [68]: p = PersonInfo.objects.filter(id=2).first()
In [69]: p
Out[69]: <PersonInfo: Tim>
# 方法一
#vocation_set的返回值为queryset对象,即查询结果
# vocation_set为模型Vocation的名称小写
# 模型Vocation的外键字段name不能设置参数related_name
# 若设置参数related_name,则无法使用vocation_set
In [70]: v= p.vocation_set.first()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-70-ff74ffcfbda9> in <module>
----> 1 v= p.vocation_set.first()
AttributeError: 'PersonInfo' object has no attribute 'vocation_set'
## 方法二
# 由模型Vocation的外键字段name的参数related_name实现
# 外键字段name必须设置参数related_name才有效,否则无法查询。
# 将外键字段name的参数related_name设为personinfo
In [76]: p = PersonInfo.objects.filter(id=4).first()
In [77]: v= p.personinfo.first()
In [78]: v
Out[78]: <Vocation: 3>
In [79]: v.job
Out[79]: '网站设计'
正向查询和反向查询还可在查询条件(filter,get)里使用,无论是正向查询还是反向查询,他们在数据库里需要执行两次SQL查询,第一次是查询某张数据表的数据,再通过外键关联获取另外一张数据表的数据信息。
为了减少查询次数,提高查询效率,
可以使用select_related或prefetch_related方法实现,该方法只需执行依次SQL查询就能实现多表查询。
select_related方法
执行依次SQL查询就能实现多表查询。
#select_related,针对一对一或一对多,使用sql的join语句进行优化的
通过减少sql查询的次数来进行优化和提高性能
# 以PersonInfo为查询对象
# 使用left outer join方式查询两个数据表
# 查询模型PersonInfo的字段name和模型Vocation的字段payment
# select_related参数为personinfo,代表外键字段name的参数related_name
# 若要得到其他数据表的关联数据,则可用双下划线'__'连接字段名
# 双下划线连接字段名必须是外键字段名或外键字段参数related_name
In [92]: p = PersonInfo.objects.select_related('personinfo').values('name','personinfo__payment')
In [93]: print(p.query)
SELECT "index_personinfo"."name", "index_vocation"."payment" FROM "index_personinfo" LEFT OUTER JOIN "index_vocation" ON ("index_personinfo"."id" = "index_vocation"."name_id")
# 可以看下p的类型
In [94]: p = PersonInfo.objects.select_related('personinfo')
In [95]: print(type(p))
<class 'django.db.models.query.QuerySet'>
In [96]: p.values('name','personinfo__payment') # 参数一为一中的此段,参数二为多中的字段,要加双下划线(一对多数据库)
Out[96]: <QuerySet [{'name': 'Lucy', 'personinfo__payment': 6666}, {'name': 'Tim', 'personinfo__payment': None}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Mary', 'personinfo__payment': 6667}, {'name': 'Tony', 'personinfo__payment': 6666}]>
以模型Vocation为查询对象
select_related使用INNER JOIN 方式查询两个数据表
select_related的参数为name,代表外键字段name
In [98]: v = Vocation.objects.select_related('name').values('name','name__age')
In [99]: v
Out[99]: <QuerySet [{'name': 1, 'name__age': 20}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 4, 'name__age': 24}, {'name': 5, 'name__age': 25}]>
In [101]: print(v.query)
SELECT "index_vocation"."name_id", "index_personinfo"."age" FROM "index_vocation" INNER JOIN "index_personinfo" ON ("index_vocation"."name_id" = "index_personinfo"."id")
select_related还支持3个或3个以上的数据表同时查询
就是通关外键两两关联
这样的查询都是通过一张表的数据,查询另外一张表的数据
本例中的模型关系,模型Person通过外键living关联模型City,模型City通过外键province关联模型Province,从而使3个模型形成一种递进关系。
下方的参数说明:
···
living是模型Person的外键字段,该字段指向模型City
province是模型City的外键字段,该字段指向模型Province
···
In [107]: P = Person.objects.select_related('living__province').get(name='Lucy')
In [108]: P.living.province
Out[108]: <Province: 浙江省>
prefetch_related
是分别查询每张数据表,然后由python语法来处理他们之间的关系,对于多对多查询,prefetch_related更有优势
而select_related是由SQL语句的join实现的,使用会增加数据查询时间和内存占用。
本例是创建两个模型分别为performer和program,这里面分别添加人员信息和节目信息,然后设置两表为多对多就会生成一张多对多字段的表,该表名字是设置多对多的那张表名_多对多的外键字段名(_是连接两个字段名),该表的字段如何设置不清楚;是用他们的主键做关联,数据好像也要自己插入
代码如下,不知为何打印出所有的数据
这应该想要的是查询喜洋洋扮演演员有多少个把,这里查询出来了,performer所对应的是演员人名,再多对多表中program为节目名称,program_id与performer_id一一对应,这里只取了喜洋洋的id1对应了以他performer表的所有id
# 查询模型Program的某行数据
In [109]: p = Program.objects.prefetch_related('performer').filter(name='喜洋洋').first()
# 根据外键字段performer获取当前数据的多对多多线
In [110]: p.performer.all()
Out[110]: <QuerySet [<Performer: Lily>, <Performer: Lilei>, <Performer: Tom>, <Performer: Hanmei>]>
In [111]: p
Out[111]: <Program: 喜洋洋>
In [112]: p.performer
Out[112]: <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager at 0x1d2ad52ca20>
In [113]: p.performer.first()
Out[113]: <Performer: Lily>
多对多的表在数据库中查找不出来
NameError: name 'people_name_many_to_many' is not defined
第七章剩余部分
extraSQL语句的执行方法
源码位置:django/db/models/query.py
适用于ORM难以实现的查询条件,将查询条件使用原生SQL语法实现,此方法需要模型对象,在某种程度上可防止SQL注入。
该字段共有6个参数
order_by:设置数据的排序方式
where设置查询条件
params为where提供数值(where设置了字符串格式化%s)
select 新增查询字段
select_params为select的%s提供数值
table 连接其他数据表,可实现多表查询
where和params参数的使用
# where设置查询条件,params为where提供数值(where设置了字符串格式化%s)
In [3]: Vocation.objects.extra(where=['job=%s'],params=['网站设计'])
Out[3]: <QuerySet [<Vocation: 3>]>
select,select_params
# 新增查询字段select,select_params为select的%s提供数值
In [5]: v = Vocation.objects.extra(select={'seat':'%s'},select_params=['seatInfo'])
In [6]: print(v.query)
SELECT (seatInfo) AS "seat", "index_vocation"."id", "index_vocation"."job", "index_vocation"."title", "index_vocation"."payment", "index_vocation"."name_id" FROM "index_vocation"
tables连接其他数据表,可实现多表查询
# 连接其他数据表
In [7]: v = Vocation.objects.extra(tables=['index_personinfo'])
In [8]: print(v.query)
SELECT "index_vocation"."id", "index_vocation"."job", "index_vocation"."title", "index_vocation"."payment", "index_vocation"."name_id" FROM "index_vocation" , "index_personinfo"
raw的功能和extra相同,实现数据库查询
raw源码位置在:django/db/models/query.py
字段参数
raw_quert是必选参数,sql语句
parames:如果raw_quert设置字符串格式化%s,那么该参数为raw_quert提供值
translations:为查询的字段设置别名
using:数据库对象,即Django所连接的数据库
raw_query
raw_quert是必选参数,sql语句
In [9]: v = Vocation.objects.raw('select * from index_vocation')
In [10]: v[0]
Out[10]: <Vocation: 2>
execute语法
它执行sql语句无需经过Django的ORM框架,他连接数据库类似于使用第三方模块mysqlclient,可通过游标的方式来执行sql语句
# 导入连接的模块
In [25]: from django.db import connection
# 产生游标
In [26]: cursor = connection.cursor()
# 执行sql语句
In [27]: cursor.execute('select * from index_vocation')
Out[27]: <django.db.backends.sqlite3.base.SQLiteCursorWrapper at 0x13607385dc8>
# 读取第一行数据
In [28]: cursor.fetchone()
Out[28]: (2, '文员', '前台文员', 6666, 1)
# 读取全部数据
In [29]: cursor.fetchall()
Out[29]:
[(3, '网站设计', '前端开发', 6667, 4),
(5, '项目经理', '项目负责人', 6666, 5),
(7, 'jj', 'hh', 6667, 4),
(8, 'zz', 'java', 6667, 4),
(11, 'aa1', 'ss1', 6667, 4),
(12, 'aa2', 'ss2', 6667, 4)]
execute能够执行所有的sql语句,但很容易受到sql注入攻击,一般不建议使用该方法实现数据操作。
数据库事务
django的事务定义在transaction.py文件中。
该文件定义了两个类和16个函数方法
开发中常用的函数方法如下:
atomic() :在视图函数视图类中使用事务
savepoint():开启事务
savepoint_rollback():回滚事务
savepoint_commit():提交事务
实例views.py
from django.shortcuts import render
from .models import *
from django.db import transaction
from django.db.models import F
@transaction.atomic # 使函数支持事务操作
def index(request):
# 开启事务保护
sid = transaction.savepoint()
try:
id = request.GET.get('id', '') # ?/id=xxx,这样就可以在url中传入参数了
if id:
v = Vocation.objects.filter(id=id) # 提取Url中输入的id
v.update(payment=F('payment') + 1) # 更新payment加1
print('Done') # 执行语句在控制台会输出啊
# 提交事务
# 如不设置,当程序执行完成后,会自动提交事务
# transaction.savepoint_commit(sid)
else:
# 全表的payment字段自减1
Vocation.objects.update(payment=F('payment') - 1)
# 事务回滚,将全表payment字段自减1的操作撤回
transaction.savepoint_rollback(sid)
except Exception as e:
# 事务回滚
transaction.savepoint_rollback(sid)
return render(request, 'index.html', locals())
使用with模块实现事务操作
除了使用装饰器@transaction.atomic外
还可以使用with模块实现事务操作
def index(request):
# 开启事务保护
# sid = transaction.savepoint()
with transaction.atomic():
sid = transaction.savepoint()
....
当网站的数据量越来越庞大时,使用单个数据库处理很容易使数据库系统瘫痪,从而导致整个网站瘫痪。
为了减轻数据库系统的压力,django可以同时连接和使用多个数据库。
项目同时配置两个mysql数据库
两个mysql数据库名分别为db1,db2
# 配置多数据库
# 新增dbRouter.py文件编写类DbAppsRouter
DATABASE_ROUTERS = ['MyDjango.dbRouter.DbAppsRouter']
DATABASE_APPS_MAPPING = { # 设置数据库与项目应用的映射关系
# 设置每个App的模型使用的数据库
# {'app_name':'database_name',}
'admin': 'defualt',
'index': 'db1', # 代表index的models.py所定义的模型都在数据库db1里创建数据表
'user': 'db2',
}
多个数据库实现需要编写的配置文件,貌似什么都不用更改,只需要在settings.py中配置正确即可
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING # 从配置文件获取DATABASE_APPS_MAPPING的值
class DbAppsRouter(object):
"""
A router to control all database operations on models for different
databases.
In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
will fallback to the `default` database.
Settings example:
DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
类DbAppsRouter根据变量DATABASE_MAPPING(数据库与项目的映射关系)来设置数据库的
读取(类方法 db_for_read)、写入(类方法 db_for_write)、数据表关系(类方法 allow_relation)和数据迁移(类方法 allow_migrate)
"""
def db_for_read(self, model, **hints): # 读取
""""Point all read operations to the specific database."""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **hints): # 写入
"""Point all write operations to the specific database."""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **hints): #数据表关系
"""Allow any relation between apps that use the same database."""
db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
if db_obj1 and db_obj2:
if db_obj1 == db_obj2:
return True
else:
return False
return None
# 用于创建数据表
def allow_migrate(self, db, app_label, model_name=None, **hints): # 数据迁移
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(app_label) == db
elif app_label in DATABASE_MAPPING:
return False
return None
两个不同的数据库中无法建立数据表关系(一对多等关系),
这样并不能创建mysql数据库,这样python manage.py migrate语句只在Sqlite3数据库里床火箭DJango内置功能的数据表。若要为多个mysql数据库设定指定数据表,需要在migrate指令中设置参数
--database==数据库名
python manage.py migrate s --database==数据库名
但我执行这个报错,我去
django.db.utils.ConnectionDoesNotExist: The connection =db2 doesn't exist
笔记来源:Django Web应用开发实战