Django在Mysql中的基本操作
Django在Mysql中的基本操作
1. Model性能相关操作:select_related、prefetch_related
1、普通查询的缺点
1. 例:现在有两张表user,和group两张表,在user表中使用m作为ForeignKey与group表进行一对多关联 2. 如果通过user表中的实例查找对应的group表中的数据,就必须重复发sql请求 3. prefetch_related()和select_related()的设计目,都是为了减少SQL查询的数量,但是实现的方式不一样
2、select_related作用
1. select_related主要针一对一和多对一关系进行优化。 2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。
3、prefetch_related()作用
1. prefetch_related()主要对于多对多字段和一对多字段进行优化 2. 进行两次sql查询,将查询结果拼接成一张表放到内存中,再查询就不用发sql请求
4、select_related与prefetch_related 使用原则
1. prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量,但是实现的方式不一样 2. 因为select_related()总是在单次SQL查询中解决问题,而prefetch_related()会对每个相关表进行SQL查询,因此select_related()的效率高 3. 所以尽可能的用select_related()解决问题。只有在select_related()不能解决问题的时候再去想prefetch_related()
5、select_related举例说明
作用:使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化
select_related举例说明
def index(request): #1 这种方法低效 users = models.User.objects.all() #拿到的仅仅是user表中内容 for row in users: print(row.user,row.ut_id) #这里打印user表中的内容不必再次sql请求 print(row.ut.name) #第一次查表,没有拿到关联表ut字段中的内容 #所以每次循环都会再次发sql请求,拿到ut.name的值,低效 #2 使用这种方法也仅需要一次数据库查询(拿到的是字典),但是如果查找的不在那些字段中直接报错 users = models.User.objects.all().values('user','pwd','ut__name') #3 select_related()可以一次sql查询拿到所有关联表信息 users = models.User.objects.all().select_related() # 这里还支持指定只拿到那个关联表的所有信息,比如:有多个外键关联,只拿到与ut外键关联的表 users = models.User.objects.all().select_related('ut') # select_related() 接受depth参数,depth参数可以确定select_related的深度。 # Django会递归遍历指定深度内的所有的OneToOneField和ForeignKey。以本例说明: # zhangs = Person.objects.select_related(depth = d) # d=1 相当于 select_related(‘hometown’,'living’) # d=2 相当于 select_related(‘hometown__province’,'living__province’)
6、prefetch_related举例说明
作用:进行两次sql查询,将查询结果拼接成一张表
prefetch_related举例说明
def index(request): users = models.User.objects.filter(id__gt=30).prefetch_related('ut') #ut和tu是user表的两个foreign key,分别关联不同的表 users = models.User.objects.filter(id__gt=30).prefetch_related('ut','tu') #1 先执行第一次sql查询:select * from users where id > 30; #2 比如:第一次查询结果,获取上一步骤中所有ut_id=[1,2] #3 然后执行第二次sql查询:select * from user_type where id in [1,2] #4 这样就仅执行了两次sql查询将两个表的数据拼到一起,放到内存中,再查询就不用发sql请求 for row in users: print(row.user,row.ut_id) #这里
打印user表中的内容不必再次sql请求
2. F()和Q()查询语句
models.py创建表
from django.db import models class Student(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name
2.1 F() ---- 专门取对象中某列值的操作
作用:F()允许Django在未实际链接数据的情况下具有对数据库字段的值的引用
F()将指定字段自动加1
from django.shortcuts import HttpResponse from app01 import models from django.db.models import F,Q def orm(request): # 每访问一次数据库中zhangsan的年纪就会自动增加1 models.Student.objects.filter(name='zhangsan').update(age=F("age") + 1) # 自动生成Student表中数据 ''' stu_list = [{'name':'zhangsan','age':11}, {'name': 'lisi', 'age': 22}, {'name': 'wangwu', 'age': 33},] for u in stu_list: models.Student.objects.create(**u) ''' return HttpResponse('orm')
2.2、Q() ---- 复杂查询(用法1)
1、Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询 2、可以组合使用 &(and),|(or),~(not)操作符,当一个操作符用于两个Q的对象,它产生一个新的Q对象 3、如: Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
复杂查询
from django.shortcuts import HttpResponse from app01 import models from django.db.models import F,Q def orm(request): # 查找学生表中年级大于1小于30姓zhang的所有学生 stus = models.Student.objects.filter( Q(age__gt=1) & Q(age__lt=30), Q(name__startswith='zhang') ) print('stu',stus) #运行结果:[<Student: zhangsan>] # 自动生成Student表中数据 ''' stu_list = [{'name':'zhangsan','age':11}, {'name': 'lisi', 'age': 22}, {'name': 'wangwu', 'age': 33},] for u in stu_list: models.Student.objects.create(**u) ''' return HttpResponse('orm')
2.3 Q() ---- 动态添加多个and和or查询条件(用法2)
crm项目中动态添加or查询条件
def table_search(request,admin_class,object_list): search_key = request.GET.get('_q','
# and和or结合查询 #1. 导入模块 >>> from crm import models >>> from django.db.models import Q #2. q1:查询id=1或者id=2的所有条目 (or条件) >>> q1 = Q() >>> q1.connector = 'OR' >>> q1.children.append(('id',1)) >>> q1.children.append(('id',2)) #3. q2:查询id=1的所有条目 (or条件) >>> q2 = Q() >>> q2.connector = 'OR' >>> q2.children.append(('id',1)) #4. con:结合q1和q2条件结果是查询id=1的所有条目 (结合q1,q2的and条件) >>> con = Q() >>> con.add(q1,'AND') <Q: (OR: ('id', 1), ('id', 2))> >>> con.add(q2,'AND') <Q: (AND: (OR: ('id', 1), ('id', 2)), ('id', 1))> >>> models.Customer.objects.values('qq','name').filter(con) <QuerySet [{'qq': '123456765432', 'name': 'haha'}]>
') q_obj = Q() q_obj.connector = 'OR' for column in admin_class.search_fields: q_obj.children.append(('%s__contains'%column,search_key)) res = object_list.filter(q_obj) return res
or动态添加多个查询条件
# or动态添加多个查询条件 >>> from crm import models >>> from django.db.models import Q >>> con = Q() #1. 实例化一个Q()查询类 >>> con.connector = "OR" #2. 指定使用‘OR’条件 >>> con.children.append(('qq__contains','123')) #3. qq字段中包含‘123’ >>> con.children.append(('name__contains','name0')) #4. name字段中包含‘naem0’ >>> con <Q: (OR: ('qq__contains', '123'), ('name__contains', 'name0'))> #5. 查找name字段中包含‘naem0’或qq字段包含‘123’的所有条目 >>> models.Customer.objects.values('qq','name').filter(con)
and和or结合查询
# and和or结合查询 #1. 导入模块 >>> from crm import models >>> from django.db.models import Q #2. q1:查询id=1或者id=2的所有条目 (or条件) >>> q1 = Q() >>> q1.connector = 'OR' >>> q1.children.append(('id',1)) >>> q1.children.append(('id',2)) #3. q2:查询id=1的所有条目 (or条件) >>> q2 = Q() >>> q2.connector = 'OR' >>> q2.children.append(('id',1)) #4. con:结合q1和q2条件结果是查询id=1的所有条目 (结合q1,q2的and条件) >>> con = Q() >>> con.add(q1,'AND') <Q: (OR: ('id', 1), ('id', 2))> >>> con.add(q2,'AND') <Q: (AND: (OR: ('id', 1), ('id', 2)), ('id', 1))> >>> models.Customer.objects.values('qq','name').filter(con) <QuerySet [{'qq': '123456765432', 'name': 'haha'}]>
**3、解决时差配置问题的报错 **
-
错误:Are time zone definitions for your database and pytz installed?
-
解决方法:安装pytz模块,在Django settings中配置数据库的时区: pip3 install pytz
在settings中配置数据库时区
# 注释的是数据库默认的配置 # LANGUAGE_CODE = 'en-us' # TIME_ZONE = 'UTC' # USE_TZ = True USE_TZ = False LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Chongqing'
4、Trunk函数基本使用
4.1models.py中创建数据库表
from django.db import models class Experiment(models.Model): start_datetime = models.DateTimeField()
4.2views.py使用
from django.shortcuts import HttpResponse from .models import * from datetime import datetime from django.db.models import Count, DateTimeField from django.db.models.functions import Trunc def orm(request): #1 首先向Experiment表中插入三条数据 Experiment.objects.create(start_datetime=datetime(2015, 6, 15, 14, 30, 50, 321)) Experiment.objects.create(start_datetime=datetime(2015, 6, 15, 14, 40, 2, 123)) Experiment.objects.create(start_datetime=datetime(2015, 12, 25, 10, 5, 27, 999)) #2 使用Trunk过滤出start_datetime字段,仅保存到日,后面的时分秒全部变成0 #3 annotate(experiments=Count('id'))可以根据天进行分组 experiments_per_day = Experiment.objects.annotate( start_day=Trunc('start_datetime', 'day', output_field=DateTimeField()) ).values('start_day').annotate(experiments=Count('id')) for exp in experiments_per_day: print(exp['start_day'], exp['experiments']) return HttpResponse('orm') # 运行结果: # 2015-06-15 00:00:00+00:00 2 # 2015-12-25 00:00:00+00:00 1 # 从运行结果中可以看出,2015-06-15日有两条记录,2015-12-25日有1条记录
4.3、Trunk函数应用:查询当天签到同学的id
trunk函数应用
def attendance(request): """签到页面""" current_datetime = datetime.now() year = current_datetime.year month = current_datetime.month day = current_datetime.day if request.method == 'GET': if course_id: # 获取当天已签到的记录 attended_students = models.Attendance.objects.annotate( attend_day=functions.Trunc('attend_time','day',output_field=DateTimeField(), ) ).filter(attend_day=datetime(year, month, day), course_id=course_id).values_list('student_id', flat=True) #Trunc函数的作用是将'attend_time'中的时间过滤成“day”: 2015-06-15 00:00:00+00:00 这种格式 tt = datetime(2016, 3, 12) #运行结果:2016-03-12 00:00:00 # 运行结果: <QuerySet [1, 2]> #这里的1,2 是数字表示当天只有id=1,和2的学生签到 # 注:使用这个查询语法和上面的查询结果完全一样 attended_students = models.Attendance.objects\ .filter(attend_time__date=datetime(year, month, day),course_id=course_id)\ .values_list('student_id', flat=True)
浙公网安备 33010602011771号