【Django】ORM操作#2 -- 2019-08-17 08:00:02
目录
原文: http://blog.gqylpy.com/gqy/264"@ 必知必会的13条查询方法
单表查询之神奇的双下划线
一对多 ForeignKey正向查找 1. 对象查找(跨表) # 获取第一个学生对象: stu_obj = models.Students.objects.first() # 获取这个学生的班级对象: stu_obj.classes # 获取这个学生的班级对象名称: stu_obj.classes.name 2. 字段查找(跨表) # 获取id为7的学生的班级姓名: models.Students.objects.filter(id=7).values_list('classes__name') 反向操作 1. 对象查找 # 获取第一个班级: class_obj = models.Classes.objects.first() # 获取这个班级的所有学生: students = class_obj.students_set.all() # 获取这个班级所有学生的姓名: students.values_list('name') 2. 字段查找 # 获取id为9的班级的所有学生的姓名: models.Classes.objects.filter(id=9).values_list('students__name') 多对多 ManyToManyField"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器. 他存在与下面两种情况:
简单来说,就是当点(.)后面的对象可能存在多个的时候就可以使用以下的方法:
注意: 在Python脚本中调用Django环境Django终端打印SQL语句在Django项目的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',} } 关于Mate类# 班级 class Classes(models.Model): name = models.CharField(max_length=64,verbose_name='姓名',db_column='myname') # db_column:自定义数据库列名 verbose_name:自定义admin网页中显示的字段名 class Meta: # 自定义数据库中生成的表名称: db_table = 'classes' # admin网页中显示的表名称: # verbose_name = "班级" # 会在名称后面加"s" verbose_name_plural = "班级信息" # 不会在名称后面加"s" # 联合索引: # index_together = [ # ("字段1","字段2") # 应为两个存在的字段 # ] # 联合唯一索引: # unique_together = (("字段1","字段2"),) # 应为两个存在的字段 聚合查询 aggregage()aggregate()是QuerySet的一个终止子句(必须放在语句的最末尾),意思是说,他返回一个包含一些键值对的字典. 键的名称是聚合值的标识,值是计算出来的聚合值. 需要用到的函数: from django.db.models import Avg,Sum,Max,Min,Count 示例: # 计算所有书的平均价格: models.Book.objects.all().aggregate(Avg('price')) 可以为聚合的值指定名称(key): models.Book.objects.all().aggregate(avg=Avg('price')) 还可以生成不止一个聚合函数(多个键值对): # 同时获取书的平均价格,最高价格,最低价格: models.Book.objects.all().aggregate(Avg('price'),Max('price'),Min('price')) 分组查询 annotate()我们使用原生SQL语句,按照出版社分组求书的平均价格: select pub,AVG(price) from book group by publisher; 如果使用ORM查询,语法如下: models.Book.objects.values('publisher').annotate(avg=Avg('price')).values('publisher','avg') 连表查询的分组: select publisher.name,AVG(price) from book inner join book on (book.publisher_id=book.id) group by book_id; ORM查询: models.Publisher.objects.annotate(avg=Avg('book__price')).values('name','avg') 更多示例: models.Book.objects.all().annotate(count=Count('author')).values('title','count') 统计出每个出版社卖的最便宜的书的价格: # 方法一: Publisher.objects.annotate(min_price=Min('book__price')).values('name','min_price') # 方法二: models.Book.objects.values('publisher__name').annotate(min_price=Min('price')) 统计不止一个作者的图书: models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1) 根据一本图书作者数量的多少对查询集 QuerySet进行排序: models.Book.objects.annotate(author_num=Count('author')).order_by('author_num') 查询各个作者出的书的总价格: models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price') F查询在上面的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢? Django提供了F()来做这样的比较,==F()的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值.== 导入: from django.db.models import F # 查询库存大于销量的书: models.Book.objects.filter(sale__gt=F('inve')).values('sale','inve') ==Djano支持F()对象之间,以及 F()对象和常数之间的加减乘除和取模的操作:== # 查询库存大于销量2倍的书: models.Book.objects.filter(sale__gt=F('inve') * 2).values('sale','inve') ==修改操作也可以使用F()函数:== # 将每一本书的价格提高10元: models.Book.objects.update(price=F('price') + 10) 引申: from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.update(title=Concat(F('title'),Value("("),Value("第一版"),Value(")"))) Q查询filter()等方法中的关键字参数查询都一起进行"AND"的,如果要执行更复杂的查询(例如OR查询),你可以使用Q对象. 导入: from django.db.models import Q 示例: # 查询作者名是"zyk"或"zyk01"的书: models.Book.objects.filter(Q(author__name="zyk") | Q(author__name="zyk01")).values_list('title','author__name') 可以组合使用 ==&== 和 ==|== 操作符以及使用括号进行分组来编写任意复杂的Q对象。同时,Q对象可以使用 ==~== 操作符取反,这允许组合正常的查询和取反(NOT)查询. 示例: # 查询作者名字是"zyk"并且不是2018年出版的书的书: models.Book.objects.filter(Q(author__name="zyk") & ~Q(publish_date__year=2018)).values_list('title','author__name','publish_date') 查询函数可以混合使用Q对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q对象)都将"AND"在一起。但是,如果出现Q对象,它就必须位于所有关键字参数的前面。也就是所,==Q对象必须放在关键字参数的前面.== 示例: # 查询出版年份是2017或2018,书名中带"逻辑"的所有书: models.Book.objects.filter(Q(publish_date__year=2017) | Q(publish_date__year=2018),title__icontains="逻辑") 模糊查询(项目摘): class SreachArticleView(APIView): """查询文章""" QUERY_FIELD_LIST = [] # 定义可被查询的数据库字段 def post(self,request): try: # 获取被查询的用户的id,和查询关键字 user_id,keyword = int(request.data.get('user_id')),request.data.get('keyword','') # 查询者是否为本人 is_self = True if request.user.id == user_id else False # 先实例化好q对象 q = self.get_search_content(keyword) # 如果有用户id,则查该用户文章,否则查全站 filter_dict = {'is_private': False,'is_draft': False,'is_delete': False} user_id and filter_dict.setdefault('user_id',user_id) query_list = models.BlogInfo.objects.filter(q,**filter_dict) # 进行序列化 query_list = ArticleInfoSerializer(is_show_article=False,instance=query_list.order_by('-release_date'),many=True) return Response({ 'data': query_list.data,'self': is_self,}) except Exception: return Response(ERROR_INFO) def get_search_content(self,keyword): """模糊查询""" q = Q() q.connector = Q.OR for field in self.QUERY_FIELD_LIST or GLOBAL_QUERY_FIELD_LIST: q.children.append(Q((f'{field}__contains',parse.quote(keyword)))) return q 事务==要么都成功,要么都失败.== 要注意的是,即使是事务执行失败,已执行成功的添加记录的表的自增id的计数值依然会增加。 import os if __name__ == '__main__': os.environ.setdefault('DJANGO_SETTINGS_MODULE','blog.settings') import django django.setup() from blog01 import models try: from django.db import transaction with transaction.atomic(): new_publisher = models.Publisher.objects.create(name="西二旗程序员太多出版社") models.Book.objects.create(title="Python终极爬虫",publisher_id=100) # 这里的"publisher_id=100"指定了一个不存在的id,因此这两条语句都失败. except Exception as e: print(str(e)) 其它鲜为人知的操作行级锁: def multi_apply(self): """公户变私户""" ids = self.request.POST.getlist('id') apply_num = len(ids) # 判断当前销售的客户加上要转为私有的客户的总量是否大于最大限制 if self.request.user.customers.count() + apply_num > settings.CUSTOMER_MAX_NUM: return HttpResponse("做人不要太贪心,给别人的机会") # 如果同时有多个销售抢一个客户: with transaction.atomic(): # 事务 # select_for_update:加锁 obj_list = models.Customer.objects.filter(id__in=ids,consultant__isnull=True).select_for_update() if apply_num == len(obj_list): obj_list.update(consultant=self.request.user) else: return HttpResponse("手速太慢") Django ORM执行原生SQL: # extra # 在QuerySet的基础上继续执行子语句 # extra(self,select=None,where=None,params=None,tables=None,order_by=None,select_params=None) # select和select_params是一组,where和params是一组,tables用来设置from哪个表 # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"},select_params=(1,)) # Entry.objects.extra(where=['headline=%s'],params=['Lennon']) # Entry.objects.extra(where=["foo='a' OR bar = 'a'","baz = 'a'"]) # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"},),order_by=['-nid']) 举个例子: models.UserInfo.objects.extra( select={'newid':'select count(1) from app01_usertype where id>%s'},select_params=[1,],where = ['age>%s'],params=[18,order_by=['-age'],tables=['app01_usertype'] ) """ select app01_userinfo.id,(select count(1) from app01_usertype where id>1) as newid from app01_userinfo,app01_usertype where app01_userinfo.age > 18 order by app01_userinfo.age desc """ # 执行原生SQL # 更高灵活度的方式执行原生SQL语句 # from django.db import connection,connections # cursor = connection.cursor() # cursor = connections['default'].cursor() # cursor.execute("""SELECT * from auth_user where id = %s""",[1]) # row = cursor.fetchone() QuerySet方法大全: ################################################################## # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # ################################################################## def all(self) # 获取所有的数据对象 def filter(self,*args,**kwargs) # 条件查询 # 条件可以是:参数,字典,Q def exclude(self,**kwargs) # 条件查询 # 条件可以是:参数,字典,Q def select_related(self,*fields) 性能相关:表之间进行join连表操作,一次性获取关联的数据。 总结: 1. select_related主要针一对一和多对一关系进行优化。 2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。 def prefetch_related(self,*lookups) 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 总结: 1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。 2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。 def annotate(self,**kwargs) # 用于实现聚合group by查询 from django.db.models import Count,Avg,Sum v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')) # SELECT u_id,COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1) # SELECT u_id,COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # SELECT u_id,COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 def distinct(self,*field_names) # 用于distinct去重 models.UserInfo.objects.values('nid').distinct() # select distinct nid from userinfo 注:只有在PostgreSQL中才能使用distinct进行去重 def order_by(self,*field_names) # 用于排序 models.UserInfo.objects.all().order_by('-id','age') def extra(self,select_params=None) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"},)) Entry.objects.extra(where=['headline=%s'],params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'","baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"},order_by=['-nid']) def reverse(self): # 倒序 models.UserInfo.objects.all().order_by('-nid').reverse() # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序 def defer(self,*fields): models.UserInfo.objects.defer('username','id') 或 models.UserInfo.objects.filter(...).defer('username','id') #映射中排除某列数据 def only(self,*fields): #仅取某个表中的数据 models.UserInfo.objects.only('username','id') 或 models.UserInfo.objects.filter(...).only('username','id') def using(self,alias): 指定使用的数据库,参数为别名(setting中的设置) ################################################## # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # ################################################## def raw(self,raw_query,translations=None,using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名 models.UserInfo.objects.raw('select id as nid from 其他表') # 为原生SQL设置参数 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s',params=[12,]) # 将获取的到列名转换为指定列名 name_map = {'first': 'first_name','last': 'last_name','bd': 'birth_date','pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table',translations=name_map) # 指定数据库 models.UserInfo.objects.raw('select * from userinfo',using="default") ################### 原生SQL ################### from django.db import connection,connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""",[1]) row = cursor.fetchone() # fetchall()/fetchmany(..) def values(self,*fields): # 获取每行数据为字典格式 def values_list(self,*fields,**kwargs): # 获取每行数据为元祖 def dates(self,field_name,kind,order='ASC'): # 根据时间进行某一部分进行去重查找并截取指定内容 # kind只能是:"year"(年),"month"(年-月),"day"(年-月-日) # order只能是:"ASC" "DESC" # 并获取转换后的时间 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC') def datetimes(self,order='ASC',tzinfo=None): # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间 # kind只能是 "year","month","day","hour","minute","second" # order只能是:"ASC" "DESC" # tzinfo时区对象 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.DDD.objects.datetimes('ctime',tzinfo=pytz.timezone('Asia/Shanghai')) """ pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) """ def none(self): # 空QuerySet对象 #################################### # METHODS THAT DO DATABASE QUERIES # #################################### def aggregate(self,**kwargs): # 聚合函数,获取字典类型聚合结果 from django.db.models import Count,Sum result = models.UserInfo.objects.aggregate(k=Count('u_id',distinct=True),n=Count('nid')) ===> {'k': 3,'n': 4} def count(self): # 获取个数 def get(self,**kwargs): # 获取单个对象 def create(self,**kwargs): # 创建对象 def bulk_create(self,objs,batch_size=None): # 批量插入 # batch_size表示一次插入的个数 objs = [ models.DDD(name='r11'),models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs,10) def get_or_create(self,defaults=None,**kwargs): # 如果存在,则获取,否则,创建 # defaults 指定创建时,其他字段的值 obj,created = models.UserInfo.objects.get_or_create(username='root1',defaults={'email': '2222211','u_id': 2,'t_id': 2}) def update_or_create(self,**kwargs): # 如果存在,则更新,否则,创建 # defaults 指定创建时或更新时的其他字段 obj,created = models.UserInfo.objects.update_or_create(username='root1','t_id': 1}) def first(self): # 获取第一个 def last(self): # 获取最后一个 def in_bulk(self,id_list=None): # 根据主键ID进行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list) def delete(self): # 删除 def update(self,**kwargs): # 更新 def exists(self): # 是否有结果 复制代码 Django终端打印SQL语句 在Django项目的settings.py文件中,在最后复制粘贴如下代码: 复制代码 LOGGING = { 'version': 1,} } 更多操作请见:【Django】ORM操作#1 原文: http://blog.gqylpy.com/gqy/264(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |