Python全棧之路--Django ORM詳解

ORM:(在django中,根據代碼中的類自動生成數據庫的表也叫--code first)

ORM:Object Relational Mapping(關係對象映射)html

咱們寫的類表示數據庫中的表前端

咱們根據這個類建立的對象是數據庫表裏的一行數據python

obj.id  obj.name.....就是數據庫一行數據中的一部分數據ajax

ORM--First:

咱們在學習django中的orm的時候,咱們能夠把一對多,多對多,分爲正向和反向查找兩種方式。sql

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查詢之旅!!!django

建表+配置url+views中寫相應的函數json

models.py(在django中僅且只能在這裏寫數據庫的相關類)app

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的一對多:

咱們在設計表結構的時候何時使用一對多呢?

好比咱們在創建用戶的時候有個菜單讓咱們選擇用戶類型的時候,使用一對多!!

一、一對多的正向查找:

正向查: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))

方法2、

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='alex@qq.com')
    models.HostAdmin.objects.create(username='mosson', email='mosson@qq.com')
    models.HostAdmin.objects.create(username='aliven', email='aliven@qq.com')
    models.HostAdmin.objects.create(username='wusir', email='wusir@qq.com')

數據中的效果:

空的關係表

多對多正向、反向添加數據

正向添加數據:

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='alex@qq.com')
    models.UserMap.objects.create(username='seven', email='seven@qq.com')
    models.UserMap.objects.create(username='mosson', email='mosson@qq.com')

    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建立

  二、查找

    正向查找

      在經過filter的時候跨表使用 雙下劃線 '__'

      在獲取值得時候經過.跨表

    反向查找

      Django自動生成 表名_set

      其餘操做和正向查找同樣

2、多對對

  一、自動生成關係表

    間接的方式獲取關係表,若是是正向的:一行數據的對象.ManyToMany字典就行   反向:一行數據的對象.表名_set

  二、自定義關係表(推薦)不論是添加、修改只對關係表操做就行

3、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)
相關文章
相關標籤/搜索