Python全栈之路--Django ORM详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python全栈之路--Django ORM详解相关的知识,希望对你有一定的参考价值。

ORM:(在django中,根据代码中的类自动生成数据库的表也叫--code first)

ORM:Object Relational Mapping(关系对象映射)

我们写的类表示数据库中的表

我们根据这个类创建的对象是数据库表里的一行数据

obj.id  obj.name.....就是数据库一行数据中的一部分数据

ORM--First:

我们在学习django中的orm的时候,我们可以把一对多,多对多,分为正向和反向查找两种方式。

class UserType(models.Model):
    caption = models.CharField(max_length=32)

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    user_type = models.ForeignKey(‘UserType‘)#外键

正向查找:ForeignKey在 UserInfo表中,如果从UserInfo表开始向其他的表进行查询,这个就是正向操作,反之如果从UserType表去查询其他的表这个就是反向操作。

马上就要开始我们的orm查询之旅!!!

建表+配置url+views中写相应的函数

models.py(在django中仅且只能在这里写数据库的相关类)

class UserType(models.Model):
    caption = models.CharField(max_length=32)

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    user_type = models.ForeignKey(‘UserType‘)

这里我们使用sqlite3数据库,在settings中使用默认设置就可以了

url.py中的配置

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^user_type‘,views.user_type),
    url(r‘^user_info‘,views.user_info),
]

views.py先不进行任何操作,我们先保证正常访问已经设置的url

from django.shortcuts import render,HttpResponse

def user_type(req):
    return HttpResponse("ok")

def user_info(req):
    return HttpResponse("ok")

先在表中插入几个数据用于测试:

usertype表

技术分享

userinfo表数据插入:

所以我们在创建UserType数据的时候有两种方法:第一种方法是直接根据这个字段进行添加数据!给user_type 加 ‘_id‘

def user_info(request):
    dic = {‘username‘:‘mosson‘,‘age‘:18,‘user_type_id‘:1}
    models.UserInfo.objects.create(**dic)
    return HttpResponse(‘OK‘)

或者通过对象添加

#先获取组的对象
usertype = models.UserType.objects.fiter(id=2)
#添加的时候直接添加对象就可以
models.UserInfo.objects.create(username=‘seven‘,age=18,user_type=usertype)

#写成一行也行
models.UserInfo.objects.create(username=‘lile‘,age=18,user_type=models.UserType.objects.filter(id=1))

django的get方法是从数据库的取得一个匹配的结果,返回一个对象,如果记录不存在的话,它会报错。
django的filter方法是从数据库的取得匹配的结果,返回一个对象列表,如果记录不存在的话,它会返回[]。

技术分享

ORM的一对多:

我们在设计表结构的时候什么时候使用一对多呢?

比如我们在建立用户的时候有个菜单让我们选择用户类型的时候,使用一对多!!

1、一对多的正向查找:

正向查:ForeignKey在UserInfo表里,如果根据UserInfo这张表去查询这两张关联的表的合起来的内容就是正向查

反向查:ForeignKey不在UserType里,如果根据UserType这张表去查询这两张关联的表的合起来的内容就是反向查

正向查-demo1--查询所有用户为COO 的用户

在django中外键就相当于简单的使用__连表,在外键那个对象中封装了user_type表中的所有字段

我们要查询所有用户为CEO的用户,我们是不是的根据UserType这张表去查,如果是跨表查询使用“双下划线” + 属性

from app01 import models
def index(req):
    ret = models.UserInfo.objects.filter(user_type__caption=‘COO‘)
    print(ret)
    for item in ret:
        print(item,item.username,item.age,item.user_type.caption)
    return HttpResponse("OK")

查询结果:

技术分享

反向查询:--

我们可以根据下面的命令,取出一个用户组,那么对于这个用户组他有多少个用户呢?

models.UserType.objects.get(id=1)
obj=models.UserType.objects.get(id=1)
    obj.caption====得到在UserType表中id为1对应的caption
    obj.id======得到在UserType表中id为1
    obj.userinfo_set  #理解为一种能力,可以获取所有当前这个用户类型的用户/或者这个用户类型的多个用户
    obj.userinfo_set.all() #获取所有用户类型为COO的用户
    obj.userinfo_set.filter(username=‘tim‘) #获取用户类型为COO的并且用户为tim的用户

    ‘‘‘
    这.userinfo_set.相当于什么?它相当于  models.UserInfo.objects.filter(user_type=obj)

    ‘‘‘

反向查询实例:查询COO用户下的所有的用户名

 ret = models.UserType.objects.filter(caption=‘COO‘).values(‘userinfo__username‘)
    for item in ret:
        print(item,type(item))

方法二、

ret = models.UserType.objects.filter(caption=‘COO‘).first()

    for item in  ret.userinfo_set.all():
        print(item.username)

总结:

  正向查找:

    filter(跨表的时候,应该是对象__跨表的字段)

    获取这个值的时候,拿到了一行数据的时候 line.对象.跨表的字段

  反向查找:

    filter(关联这个表的表明) 自动创建和表明相同的对象,通过这个对象__跨表的字段

    line.自动创建和表明相同的对象_set.方法

技术分享

ORM多对多  系统生成第三张表:

多对多和一对多没有任何关系

models.py

class Host(models.Model):
    hostname = models.CharField(max_length=32)
    port = models.IntegerField()

class HostAdmin(models.Model):
    username = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    host = models.ManyToManyField(‘Host‘)

当我们在host表里和hostadmin表里添加数据时和第三章关系表没有任何关系,当我们这样去建立表时,第三张表里面的列就已经固定了,分别是这两个表的id

给主机表添加数据:

def index(req):
    #主机数据
    models.Host.objects.create(hostname=‘host1.test.com‘, port=80)
    models.Host.objects.create(hostname=‘host2.test.com‘, port=80)
    models.Host.objects.create(hostname=‘host3.test.com‘, port=80)
    models.Host.objects.create(hostname=‘host4.test.com‘, port=80)
    #用户数据
    models.HostAdmin.objects.create(username=‘alex‘, email=‘[email protected]‘)
    models.HostAdmin.objects.create(username=‘mosson‘, email=‘[email protected]‘)
    models.HostAdmin.objects.create(username=‘aliven‘, email=‘[email protected]‘)
    models.HostAdmin.objects.create(username=‘wusir‘, email=‘[email protected]‘)

数据中的效果:

技术分享

技术分享

空的关系表

技术分享

多对多正向、反向添加数据

技术分享

正向添加数据:

def index(request):

    #正向添加数据

    #找到用户dali这个
    admin_obj = models.HostAdmin.objects.get(username=‘dali‘)
    #找到主机
    host_list = models.Host.objects.filter(id__lt=3)
    #通过找到的dali的对象.add去添加数据
    admin_obj.host.add(*host_list)
    ‘‘‘
    admin_obj 通过大力这个对象.add 去操作的主机,
    大力的ID为 2 主机ID为:(1,2)
    那就会生成这样的表:
    #2 1
    #2 2
    ‘‘‘
    return HttpResponse(‘OK‘)

反向添加数据:

def index(request):

    #反向添加数据
    #获取主机
    host_obj = models.Host.objects.get(id=3)
    #获取用户列表
    admin_list = models.HostAdmin.objects.filter(id__gt=1)
    #和一对多一样的道理
    host_obj.hostadmin_set.add(*admin_list)
    #host_obj = 3   管理员ID = 2 3 4
    #3 2
    #3 3 
    #3 4    
    return HttpResponse(‘OK‘)

ORM 多对多 自定义 第三张表

class HostInfo(models.Model):
    hostname = models.CharField(max_length=32)
    port = models.IntegerField()

class UserMap(models.Model):
    username = models.CharField(max_length=32)
    email = models.CharField(max_length=32)

    #through告诉Django用那张表做关联
    host = models.ManyToManyField(HostInfo , through=‘HostRelation‘)

class HostRelation(models.Model):
    host = models.ForeignKey(‘HostInfo‘)
    user = models.ForeignKey(‘UserMap‘)

    ‘‘‘
    并且这里我们可以添加多个关系,比如在加一个字段
    usertype = models.ForeignKey(‘UserType‘)
    或者增加一个普通字段
    status = models.CharField(max_length=32)
    ‘‘‘

现在咱们自己创建了第三张表了。现在我们已经会了两种方式创建第三张表了,当我们使用自定义创建的第三张表的时候,在去添加数据的时候!

就不能使用第一种方式对象添加了!

现在我们有第三张表了这个对象了,我们就不需要管另外两张表了,直接添加就行了! 0 0 !

添加主机和用户--

def index(req):

    models.HostInfo.objects.create(hostname=‘alex.test.com‘, port=80)
    models.HostInfo.objects.create(hostname=‘seven.test.com‘, port=80)
    models.HostInfo.objects.create(hostname=‘mosson.test.com‘, port=80)

    models.UserMap.objects.create(username=‘alex‘, email=‘[email protected]‘)
    models.UserMap.objects.create(username=‘seven‘, email=‘[email protected]‘)
    models.UserMap.objects.create(username=‘mosson‘, email=‘[email protected]‘)

    return HttpResponse(‘ok‘)

将数据插入到第三张表中---办法1:

插入单条数据

def index(request):
    models.HostRelation.objects.create(
        user = models.UserMap.objects.get(id=1),
        host = models.HostInfo.objects.get(id=1)
    )
    return HttpResponse(‘OK‘)

多对多两种方式对比和查询

技术分享

查询--方法一:

第一种方式都是基于表中的对象去找到第三张表! 通过间接的方式找到这张表的句柄!

    #正向查
    admin_obj = models.HostAdmin.objects.get(id=1)
    admin_obj.host.all()
    #反相差
    host_obj = models.Host.objects.get(id=1)
    host_obj.hostadmin_set.all()

查询--方法二:

用第二种方法就没有正向和反向这么一说了,直接查即可!

    relation_list = models.HostRelation.objects.all()
    for item in relation_list:  #每一个item就是一个关系
        print (item.user.username)
        print( item.host.hostname)
relation_list = models.HostRelation.objects.filter(user__username=‘mosson‘)
    for item in relation_list:  #每一个item就是一个关系
        print( item.user.username)
        print (item.host.hostname)

通过第二种方式可以把所有的关系都找到,第一种方式可以把所有的关系表都找到吗?

第一种方式只能找到某一个人管理的机器,不能把有的对应关系找到!

select_related的作用:

class UserType(models.Model):
    caption = models.CharField(max_length=32)

class UserInfo(models.Model):
    user_type = models.ForeignKey(‘UserType‘) #这个user_type是一个对象,对象里面封装了ID和caption
    username = models.CharField(max_length=32)
    age = models.IntegerField()

select_related的作用,他就是用来优化查询的,没有他也可以,用它主要是用来优化ForeignKey

def index(request):
    ret = models.UserInfo.objects.all()
    #咱们看下他执行的什么SQL语句
    print( ret.query)
‘‘‘
SELECT "app01_userinfo"."id", "app01_userinfo"."user_type_id", "app01_userinfo"."username", "app01_userinfo"."age" FROM "app01_userinfo"
‘‘‘

加上select_related是什么样子的

def user_info(request):
    ret = models.UserInfo.objects.all().select_related()
    #咱们看下他执行的什么SQL语句
    print ret.query

SELECT "app01_userinfo"."id", 
       "app01_userinfo"."user_type_id", 
       "app01_userinfo"."username", 
       "app01_userinfo"."age", 
       "app01_usertype"."id", 
       "app01_usertype"."caption" 

       FROM
       "app01_userinfo" INNER JOIN "app01_usertype" ON ("app01_userinfo"."user_type_id" = "app01_usertype"."id")

所以说select_related就是优化查询的!

ORM连表操作的梳理:

一、一对多创建

  1、创建数据

    通过对象创建

    或者通过对象字段_id创建

  2、查找

    正向查找

      在通过filter的时候跨表使用 双下划线 ‘__‘

      在获取值得时候通过.跨表

    反向查找

      Django自动生成 表名_set

      其他操作和正向查找一样

二、多对对

  1、自动生成关系表

    间接的方式获取关系表,如果是正向的:一行数据的对象.ManyToMany字典就行   反向:一行数据的对象.表名_set

  2、自定义关系表(推荐)不管是添加、修改只对关系表操作就行

三、select_related

  用于优化查询,一次性将查询的表和ForiegnKey关联的表一次性加载到内存。

Django中的F和Q

F:用来批量修改数据(使用查询条件的值)

demo:比如我有个price这个列,我想让price自增10或者某一些自增10

 # from django.db.models import F
 # models.Tb1.objects.update(num=F(‘num‘)+10)

Q:用来做条件查询的

默认的django的查询只是且操作如下:

models.UserInfo.objects.filter(username=‘mosson‘,age=‘18‘)

找到用户名为:mosson且age=18的数据

有没有这么一种情况:username=mosson或 username=wusir 或 username=alex 并且 age=18的需求?原生的查询是不支持的!所以就用到了Q~

第一步:
#生成一个搜索对象
search_q = Q()

#在生成两个搜索对象
search1 = Q()
search2 = Q()

第二步:
#标记search1中的搜索条件为  ‘ 或’  查询
search1.connector = ‘OR‘

#把搜索条件加入到search1中
search1.children.append((‘字段名‘,‘字段内容‘))
search1.children.append((‘字段名‘,‘字段内容‘))
search1.children.append((‘字段名‘,‘字段内容‘))
search1.children.append((‘字段名‘,‘字段内容‘))


#标记search2中的搜索条件为  ‘ 或’  查询
search2.connector = ‘OR‘

#把搜索条件加入到search2中
search2.children.append((‘字段名‘,‘字段内容‘))
search2.children.append((‘字段名‘,‘字段内容‘))
search2.children.append((‘字段名‘,‘字段内容‘))
search2.children.append((‘字段名‘,‘字段内容‘))

第三步:
#把多个搜索条件进行合并
search_q.add(search1,‘AND‘)
search_q.add(search2,‘AND‘)

第四步:
#执行搜索
models.HostInfo.objects.filter(search_q)

实例:

在前端获取搜索条件的时候我把相同类型的搜索写成字典的形式{字段名:[条件结合列表]},这样我在查询的时候直接通过循环字典就可以把搜索条件分为不同的子条件!

 function SearchSubmit() {
            //清空当前列表
            $(‘#table-body‘).children().remove();
            //设置一个空字典
            SEARCH_DIC = {};
            //找到所有的搜索框体
            var search_data = $(‘.inputs‘).find("input[is-condition=‘true‘]");
            //循环找到的内容
            $.each(search_data,function (index,data) {
                //获取搜索的类型
                /*
                这里需要注意:重点:::::
                这里和Django的Q可以进行耦合,在我们定义搜索的类型的时候可以直接写成我们要搜索的的‘库中的字段或者条件都可以!!!‘
                如下:
                <input is-condition="true" type="text" placeholder="逗号分割多条件" class="form-control no-radius" name="hostname" />
                */
                var search_name = $(this).attr(‘name‘);
                //获取搜索的值
                var search_value = $(this).val();
                if(SEARCH_DIC.hasOwnProperty(search_name)){//判断是否有这个KEY
                    SEARCH_DIC[search_name].push(search_value)
                }else{
                    SEARCH_DIC[search_name] = [search_value]
                }


            } );//ajax请求结束
            $.get("{% url ‘search_info‘ %}",{‘search_list‘:JSON.stringify(SEARCH_DIC)},function(callback){
                $(‘#table-body‘).append(callback)
            });//搜索按钮结束

重点:

在前端我们定义input的name属性的时候,我们可以直接定义为数据库中的“字段名”,并且在Django的Q中支持跨表操作“双下划线”,所以我们在定义name的时候可以直接定义双下划线操作

search1.children.append((‘字段名‘__‘跨表字段名‘,‘跨表字段内容‘))
@login_auth
def search_info(request):
    #获取用户请求的数据
    user_post = json.loads(request.GET[‘search_list‘])
    print user_post
    #生成搜索对象
    Serach_Q = Q()
    #循环字典并生成搜索条件集合
    for k,v in user_post.items():
        #生成一个搜索结合
        q = Q()
        #生命集合中的搜索条件为‘或‘条件
        q.connector = ‘OR‘
        #循环字典中的value,value是前端传过来的条件集合
        for i in v:
            #在搜索条件集合中增加条件,条件为元组形式,k为字典中的key! key是字段名或者跨表字段名或者支持_gt等
            #i为字典中的vlaue中的元素,为条件
            #
            q.children.append((k,i))
        #没循环一次后后,吧他加入到总的搜索条件中
        Serach_Q.add(q,‘AND‘)
    #使用总的搜索条件进行查询
    data = models.HostInfo.objects.filter(Serach_Q)
    #拼接字符串并返回
    html = []
    for i in data:
        html.append(
            "<tr>"+
                "<td>" + "<input type=‘checkbox‘ >"+ "</td>" +
                "<td name=‘host_id‘>" + ‘%s‘ %i.id + "</td>" +
                "<td name=‘host_name‘ edit=‘true‘>" + i.hostname + "</td>"+
                "<td name=‘host_ip‘ edit=‘true‘>" + i.hostip + "</td>"+
                "<td name=‘host_port‘ edit=‘true‘>" + ‘%s‘ %i.hostport + "</td>"+
                "<td name=‘host_business‘ edit=‘true‘ edit-type=‘select‘ global-key=‘BUSINESS‘ select-val=‘" + ‘%s‘ %i.hostbusiness_id + "‘>" + i.hostbusiness.hostbusiness + "</td>"+
                "<td name=‘host_status‘ edit=‘true‘ edit-type=‘select‘ global-key=‘STATUS‘ select-val=‘" + ‘%s‘ %i.hoststatus_id + "‘>" + i.hoststatus.hoststatus + "</td>"+
            "</tr>"
        )

    html = mark_safe("".join(html))
    return HttpResponse(html)

  

 

以上是关于Python全栈之路--Django ORM详解的主要内容,如果未能解决你的问题,请参考以下文章

西游之路——python全栈——ORM之SQLAlchemy外键与relationship的关系

1Python全栈之路系列之Django初体验

5Python全栈之路系列之Django模型续

7Python全栈之路系列之Django表单

4Python全栈之路系列之Django模型

2Python全栈之路系列之Django路由与视图