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
model模型

 

  - 正向跨表查询:

    - 对象查询:

      - 语法: 数据对象.关联字段.字段

      - 示例:

# 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所有操作

- 中文官网所有API解析(点我过去)

##################################################################
# 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
posted @ 2018-10-20 16:30  浮生凉年  阅读(213)  评论(0编辑  收藏  举报