Django ORM操作
ORM对表的操作
- ORM中对表的映射:
models中的类 映射到数据库中的表;
- 单表操作:
- 创建表:
- 直接在app中的models.py中定义类即可:
from django.db import models class UserInfo(models): name = models.CharField(verbose_name='用户名', max_length=32)
- ForeignKey(一对多)操作:
- 创建FK会影响访问速度,若想提高用户的访问速度,最好的办法就是不做FK约束;
- 创建具有ForeignKey的关系表:
class Department(models.Model): """ 部门表 """ title = models.CharField(verbose_name='标题',max_length=32) class UserInfo(models.Model): """ 用户表 """ username = models.CharField(verbose_name='用户名', max_length=32) # 与部门表建立 ForeignKey的关系 depart = models.ForeignKey(verbose_name='所属部门',to="Department")
- ForeignKey()类中的参数解析
- __init__()源码:
class ForeignKey(ForeignObject): ... def __init__(self, to, on_delete=None, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, to_field=None, db_constraint=True, **kwargs): try: to._meta.model_name except AttributeError: assert isinstance(to, six.string_types), ( "%s(%r) is invalid. First parameter to ForeignKey must be " "either a model, a model name, or the string %r" % ( self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT, ) ) else: ...
- to参数解析:
- 与某表(类名)建立ForeignKey的外键关系,值可以是类名,也可是字符串;若是类名的话,被绑定的类,必须写在当前类的前面;
- on_delete参数解析:
- 当被绑定的表中的数据被删除后,当前表中的数据进行的操作;默认同步删除;
- 对应值的用法:
- models.CASCADE,删除部门,则将改部门下的员工全部删除。 + 代码判断 - models.DO_NOTHING,删除部门,什么也不做 - models.PROTECT,删除部门,引发错误ProtectedError - models.SET_NULL,删除部门,则将改部门下的员工所属部门ID设置为空。(将FK字段设置为null=True) - models.SET_DEFAULT,删除部门,则将改部门下的员工所属部门ID设置默认值。(将FK字段设置为default=2) - models.SET,删除部门,则将执行set对应的函数,函数的返回值就是要给改部门下员工设置的新的部门ID。 例如: def func(): models.Users....... return 10 class MyModel(models.Model): user = models.ForeignKey(to="User",to_field="id"on_delete=models.SET(func),) 推荐使用: - models.CASCADE, 删除逻辑时,通过代码判断当前 “部门” 下是否有用户。 - models.SET_NULL,最为稳妥。 沟通之后在确定。
- db_constraint参数解析:
- 控制是否在数据库中为这个外键创建约束,默认值为True;
- 当值为False时,在数据库不建立约束,但是依然可以进行跨表查询;
- limit_choice_to参数解析:
- 限制被绑定的表中显示的数据;
- 示例
from django.db import models class Department(models.Model): """ 部门表 ID 名称 1 教质部 2 Python学院 """ title = models.CharField(verbose_name='标题',max_length=32) class User(models.Model): """ 员工表 ID name depart_id 1 小雪 1 2 冰冰 1 3 小雨 1 4 太亮 2 5 金菊 2 """ name = models.CharField(verbose_name='员工名称',max_length=32) depart = models.ForeignKey(to='Department') class ClassList(models.Model): """ 班级表 """ title = models.CharField(verbose_name='班级名称', max_length=32) # 显示id小于4的所有的User中的数据 bzr = models.ForeignKey(to=User,limit_choices_to={'id__lt':4}) teacher = models.ForeignKey(to=User,limit_choices_to={'id__gte':4})
- related_name参数解析:
- 反向查找时的作用参数,用于让关联的对象反查到源对象
- 若不想被反向查找时,将值设置为"+" 即可
- 示例:
1 from django.db import models 2 3 class Department(models.Model): 4 """ 5 部门表 6 ID 名称 7 1 教质部 8 2 Python学院 9 10 """ 11 title = models.CharField(verbose_name='标题',max_length=32) 12 13 class User(models.Model): 14 """ 15 员工表 16 ID name depart_id 17 1 小雪 1 18 2 太亮 2 19 3 小雨 1 20 4 冰冰 1 21 5 金菊 2 22 23 """ 24 name = models.CharField(verbose_name='员工名称',max_length=32) 25 depart = models.ForeignKey(to='Department') 26 27 28 29 class ClassList(models.Model): 30 """ 31 班级表 32 """ 33 title = models.CharField(verbose_name='班级名称', max_length=32) 34 35 bzr = models.ForeignKey(to=User,related_name='x') 36 teacher = models.ForeignKey(to=User,related_name='y') 37 38 39 from app01 import models 40 41 # 找班主任小雪带的所有班级 42 obj = models.User.objects.filter(name='小雪').first() 43 44 class_list = obj.x.all() 45 for row in class_list: 46 print(row.title) 47 48 # 找老师金鑫带的所有班级 49 obj1 = models.User.objects.filter(name='金鑫').first() 50 51 class_list = obj1.y.all() 52 for row in class_list: 53 print(row.title)
- ManyToMany(多对多)操作:
- MTM在ORM中默认会自动创建关联表;若关联表不需要存放其他数据时可以直接使用;若还需要其他字段请手动创建第三张关联表:
- 两种不同情况示例:
自动创建第三张表(场景:关系表只有boy和girl的id): class Boy(models.Model): name = models.CharField(max_length=32) class Girl(models.Model): name = models.CharField(max_length=32) boy = models.ManyToManyField('Boy')
手动创建第三张表(场景:除了boy和girl的id以外,还需要其他字段): class Boy(models.Model): name = models.CharField(max_length=32) class Girl(models.Model): name = models.CharField(max_length=32) class Boy2Girl(models.Model): b = models.ForeignKey(to='Boy') g = models.ForeignKey(to='Girl') class Meta: unique_together = ( ("b", "g"), )
- OneToOne(一对一)操作:
- 自己关联自己,用于做对表进行扩展,一对一
- 创建示例:
class UserInfo(models.Model): username = models.CharField(verbose_name='标题',max_length=32) class Blog(Model.Model): title = models.CharField(verbose_name='标题',max_length=32) a = models.OneToOneField(to='A')
ORM对数据的基本增删改查
- 示例表结构:
class Department(models.Model): title = models.CharField(verbose_name='标题',max_length=32) class UserInfo(models.Model): name = models.CharField(verbose_name='员工名称',max_length=32) depart = models.ForeignKey(to='Department') roles = models.ManyToManyField(to="Role") class Role(models.Model): title = models.CharField(verbose_name='标题',max_length=32)
- 增:
# 单表增加 # 正常简单增加 models.Department.objects.create(title='销售部') # 利用** 打散增加 models.Department.objects.create(**{'title':'销售部'}) # FK增加 # 增加对象 models.UserInfo.objects.create(name='浮生',depart=models.Department.objects.get(id=1)) # 增加id models.UserInfo.objects.create(name='浮生',depart_id=1) # MTM增加 obj = models.UserInfo.objects.filter(name='浮生').first() obj.roles.add([1,2,3])
- 删:
# 通用:数据对象.delete()直接删除 obj.delete()
- 改:
# update直接更改 models.UserInfo.objects.filter(id__gt=5).update(name='xx') # MTM不可直接更改,需要准确找到该对象,然后使用 set(xxxx)进行更改 obj = models.UserInfo.objects.filter(name='浮生').first() obj.roles.set([2,3,6,7])
- 查:
# 查询该表中的所有数据,返回的是QuerySet对象,可迭代 models.UserInfo.objects.all() # 查询该表中符合条件的数据对象,该数据必须 存在且唯一 models.UserInfo.objects.get(id=1) # 查询该表中符合条件的所有数据对象,返回的是QuerySet对象,可迭代; models.UserInfo.objects.filter(id=1)
# 查询该表中符合条件之外的所有数据对象,返回的是QuerySet对象,可迭代;
models.UserInfo.objects.exclude(id=1) # 将QuerySet对象转换成字典 models.UserInfo.objects.values('id','name') # 将QuerySet对象转换成元组 models.UserInfo.objects.values_list('id','name')
ORM对数据的常用方法
- 排序 order_by
# 正序order_by models.UserInfo.objects.all().order_by(id) # 反转reverse: # 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用 models.UserInfo.objects.all().order_by(id).reverser() # 去重distinct models.UserInfo.objects.all().distinct()
- 连表操作
- 示例中使用的model模型:
1 from django.db import models 2 3 4 class Process(models.Model): 5 """ 6 权限表 7 """ 8 url = models.CharField(verbose_name="路由", max_length=32) 9 name = models.CharField(verbose_name="别名", max_length=32, unique=True) 10 roles = models.ManyToManyField("Role") 11 menu = models.ForeignKey("Menu", null=True, blank=True) 12 13 def __str__(self): 14 return self.url 15 16 17 class Menu(models.Model): 18 """ 19 菜单表 20 """ 21 name = models.CharField(verbose_name="一级菜单", max_length=32) 22 icon = models.CharField(max_length=32) 23 24 def __str__(self): 25 return self.name 26 27 28 class Role(models.Model): 29 """ 30 角色表 31 """ 32 name = models.CharField(verbose_name="角色", max_length=32) 33 user = models.ManyToManyField("UserInfo") 34 35 def __str__(self): 36 return self.name 37 38 39 class UserInfo(models.Model): 40 """ 41 用户表 42 """ 43 name = models.CharField(verbose_name="用户姓名", max_length=32) 44 pwd = models.CharField(verbose_name="密码", max_length=32) 45 46 def __str__(self): 47 return self.name
- 正向跨表查询:
- 对象查询:
- 语法: 数据对象.关联字段.字段
- 示例:
# ForeignKey跨表查询: pro_obj = models.Process.objects.all().first() menu = pro_obj.menu # MTM跨表查询: 查询到的是对象,需要.all() 才可以获得QuerySet类型的数据 pro_obj = models.Process.objects.all().first() role = pro_obj.roles.all()
- 字段查询:
- 语法:models.Process.objects.all().values(关联字段__字段)
- 使用__字段跨表查询的条件:
- 必须是QuerySet对象,单纯的数据对象,不具备.values() 方法;
- 可以当做filter查询条件
- 示例:
# MTM查询 pro_obj = models.Process.objects.filter(id=1).values("roles__id") # FK查询 pro_obj = models.Process.objects.filter(id=1).values("menu__name") # 当做filter查询条件的两种方式 pro_obj = models.Process.objects.get(menu__name="用户管理") pro_obj = models.Process.objects.get(**{"menu__name": "用户管理"})
- 反向跨表查询:
- 从被关联表反向查询有关联字段的表中的信息:
- 对象查询:
- 语法: 被关联数据对象.关联表表名_set
- 示例:
# MTM反向跨表查询: role_obj = models.Role.objects.all().first() pro_obj = role_obj.process_set.all() # FK反向跨表查询: menu_obj = models.Menu.objects.all().first() pro_obj = menu_obj.process_set.all()
- 字段查询:
- 雷同与正向查询
- 语法:models.Process.objects.all().values(关联表名__字段)
- 示例:
# MTM跨表查询 role_obj = models.Role.objects.all().values("process__id") # FK跨表查询 menu_obj = models.Menu.objects.all().values("process__id")
- 聚合查询:
- MySQL中的聚合函数,对应Django中的 django.db.models 里面的 Avg, Sum, Max, Min, Count 五种函数;
- 执行聚合函数,需要使用aggregate();
- aggregate() 是QuerySet的一个终止子句,会返回一个包含一些键值对的字典,且只能放在最后;
- aggregate() 参数中使用聚合函数,该聚合函数还可以进行命名;
- aggregate() 中的聚合子句:Avg, Sum, Max, Min, Count
- 示例:
# 简单使用,aggregate(聚合子句) obj = models.Process.objects.all().aggregate(Sum("id")) # 对查询到的数据进行重新命名 obj = models.Process.objects.all().aggregate(sum_id=Sum("id"))
- 分组查询:
- Django中ORM的分组利用的是annotate();等同于数据库中分组的用法, 函数中,需要传入分组条件,也可使用聚合子句;
- 语法:models.Process.objects.values("分组条件").annotate(分组中使用的聚合子句)
- 简单示例:
obj = models.Process.objects.values("id").annotate()
- 筛选条件的双下划线方法
# __lt 大于 # __gt 小于 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # __in 在...之中 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # __contains 包含... # __icontains 包含...(对大小写不敏感) models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的 models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # __range 范围 models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and # ...
ORM对数据的高级用法
- F查询:
- Q查询:
- only:加速查询
- defer:不查询某些字段
- select_related:帮助开发者进行主动连表查询。
- prefetch_related:不进行连表查询,将两张表或多张表分别查询
- 执行原生SQL的三种放法:
- connections
from django.db import connection, connections # cursor = connections['db1'].cursor() cursor = connection.cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1,]) # row = cursor.fetchall() # 获取符合条件的所有数据,models.User.objects.all() row = cursor.fetchone() # 获取符合条件的第一条数据,models.User.objects.all().first()
- raw
def raw(self, raw_query, params=None, translations=None, using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo where id > 10 ') # 如果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")
- extra
def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 构造额外的查询条件或者映射,如:子查询 UserInfo.objects.extra(where=['headline ? %s'], params=['Lennon']) # select * from userinfo where headline > 'Lennon' UserInfo.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) # select * from userinfo where (foo='a' OR bar = 'a') and baz = 'a' UserInfo.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) """ select id, name, (select col from sometable where othercol > 1) as new_id """ UserInfo.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
ORM所有操作
################################################################## # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # ################################################################## def all(self) # 获取所有的数据对象 def filter(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Q def exclude(self, *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Q def select_related(self, *fields) # 性能相关:表之间进行join连表操作,一次性获取关联的数据。 model.tb.objects.all().select_related() model.tb.objects.all().select_related('外键字段') model.tb.objects.all().select_related('外键字段__外键字段') def prefetch_related(self, *lookups) # 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 # 获取所有用户表 # 获取用户类型表where id in (用户表中的查到的所有用户ID) models.UserInfo.objects.prefetch_related('外键字段') from django.db.models import Count, Case, When, IntegerField Article.objects.annotate( numviews=Count(Case( When(readership__what_time__lt=treshold, then=1), output_field=CharField(), )) ) students = Student.objects.all().annotate(num_excused_absences=models.Sum( models.Case( models.When(absence__type='Excused', then=1), default=0, output_field=models.IntegerField() ))) def annotate(self, *args, **kwargs) # 用于实现聚合group by查询 from django.db.models import Count, Avg, Max, Min, 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=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 构造额外的查询条件或者映射,如:子查询 UserInfo.objects.extra(where=['headline ? %s'], params=['Lennon']) # select * from userinfo where headline > 'Lennon' UserInfo.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) # select * from userinfo where (foo='a' OR bar = 'a') and baz = 'a' UserInfo.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) """ select id, name, (select col from sometable where othercol > 1) as new_id """ UserInfo.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), 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中的设置) models.UserInfo.objects.filter(id=5).using('db1') ################################################## # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # ################################################## def raw(self, raw_query, params=None, translations=None, using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo where id > 10 ') # 如果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, field_name, kind, 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','hour',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, *args, **kwargs): # 聚合函数,获取字典类型聚合结果 from django.db.models import Count, Avg, Max, Min, 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, *args, **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': '1111111','u_id': 2, 't_id': 2}) def update_or_create(self, defaults=None, **kwargs): # 如果存在,则更新,否则,创建 # defaults 指定创建时或更新时的其他字段 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, '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) models.User.objects.filter(id__in=[11,21,31]) def delete(self): # 删除 def update(self, **kwargs): # 更新 def exists(self): # 是否有结果 pass