django -- models数据库操作---玩弄ORM表的喜怒哀乐(满满的一把骚操作)

一、ORM操作表:

创建表:必须在app文件下的models.py文件中,利用类创建表
        - 注意:创建类的时候,必须继承models.Model基类!
          创建列都是用models去调用方法:
            AutoField()  #设置自增字段
            CharField()  #设置字符串类型的字段
            max_length = XXX  #字符串的长度
            ForeignKey("类名",null=True)  #设置外键 值可以为空

 from django.db import models
        
        class UserInfo(models.Model):
            """
            员工
            """
            nid = models.BigAutoField(primary_key=True)  #设置自增ID  
            user = models.CharField(max_length=32)
            password = models.CharField(max_length=64)
            age = models.IntegerField(default=1) #null=True
            # ug_id
            ug = models.ForeignKey("UserGroup",null=True) #注意创建完之后,生成的列名
        
            注意:一个表中,ID列也可以不设置,django在创建表的过程中,会自动添加上!
        class UserGroup(models.Model):
            """
            部门
            """
            title = models.CharField(max_length=32)

注意: 写完代码之后 一定要先运行django 然后通过两个命令,在数据库中创建生成表:

python3 manage.py makemigrations
python3 manage.py migrate

修改表:(表内有数据也没问题)
        更名,或是再添加一列
            - 注意:给表新增列的时候,这个若这个表中有数据,那么新增列的话,值就不能为空,否则会提示!
                    解决这种问题的有两种方式:就是在该列内写上可以为空 null=True,或是设置初始值:default=x;

写完代码之后,再执行一次上边代码,就完成了对表的修改!
      python3 manage.py makemigrations
      python3 manage.py migrate

删除表:            

操作数据行:(在views.py函数中操作!)
        万年不变的 增删改查  参考ORM操作方法

示例:
# 数据库相关操作
	def index(request):
		# 增删改查
		from app01 import models  #先导入模块
		# 新增
			models.UserGroup.objects.create(title='销售部')
			models.UserInfo.objects.create(user='root',password='pwd',age=18,ug_id=1)
		# 查找
			group_list = models.UserGroup.objects.all()  #获取所有信息
			print(group_list) 
			# group_list获取的是QuerySet,内部元素是UserGroup对象,每一个对象代指一行数据。
			for row in group_list:
				print(row.id,row.title)
			
			group_list = models.UserGroup.objects.filter(id=1) #获取具体信息

		#神奇的双下划线
			group_list = models.UserGroup.objects.filter(id__gt=1)  #__gt  大于某值
			group_list = models.UserGroup.objects.filter(id__lt=1)  #__lt  小于某值


		# 删除   先找后删
			models.UserGroup.objects.filter(id=2).delete()

		# 更新   先找后改
			models.UserGroup.objects.filter(id=2).update(title='公关部')
			group_list = models.UserGroup.objects.all() #更新之后再获取
		
		return render(request,'newindex.html',{"group_list": group_list})

 三、Django ORM 操作数据库补充:(连表,正反向查找信息,取值数据类型) 

- 基础操作:

1、先创建两个表,在表里写上一些数据:(默认使用的数据库为sqlite3,仅为test)

    from django.db import models

    class UserType(models.Model):
        title = models.CharField(max_length=16)

    class UserInfo(models.Model):
        name = models.CharField(max_length=16)
        age = models.IntegerField()
        ut = models.ForeignKey("UserType")

   往表内添加数据……过程略过……
2、连表操作,正反向取值 -----> 正反向操作,实质上都是连表操作,以左边的表为基准(PS:前面表的所有数据都会显示!)查找的数据<正反向操作获取的数据个数不同>。
    - 对用户信息表正向操作:(userinfo,ut是FK字段,通过外键名跨表 - 正向操作)<对象.外键名.关联表的列名>
        - 获取单个数据:

        obj  =  models.UserInfo.objects.all().first() #查看获取的第一个数据
        print(obj.id,obj.name,obj.age,obj.ut_id,obj.ut.title)

        - 获取多条数据

        group_list = models.UserInfo.objects.all()   #查看所有数据
            # group_list获取的是QuerySet,内部元素是UserInfo对象,每一个对象代指一行数据,对象中如果包含ForeignKey,则代指与其关联表的一行数据
        for row in group_list:
            print(row.name)
            print(row.age)
            print(row.ut_id)    # 用户信息关联的部门ID
            print(row.ut.title) # 用户信息关联的部门名称  
   

    - 对用户类型表反向操作:(USerType, 关联表名小写_set.all() - 反向操作)
        #注意点:对于用户类型表,一个类型跨表查询的时候,可能对应着多个用户,所以一定要注意拿到的跨表数据操作。
        - 获取单个跨表数据:   

        obj = models.UserType.objects.all().first()
        row = obj.userinfo_set.all().first()  #取多个跨表数据中的第一个
        print("用户类型",obj.id,obj.title,row)
        print(row.name,row.age)    

        - 获取多条跨表数据

        obj    = models.UserType.objects.all().first()
        row = obj.userinfo_set.all()
        print("用户类型:"obj.id,obj.title)
        print(row)  #发现是一个Queryset类型的,存放好多用户信息表对象的列表,既然是对象那就好操作了!
        for item in row:
            print(item.name,item.age,)

3、获取数据类型及跨表操作<神奇的双下划线__>:(对象obj,字典{"key":"value"},和元组("v1","v2"))
    - 正向跨表操作:    
    # 数据获取多个数据时
        - 1.对象类型[obj,obj,obj,]  ***直接获取表内所有的数据行***

        models.UserInfo.objects.all()
        models.UserInfo.objects.filter(id__gt=1)
        result = models.UserInfo.objects.all()
        for item in result:
            print(item.name,item.ut.title)

        ***字典或是元组类型,是获取表内指定列的数据,同时注意取值的方式!***
        - 2.字典类型 [{id:1,name:fd},{id:1,name:fd},{id:1,name:fd},]  方法:.values("列名1","列名2")  

        models.UserInfo.objects.all().values('id','name')
        models.UserInfo.objects.filter(id__gt=1).values('id','name')
        
        # 无法跨表 没有获取跨表的数据
        result = models.UserInfo.objects.all().values('id','name')
        for item in result:
            print(item['id'],item['name'])

        # 跨表操作 __  (外键名__关联表列名)在values() 写上要查找的跨表列名 
         result = models.UserInfo.objects.all().values('id','name',"ut__title")
        for item in result:
            print(item['id'],item['name'],item['ut__title'])    

        - 3.元组类型 [(1,df),(2,'df')]  方法:.values_list("列名1","列名2")

        models.UserInfo.objects.all().values_list('id','name')
        models.UserInfo.objects.filter(id__gt=1).values_list('id','name')
        # 无法跨表
        result = models.UserInfo.objects.all().values_list('id','name')
        for item in result:
            print(item[0],item[1])
        # 跨表  __
        result = models.UserInfo.objects.all().values_list('id','name',"ut__title")
        for item in result:
            print(item[0],item[1],item[2])
       

    - 反向跨表操作:
        语法操作与正向相同,只是跨表的语法不一样 .
        通过values或是values_list取值,如果需要跨表的话,在括号内写上---->小写的表名
        注意:只写小写的表名  默认只取另一个表id, 小写表名__列名,取得是另一张表中对应列的数据!

#示例如下:以字典类型举例,元组类型的操作一致!
    v = models.UserType.objects.values("id","title")
    v = models.UserType.objects.values("id","title","小写表名")
    v = models.UserType.objects.values("id","title","小写表名__列名")
    print(v)
  - 示例:
        #正向操作:
        user_list = models.UserInfo.objects.values("id","age","name","ut__title")
        print(user_list)
        #反向操作:
        user_lists = models.UserType.objects.values("id","title","userinfo","userinfo__name")
        print(user_lists)

 - 进阶操作:
1、先回顾一下学习的简单的操作方法,如下:

数据库的增删改查:
    models.UserInfo.objects.all()
    models.UserInfo.objects.filter(id=1,id=2)
    models.UserInfo.objects.all().first()
    models.UserInfo.objects.all().count()
    models.UserInfo.objects.all().update()
    models.UserInfo.objects.all().delete()
    models.UserInfo.objects.all()[1:19]  #取第一条到第18条数据

跨表:#实质上是先进行连表操作,再进行的where筛选,最后显示的列名
    正向:
        xxxx.filter(ut__title='值').values('id','name','ut__title')
    反向:
        xxxx.filter(小写表名称__列名='值').values('id','name','小写表名称__列名')
- 示例:    
    #正向先过滤后取值
    obj = models.UserInfo.objects.filter(ut__title="白银用户").values("id","name","ut__title")
    print(obj.query)
    print(obj)
    """
    SELECT "app01_userinfo"."id", "app01_userinfo"."name", "app01_usertype"."title" FROM "app01_userinfo"
     INNER JOIN "app01_usertype" ON("app01_userinfo"."ut_id" = "app01_usertype"."id")
        WHERE "app01_usertype"."title" = 白银用户
    < QuerySet[{'id': 6, 'name': 'rain5', 'ut__title': '白银用户'}] >
    """
    #反向先过滤后取值
    obj = models.UserType.objects.filter(userinfo="3").values("userinfo__name","userinfo__age","title",)
    print(obj.query)
    print(obj)
    """
    SELECT "app01_userinfo"."name", "app01_userinfo"."age", "app01_usertype"."title" FROM "app01_usertype"
     INNER JOIN "app01_userinfo" ON ("app01_usertype"."id" = "app01_userinfo"."ut_id")
        WHERE "app01_userinfo"."id" = 3
    <QuerySet [{'userinfo__name': 'rain2', 'userinfo__age': 19, 'title': '黄金用户'}]>
    """

 2、过滤/条件判断 filter<等同于 where>:
      __gt   --->大于
      __gte  ---> 大于等于
      __lt   ---> 小于
      __lte  ---> 小于等于
      __in = [] ----> 获取 xx 在列表中的值 相当于in 例:id__in = [1,2,3,4,5]
      __range=[] ----> 范围查找 相当于between and
      __contains="xxx" -----> 包含
      __startswith = "xxxxx"  ----->起始值
      __endswith = "xxxxx"  ----->结尾
      .exclude(列名__in=[列表值])  获取不在列表内的值,相当于 not in

 - 示例:
    models.UserInfo.objects.filter(id__gt=1)
    models.UserInfo.objects.filter(id__lt=1)
    models.UserInfo.objects.filter(id__lte=1)
    models.UserInfo.objects.filter(id__gte=1)
    models.UserInfo.objects.filter(id__in=[1,2,3])
    models.UserInfo.objects.filter(id__range=[1,2])
    models.UserInfo.objects.filter(name__startswith='xxxx')
    models.UserInfo.objects.filter(name__contains='xxxx')
    models.UserInfo.objects.exclude(id=1)

 此处需要注意: .exclude([条件]) 方法, 意思是排除满足条件的数据.相当于是取非.所以此处着重说明下他的用途!
  1 可以对当前的所有数据进行筛选操作!
    models.UserInfo.objects.exclude(id=1)
  2 可以对当前已经筛选完的数据再进行二次筛选操作!        
    models.UserInfo.objects.filter(name__startswith='xxxx').exclude(id=1) # 意思就是 过滤查找name是以XXX起始,并且 id不为1 的所有数据.

3、排序:.order_by("列名") <等同于order by 列名 asc or desc> 可以通过多个列名排序!
    - 语法:
        对象.order_by("id") #默认排序是从小到大
        对象.order_by("-id") #从大到小排序

    - 示例:
        user_list = models.UserInfo.objects.values("id","name","age").order_by("id") #从小到大排
        user_list = models.UserInfo.objects.values("id","name","age").order_by("-id") #从大到小排
        user_list = models.UserInfo.objects.values("id","name","age").order_by("id","name") #先按照id从小到大排,如果有重复的再按照name从小到大排列
        print(user_list)

4、分组:.annotate(别名=聚合函数(列名))  <等同于 group by >
    - 语法:
        from django.db.models import Count,max,min,Sum,……  #使用前先导入聚合函数
        .annotate(别名=Count(列名))
        二次筛选----->在分组之后.filter(别名的条件)<__gt:大于;__lt:小于>

    - 示例:
        from django.db.models import Count
        v = models.UserInfo.objects.values("ut_id").annotate(sss = Count("id"))
        print(v.query)  #用于查看sql语句 上条语句的实质是以values中的列作为分组的列。
                        # 如果annotate 中不写任何语句,则只是查看数据包中对应的这列的数据!
        print(v)    #查看获取的数据行

        v1 = models.UserInfo.objects.values("ut_id").filter(ut_id__gte=2).annotate(sss = Count("id")).filter(sss__gt=1)
        print(v1.query) #查看sql语句发现,第一个filter等同于where,第二个filter等同于 having
        print(v1)  #查看获取的数据行

 5、F:
    用于获取表中某列的原始数据 常用于更新操作.update()    

    from django.db.models import F  #导入模块
    models.UserInfo.objects.all().update(age=F("age")+1)

6、Q: 
    - 常用于数据的筛选.filter() 用于构造复杂的查询条件
    - Q使用有两种方式:对象方式,方法方式***

#补充:查询条件是字典类型,将字典作为条件进行筛选
        condition = {
            'id':1,
            'name': 'root'
        }
        v = models.UserInfo.objects.filter(**condition)   #结果为obj对象
    
    from django.db.models import Q #导入模块           
    应用一:对象 组合判断
        from django.db.models import Q
        user_list = models.UserInfo.objects.values("id","name").filter(Q(id__gt=1))  #单个Q对象
        user_list = models.UserInfo.objects.values("id","name").filter(Q(id=2)| Q(id=3)) # 多个Q对象 或关系关联
        user_list = models.UserInfo.objects.values("id","name").filter(Q(id=2) & Q(name="rain1")) # 多个Q对象 与关系关联
        print(user_list)
    应用二:先创建方法,然后添加条件
        q1 = Q()  #生成Q对象
        q1.connector = "OR"  #指定多个对象间连接的关系
        q1.children.append(("id__gt",1))
            #添加的数据:必须是元组类型,第一个是条件,第二个是值
        q1.children.append(("id__lt",5))

        q2 = Q()
        q2.connector = "AND"
        q2.children.append(("id",1))
        q2.children.append(("id",2))

        conn = Q() #生成一个Q对象
        conn.add(q1,"AND")  #以某种关系,添加其他的Q对象
        conn.add(q2,"AND")
        v = models.UserInfo.objects.all().filter(conn)  #通过条件去查找对应数据
        print(v)
    - 若是查询信息是字典格式,想转成Q对象的方法:
    condition_dict = {
        'k1': [1, 2, 3, 4],
        'k2': [1, ],
    }
    con = Q()
    for k, v in condition_dict.items():
        q = Q()
        q.connector = 'OR'
        for i in v:
            q.children.append(('id', i))
        con.add(q, 'AND')
    user_list = models.UserInfo.objects.filter(con)

***当django定义的规则无法完成或是无法满足需求的时候,就需要用到以下两种方式,进行对数据库的操作!***

7、extra:
    (额外查询条件以及相关表,排序)用于子查询,再从别处获取点数据 除filter外可以在外边添加extra,这是同时生效的。
    语法:.extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

使用分类:
        a. 映射
            select = {"别名":"带占位符的原生sql语句"}
            select_params=[按顺序要传递的值]
            #select 此处 from 表
        
         b. 条件
            where=["字符串类型的条件(有占位符)",]
            params=[传值],
            # select * from 表 where 此处
        
        c. 表
            tables = ["表"]
            # select * from 表,此处
            
        d. 排序
            order_by=["列名"]
            # select * from 表 order by 此处
    - 示例:
        v = models.UserInfo.objects.all().extra(
            select={
                'n':"select count(1) from app01_usertype where id=%s or id=%s",
                'm':"select count(1) from app01_usertype where id=%s or id=%s",
            },
            select_params=[1,2,3,4])
        for obj in v:
            print(obj.name,obj.id,obj.n,obj.m)

        v = models.UserInfo.objects.extra(
            where=["id=1","name='alex'"]
        )
        v = models.UserInfo.objects.extra(
            where=["id=1 or id=%s ","name=%s"],
            params=[1,"alex"]
        )

        v = models.UserInfo.objects.extra(
            tables=['app01_usertype'],
        )
        # sql语句"""select * from app01_userinfo,app01_usertype""" 查询两个表,以笛卡尔基的方式显示多条数据
        models.UserInfo.objects.extra(
            tables=['app01_usertype'],
            where = ["app01_usertype.id = app01_userinfo.ut_id"],
            order_by = ["-app01_userinfo.id"],
        )
        # sql语句"""select * from app01_userinfo,app01_usertype where app01_usertype.id = app01_userinfo.ut_id""" 连表操作,where判断
        print(v.query) #查看对应的sql语句
    - 所有方法操作:    
        v = models.UserInfo.objects.filter(id__gt=3).extra(
                tables=["app01_usertype"],
                select={"title":"select title from app01_usertype where id >%s"},
                select_params=[1,],
                where=["app01_userinfo.age>%s","app01_userinfo.ut_id = app01_usertype.id",],
                params=[16,],
                order_by=["-app01_userinfo.id"]
            )
        print(v.query)
        
    """
    SELECT (select title from app01_usertype where id >1) AS "title",
      "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."age", "app01_userinfo"."ut_id"
        FROM "app01_userinfo" , "app01_usertype"
          WHERE ("app01_userinfo"."id" > 3 AND (app01_userinfo.age>16) AND (app01_userinfo.ut_id = app01_usertype.id))
            ORDER BY ("app01_userinfo".id) DESC
        """

8、原生sql语句:
    - 当ORM操作无法完成的时候,我们就需要写原生的sql语句来实现这个操作数据库的功能
   

from django.db import connection, connections #首先先导入模块
        connection  默认连接settings文件中对应的 default 库
        connections["数据库"]  ----> 选择连接settings中设置的不同数据库
        
    #注意:django的机制会连接上数据库,我们就利用现成的连接,访问数据库,创建游标,写原生sql语句并提交。不需要考虑关闭连接的问题。
        #谨慎一些可以把创建的游标关闭,数据库连接由django去做判断
    
    #创建不同类型的游标
    cursor = connection.cursor() # connection=default数据
    cursor = connections['db2'].cursor()
        
    cursor.execute("""SELECT * from auth_user where id = %s""", [1]) #向数据库提交数据
        
    row = cursor.fetchone()  #接收查到的单个信息
    row = cursor.fetchall()  #接收查到的所有信息

总结:
    可以写sql语句的方法:
    1、原生sql语句,
    2、extra
    3、raw

9、其他操作:
    ##################################################################
    # 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()
                )))

    - 以上两种连表操作方法总结:
        优点:约束,节省磁盘空间
        缺点:性能不高,连表越多,性能越差

        对于连表,操作少量的数据表显现不出性能的缺点,
        但是对于大数据的表,或者是访问量巨大,同时追求响应速度的网站,查看搜索数据过大,再加上连表操作的时候,缺点就会显现。
        针对这种情况,大公司都是舍弃磁盘,把数据都放在一个表里操作以提高性能!

        #select_related: 查询主动做连表。<性能相对的会降低>
        # 操作机制:通过外键名,先做连表操作把数据都放在一个对象里,对象再通过外键名.列名去获取数据,而不再做二次查询。
        v = models.UserInfo.objects.all().select_related("ut",……)   #括号内可以写多个外键名,前提是当前表内有多个
        for row in v:
            print(row.id,row.name,row.age,row.ut.title)

        #prefetch_related: 不做连表,做多次查询。
        <避免影响性能,django自己通过外键[过滤之后的]作为条件,对多表都先查询,然后django内部对查询到的结果做连表操作>
        q = models.UserInfo.objects.all().prefetch_related("ut",……) #括号内可以写多个外键名,前提是当前表内有多个
        for row in q:
            print(row.id,row.name,row.age,row.ut.title)
    
    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)
        # 构造额外的查询条件或者映射,如:子查询

        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"}, select_params=(1,), order_by=['-nid'])

     def reverse(self):
        # 倒序
        models.UserInfo.objects.all().order_by('-nid').reverse()
        # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序
        #倒序:只有前边有order_by操作的时候才起作用,否则不起任何作用!倒序只是反转 按照 某列分组 的排序规则


     def defer(self, *fields):
        models.UserInfo.objects.defer('username','id')
        或
        models.UserInfo.objects.filter(...).defer('username','id')
        #映射中排除某列数据
        #defer 获取除了条件列外所有的数据

     def only(self, *fields):
        #仅取某个表中的数据
         models.UserInfo.objects.only('username','id')
         或
         models.UserInfo.objects.filter(...).only('username','id')
        
        #only 只获取条件内的列所对应的数据
            v = models.UserInfo.objects.all().only("id","name")  
        #操作之后的结果还是obj对象,只是对象内的数据只有id和name,当然也可以通过这个对象去获取其他列的信息,就会再往数据库发送一次查询请求!
        #所以说,取了哪个就获取哪个,不要再获取额外的数据!
        
     def using(self, alias):
        #指定使用的数据库,参数为别名(setting中的设置)
        #多个数据库的情况下:using用于指定去某个数据库中取数据,不写默认是default。当然,前提是数据库里得有要操作的表。
        models.UserInfo.objects.all().using("数据库名")
#注意点:数据库操作拿到的对象相同,就能一直调用这个对象对应的方法。obj对象就可以。
    
    ##################################################
    # 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')

        # 如果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(..)
    - 示例:
        #sql语句,查询本表
        v = models.UserInfo.objects.raw("select * from app01_userinfo")
        print(v)
        print(v.query)
        for i in v:
            print(i.id,i.name)
        """
            <RawQuerySet: select * from app01_userinfo>
            select * from app01_userinfo
        """
        #查询另一张表,由于两张表的id名相同,Userinfo在生成对象的时候,会根据id再对自己的表进行一次查询。拿到自己表对应的数据。
        #而sql语句中要找的title列信息,不会存在Userinfo的对象中。
        v = models.UserInfo.objects.raw("select id,title from app01_usertype")
        print(v)
        print(v.query)
        for i in v:
            print(i.id,i.name)
        #SQL是其他表,将名字设置为当前UserInfo表中相对应的列名<id名相同>,
        # UserInfo生成对象的时候,就会把sql语句中查到的数据,按照列名存放<对应的自己表的数据不取,此时就是对sql语句起一个包装的作用>
        v = models.UserInfo.objects.raw("select id,title as name from app01_usertype")
        print(v)
        print(v.query)
        for i in v:
            print(i.id,i.name)
        #将获取到的列名,转换成指定列名 自定义名 = {"获取的列名":"指定列名",.....}
        name_map = {"id":"id","title":"name"}
        v = models.UserInfo.objects.raw("select * from app01_usertype",translations=name_map).using("default")
        print(v)
        print(v.query)
        for i in v:
            print(i.id, i.name)
        
        
    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',), n=Count('nid')) #不对数据进行去重
        ===>{"k":6,"n":6}
        result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) #对数据进行去重
        ===> {'k': 3, 'n': 6}

    def count(self):
       # 获取个数

    def get(self, *args, **kwargs):
       # 获取单个对象
        #get(条件)  能找到数据正常,找不到对应的数据就会报错;找到一条数据正常,找到多条数据会报错;
    def create(self, **kwargs):
       # 创建对象
        #增加会有返回值,表示当前增加的数据,通过obj.id 获取新增的id。
        obj = models.UserInfo.objects.create(XXXXX)
        print(obj.id)
        
    def bulk_create(self, objs, batch_size=None):
        # 批量插入
        # batch_size表示一次插入的个数
        objs = [
            models.UserInfo(name='r11'),   #注意插入数据的类型,没有object
            models.UserInfo(name='r22')
        ]
        models.UserInfo.objects.bulk_create(objs, 10) #batch_size:批量增加,每次最多提交对象的个数,最多不要操作999。

    def get_or_create(self, defaults=None, **kwargs):
        # 如果存在,则获取,否则,创建
        #查找的条件可以有多个,若是存在,则defaults内的字段全部作废,否则创建一条新的数据
        # 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进行查找
       #根据主键进行查询,相当于是in操作,判断主键在不在条件中。
       id_list = [11,21,31]
       models.UserInfo.objects.in_bulk(id_list)

    def delete(self):
       # 删除

    def update(self, **kwargs):
        # 更新

    def exists(self):
       # 是否有结果
       #查看新增之后的数据存不存在,返回的是布尔值。

 四、多表操作:

数据库多对多操作:

1.创建关系表 (灵活)

    class Boy(models.Model):
        name = models.CharField(max_length=16)

    class Girl(models.Model):
        nick = models.CharField(max_length=16)

    #创建多对多的关系表
    class Love(models.Model):
        b = models.ForeignKey("Boy")
        g = models.ForeignKey("Girl")
        #除外键外,创建其他的约束关系(联合索引or联合唯一索引)注意语法结构!
        class Meta:      #定义关系类,固定写法
            #创建联合唯一索引
            unique_together=[      #关系类型 = [("别名1","别名2"),]   #列表,元素是元组
                ("b","g"),
            ],
            #创建联合索引
            index_together=[
                ("c","d"),
            ]  

操作:bulk_create 批量增加

#批量增加数据
    objs = [
        models.Boy(name="小张"),
        models.Boy(name="小王"),
        models.Boy(name="小李"),
        models.Boy(name="小宋"),
    ]
    models.Boy.objects.bulk_create(objs,5)
    
    obs = [
        models.Girl(nick="小红"),
        models.Girl(nick="小黄"),
        models.Girl(nick="小绿"),
        models.Girl(nick="小紫"),
    ]
    models.Girl.objects.bulk_create(obs,5)

    objs = [
        models.Love(b_id=1,g_id=1),
        models.Love(b_id=1,g_id=2),
        models.Love(b_id=1,g_id=3),
        models.Love(b_id=2,g_id=1),
        models.Love(b_id=2,g_id=2),
    ]
    models.Love.objects.bulk_create(objs,5)
    
    #连表操作,查找与小张有过关联的女生
    #字典类型的数据
    love_list = models.Love.objects.filter(b__name="小张").values("g__nick")
    for item in love_list:
        print(item["g__nick"])
    #对象类型的数据
    love_lists = models.Love.objects.filter(b__name="小张").select_related("g")
    for obj in love_lists:
        print(obj.g.nick)

2.通过django自带功能ManyToManyField,完成多对多关系的关联 (任意表添加,但局限性太大)
    - 创建表的过程中,不再写关系表,而是对某个表中添加关联信息,django会自动创建一个以主键为准的信息关联表(表格式:项目名_小写表名_自定义名)。
        有一点需要注意:表内的数据结构是固定的,如果除了建立多对多的关系外还需要记录其他的数据,则该表无法插入新的列,以至于无法完成你的要求!
    - 类内定义的方式:
        自定义名 = models.ManyToManyField("要关联的类名")

    1)创建表:
        class Boy(models.Model):
        name = models.CharField(max_length=16)
        m = models.ManyToManyField("Girl")  #多对多,django会自动创建一个新表,用以存放多对多关系。

        class Girl(models.Model):
            nick = models.CharField(max_length=16)
    
    2)表操作:
        #哪个表建立的关系,就先从这个表操作。
        # 先获取boy对象
        obj = models.Boy.objects.filter(name="小张").first()
        #通过多对多的名,对第三张关系表进行操作!
        
        # 添加数据
        obj.m.add(1) #添加一个数据
        obj.m.add(2,3) #添加多个数据
        obj.m.add(*[4,]) #以列表形式添加
        
        #删除
        obj.m.remove(1)
        obj.m.remove(2,3)
        obj.m.remove(*[4,])
        
        #重置 #把当前对象的数据清空,再重新写入
        obj.m.set([1,])
        
        #查询全部  -->已经完成了跨表 跨到了女生表
        q = obj.m.all()
        print(q)  #查看拿到的数据类型及所属对象,发现全是gril表的对象
        for item in q:
            print(item.nick)  #获取每个对象的名称
        
        #过滤查找
        girl_list = obj.m.values("nick").filter(nick="小红").first()
        print(girl_list)
        
        #反向跨表,通过女生找到约会过的男生
        obj = models.Girl.objects.values("id","nick","boy__name").filter(nick="小红")
        print(obj)
        
        #清表 把关系表内的数据全部清除
        obj.m.clear()

 3.自定义表与django自带功能ManyToManyField合用(杂交):

   1)创建表:
    class Boy(models.Model):
    name = models.CharField(max_length=16)
        m = models.ManyToManyField("Girl",through="Love",through_fields=("b","g",)) #指定通过哪个表连接,用哪几列进行关联!

    class Girl(models.Model):
        nick = models.CharField(max_length=16)

    #创建多对多的关系表
    class Love(models.Model):
        b = models.ForeignKey("Boy")
        g = models.ForeignKey("Girl")
        #除外键外,其他的约束关系(联合索引or联合唯一索引)
        class Meta:
            #创建联合唯一索引
            unique_together=[
                ("b","g"),
            ],
    2)操作:有约束
        可以通过自定义的关系表进行增删改查操作;
        但是通过django自带的关系有会有约束,只能查询或是清表,其他操作会报错。
        查 | 清表
        obj.m.clear() 可以
        
        v = obj.m.all()
        print(v)

4.自关联:
    以相亲关系表举例,男女生信息表都放在一个数据表中,男生可以看到相亲过的女生,同理女生也可以看到相亲过的男生
    
    1.前戏:通过外键表引出:
        - 所有的信息都整合在一张表里,通过另一张按照自定义规则设置的外键表,实现连表操作查询的功能。
        建立外键表:
            - 由于是对同一张表操作,django框架此时就没有那么智能,此时就需要我们自定义约束规则,以规范使用!
        
        PS:补充一点内容:ForeignKey()中的参数:
            - to="表名"
            - to_field="列名" #建立外键关系的列名,默认是id列
            - related_query_name="自定义名" #定义一个反向操作的名称,保留了反向连表操作中_set的属性
            - related_name="自定义名" #定义一个反向操作的名称,直接通过自定义名即可反向操作
            related_query_name和related_name都是定义的反向操作的属性,一般常用于related_name.

            举例说明:-外键表中,反向操作的名字,女生为a,男生为b!
            # related_query_name
                obj对象男.b_set.all()
                obj对象女.a_set.all()
            # related_name
                obj对象男.a.all()
                obj对象女.b.all()

  1)models.py文件中,ORM利用类创建表的代码:

            #创建用户信息表
            class Userinfo(models.Model):
                nickname = models.CharField(max_length=32) #昵称
                username = models.CharField(max_length=32) #用户名
                password = models.CharField(max_length=64) #密码
                gender_choices = (
                    (1,"男"),
                    (2,"女"),
                )
                gender = models.IntegerField(choices=gender_choices) #性别

            #外键关联表
            class U2U(models.Model):
                g = models.ForeignKey("Userinfo",related_name="boys") #设置与女生关联的外键,及反向操作的名称
                b = models.ForeignKey("Userinfo",related_name="girls")#设置与男生关联的外键,及反向操作的名称

  2)给两张表添加数据:
            - 信息表就不用多说了,就是平常添加的顺序,此除多说说关联表,关联表添加数据的方式有两种:

            #第一种:知道ID直接添加:
       models.U2U.objects.create(b_id=1,g_id=4)
            #第二种:先从信息表中,获取相对应的对象,然后在关联表中直接添加这个对象,内部会做转换操作,拿取用户的id。
                boy = models.Userinfo.objects.filter(gender=1,id=2).first()
                gril = models.Userinfo.objects.filter(gender=2,id=5).first()
                models.U2U.objects.create(b=boy,g=gril)    

        3)表和数据都有了,那就剩下操作了,怎么获取相对应的信息!
            - 此处再次普及一下正反向操作的定义:
            正反向操作是以外键为标志,从当前有外键的表,通过外键获取对应表的数据操作称为正向操作;反之,从数据表反向获取关联表的操作,称为反向操作。
            注意一点:不管是正反向操作,获取的对象(数据)都是跨表之后的另一张表的对象(信息)!

           root = models.Userinfo.objects.filter(id=1).first() #先获取一个信息表的对象
           result = root.girls.all()  #通过定义的反向关联的名字,反向查找与当前对象有关系的对象,注意:此处获取的是 U2U表的对象!
           for u in result:
                print(u.g.nickname) #通过拿到的对象,在通过外键关系,正向获取有关系对象的昵称。

2.正题:
        - 既然上边的操作可以实现信息的整合,那么建立联系的外键是否也可以写在一张表里呢?答案是肯定的。这种创建关联的方式就叫做自关联。
        本质:把所有的信息都在一张表上创建,虽然只有一张表,但是我们可以再想象着生成一张一模一样的表,这两个表之间通过外联建立关系。
            虽然是叫做自关联,实质上还是两张表的操作! 
        - M2M自关联特性:(多对多)

#创建自关联的整合表
            class Userinfo(models.Model):
                nickname = models.CharField(max_length=32) #昵称
                username = models.CharField(max_length=32) #用户名
                password = models.CharField(max_length=64) #密码
                gender_choices = (
                    (1,"男"),
                    (2,"女"),
                )
                gender = models.IntegerField(choices=gender_choices) #性别
                m = models.ManyToManyField('Userinfo')

  ManyToManyField有一特性,就是会自动生成一张关系表,我们设置的这个自关联表中,会生成三列,id,from_userinfo_id,to_userinfo_id.  自增id不用说,from_userinfo_id,to_userinfo_id,从列名上就能看出来,分别代表着userinfo表的不同id,一个是从哪儿来,一个是去哪里。

            obj = models.Userinfo.objects.filter(id=1).first()
            # from_userinfo_id  #正向操作 sql语句
            obj.m          => select xx from xx where from_userinfo_id = 1
            
            # to_userinfo_id    #反向操作 sql语句
            obj.userinfo_set => select xx from xx where to_userinfo_id = 1
            
            - 关系表内自定义规则:记住定义了规则之后,一定要严格遵守!!!
                定义:
                    # 前面列:男生ID
                    # 后面列:女生ID
应用:
                # 男生对象
                obj = models.UserInfo.objects.filter(id=1).first()
                # 根据男生ID=1查找关联的所有的女神
                obj.m.all()
                
                # 女生
                obj = models.UserInfo.objects.filter(id=4).first()
                # 根据女生ID=4查找关联的所有的男生
                obj.userinfo_set.all()

- FK自关联:(一对多)
  - 一条信息,对应着多条操作!原理与上边多对多同理,以评论举例!

            创建表:
            class Comment(models.Model):
                """
                评论表
                """
                news_id = models.IntegerField()            # 新闻ID
                content = models.CharField(max_length=32)  # 评论内容
                user = models.CharField(max_length=32)     # 评论者
                reply = models.ForeignKey('Comment',null=True,blank=True,related_name='xxxx')  #评论者 评论内容的ID
            
            表结构:
                """
                ID 新闻ID    评论内容  评论者     reply_id
                1    1        别比比    root         null
                2    1        就比比    root         null
                3    1        瞎比比    shaowei      null
                4    2        写的正好  root         null
                5    1        拉倒吧    由清滨         2
                6    1        拉倒吧1    xxxxx         2
                7    1        拉倒吧2    xxxxx         5
                """
            内容显示:
                """
                新闻1
                    别比比
                    就比比
                        - 拉倒吧
                            - 拉倒吧2
                        - 拉倒吧1
                    瞎比比
                新闻2:
                    写的正好
                """
posted @ 2017-07-01 23:32  细雨蓝枫  阅读(1861)  评论(0编辑  收藏  举报