Django基礎——Model篇(三)

一 Django ORM中的概念

ORM —— 關係對象映射,是Object Relational Mapping的簡寫,是用來簡化數據庫操做的框架

Django ORM遵循Code Frist原則,即根據代碼中定義的類來自動生成數據庫表,對於ORM框架:
  (1)自定義的類表示待建立數據庫的表
  (2)根據自定義類建立的對象obj表示數據庫表中的一行數據
  (3)obj.字段一、obj.字段二、...、obj.字段n表示每一行數據中相應字段的值

ORM中的一對多、一對一以及多對多的概念及應用場景,具體參見前期博客。
ORM中的正向和反向操做,這是相對而言的,主要取決於一對多/一對一/多對多字段寫在哪一個類中。html

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

   對於上面兩張表,UserInfo表是關聯UserType表的,即一對多字段(外鍵)在UserInfo表中。那麼根據UserInfo表去操做數據庫,就表示正向;反之根據UserType表去操做數據庫,就表示反向。數據庫

二 ORM中一對多的操做

  models.py中的表結構django

class UserType(models.Model): caption = models.CharField(max_length=32) class UserInfo(models.Model): # 此處user_type是models.ForeignKey類封裝了UserType類生成的的對象
    # 即user_type是包含了id和caption字段的對象
    user_type = models.ForeignKey(UserType) username = models.CharField(max_length=32) age = models.IntegerField()

   views.py中生成三種用戶類型的代碼app

def user_type(request): # 添加user_type表的數據
    # dic = {'caption': 'CEO'} 對應id爲1
    # dic = {'caption': 'CTO'} 對應id爲2
    # dic = {'caption': 'COO'} 對應id爲3
    # models.UserType.objects.create(**dic)
    print models.UserType.objects.all() return HttpResponse('ok')

  生成的表結構以下圖所示:框架

  1 UserInfo表添加數據的兩種方法性能

  (1)數據庫級別優化

    上表是UserInfo數據庫表結構,外鍵user_type字段默認會加上"_id"進行存儲,那麼從數據庫級別添加數據的代碼以下:spa

def user_info(request): # 一對多:外鍵
    # 一對多中添加user_info表數據:數據庫級別

    # 第1種方法(數據庫級別):按外鍵在數據庫中的存儲方式
    dic = {'username':'xx','age':18,'user_type_id':1} models.UserInfo.objects.create(**dic) result = models.UserInfo.objects.all() for item in result: print item.username,item.age,item.user_type.caption

  (2)對象級別code

def user_info(request): # 一對多:外鍵
    # 一對多中添加user_info表數據:對象級別

    # 第2種方法(對象級別):UserInfo表中的user_type字段對應models中的UserType對象

    #先獲取用戶類型的對象
    type = models.UserType.objects.filter(id=2) #添加時直接添加對象
    models.UserInfo.objects.create(username='xxx',age=28,user_type=type) #或寫成下面形式,省略中間步驟
    dic = {'username':'xxx','age':28,'user_type':models.UserType.objects.get(id=2)} models.UserInfo.objects.create(**dic)

 get方法是從數據庫中取一個匹配的結果,返回一個對象,若是不存在,則會報錯
 filter方法是從數據庫中取匹配的結果,返回一個對象列表,若是不存在,則會返回[]
htm

  2 查找數據

  (1)正向查

def user_info(request): # 查找表中的全部數據
    result = models.UserInfo.objects.all() # 查找指定數據
    # 正向查找:查詢當前表中數據
    result = models.UserInfo.objects.filter(username='xx') # 跨表查詢(使用雙線劃線"__"),user_type是UserType類的對象,因此這裏是查詢UserType表中的caption字段,便是跨表查詢
    result = models.UserInfo.objects.filter(user_type__caption='CTO') for item in result: # 與跨表查詢數據不一樣的是,當查詢完數據再取數據時,使用的是".",這裏要注意
      print item.username,item.age,item.user_type.caption return HttpResponse('ok')

  (2)反向查

def user_info(request): # 反向查找
    # 搜索條件:id/caption/userinfo(該字段是Django在UserType表中自動生成的,關聯到UserInfo表,這個字段很重要,爲反向查找提供了可能)
    line = models.UserType.objects.get(id=1) print line.id  #輸出"1"
    print line.caption  #輸出"CEO"
    #輸出"[<UserInfo: UserInfo object>]",表示當前用戶類型UserType(id=1)對應的用戶對象
    ''' 注意下面的line.userinfo_set等價於models.UserInfo.objects.filter(user_type=line) 好好理解這種等價關係 '''
    print line.userinfo_set.all() print line.userinfo_set.filter(username='xx') for item in line.userinfo_set.all(): # 輸出"xx 18 UserType object"
        print item.username, item.age, item.user_type # 輸出"xx 18 CEO",注意user_type是UserType的對象
        print item.username, item.age, item.user_type.caption # 一、查找某我的是哪一種用戶類型;
     user_type_obj = models.UserType.objects.get(userinfo__username='xx') print user_type_obj.caption

# 二、查找指定用戶類型下有哪些用戶
# 下面兩種方法是等價的,注意在Django中,UserType表默認會增長一列表示與UserInfo表的關係 print user_type_obj.userinfo_set.count() # 下面方法是從UserInfo表出發查找的 print models.UserInfo.objects.filter(user_type=user_type_obj).count() return HttpResponse('ok')

  3 新聞點贊實例

  models.py中的表結構

#用戶表
class
MyUser(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) def __unicode__(self): return self.username
#新聞表
class New(models.Model): title = models.CharField(max_length=32) content = models.CharField(max_length=32) def __unicode__(self): return self.title
#點贊表,這裏須要注意:1 哪一個用戶點的贊;2 給哪一個新聞點的贊;3 不能重複點贊
class Favor(models.Model): # 外鍵關聯MyUser user_obj = models.ForeignKey(MyUser) # 外鍵關聯New new_obj = models.ForeignKey(New) def __unicode__(self): return "%s -> %s" %(self.user_obj.username, self.new_obj.title)

   配置admin以及建立admin用戶,並建立幾篇文章

from django.contrib import admin from app01 import models # Register your models here.
admin.site.register(models.MyUser) admin.site.register(models.New) admin.site.register(models.Favor) admin.site.register(models.HostAdmin) admin.site.register(models.Host)

                                             

 views.py中的相關代碼,主要完成兩個功能:1 查詢某人點贊過的文章; 2 查詢某篇文章被點贊過的次數

# 新聞點贊實例(相關表數據已在Django的admin中添加完成)
def favor_new(request): # 獲取全部的新聞列表
    # new_list = models.New.objects.all()

    # 獲取alex點贊過的新聞列表,注意這裏的反向查詢和跨表查詢
    new_list = models.New.objects.filter(favor__user_obj__username='alex') for item in new_list: print '====================='
        print item.title print item.content # 打印每條新聞的點贊次數
        print item.favor_set.all().count() return HttpResponse('ok')

三 ORM中多對多的操做

    Django中的多對多與一對多沒有聯繫,它的實際操做比一對多簡單,Django默認會爲多對多生成第3張表。下面是對應數據庫表的關係:

   models.py中的數據庫結構

#============多對多================ #下面創建多對多關係的字段,寫了其中任何一個便可,它們的區別只在於哪一個是"正向查找",哪一個是"反向查找",即查找時的參照類有區別。
class Host(models.Model): hostname = models.CharField(max_length=32) port = models.IntegerField() # admin = models.ManyToManyField(HostAdmin)

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

   views.py中添加數據

def many_to_many(request): #建立主機表
    models.Host.objects.create(hostname='c1',port=80) models.Host.objects.create(hostname='c2',port=80) models.Host.objects.create(hostname='c3',port=80) #建立用戶表,注意這裏只須要添加username和email字段,host字段不須要指定
    models.HostAdmin.objects.create(username='alex',email='alex@qq.com') models.HostAdmin.objects.create(username='eric',email='eric@qq.com') models.HostAdmin.objects.create(username='pony',email='pony@qq.com') models.HostAdmin.objects.create(username='rain',email='rain@qq.com') return HttpResponse('ok')

    生成的三張表以下:

   
  1 正向添加數據(從包含多對多字段的表操做數據庫)

def many_to_many(request): # 正向添加
    # 目的:給alex分配兩個主機的管理權限
    # 一、獲取指定的HostAdmin對象
    admin_obj = models.HostAdmin.objects.get(username='alex') # 二、獲取指定的Host,條件爲id小於3
    host_list = models.Host.objects.filter(id__lt=3) # 三、將用戶alex和指定的兩個主機添加到對應的第3張表中,注意host_list是列表,全部加*號
    admin_obj.host.add(*host_list) return HttpResponse('ok')

  Django自動生成的第3張表以下所示:

  2 反向添加數據(從不包含多對多字段的表操做數據庫)

def many_to_many(request): # 反向添加
    # 一、獲取主機表
    # host_obj = models.Host.objects.get(id=3)
    # 二、獲取用戶表
    # admin_list = models.HostAdmin.objects.filter(id__gt=1)
    # 三、添加數據,注意hostadmin_set,這是在Host表中,Django自動生成的關聯HostAdmin表的字段
    # host_obj.hostadmin_set.add(*admin_list)
  return HttpResponse('ok')
不管是正向添加仍是反向添加,其本質都是基於主機表或用戶表的一行數據(對象)對應另外一張表中的一行或多行數據(對象)。

正向添加,對於ID=2的用戶,添加多臺主機(好比主機ID爲1,2,3,4),那麼Django自動生成的第3張表信息以下:
# 用戶ID  主機ID
    2       1
    2       2
    2       3
    2       4

反向添加,對於ID=2的主機,添加多個用戶(好比用戶ID爲1,2,3),那麼Django自動生成的第3張表信息以下:
# 用戶ID  主機ID
    1       2
    2       2
    3       2

    3 自定義第3張表

    對於Django自動生成的第3張表,在使用的過程當中不是很靈活,也不能增長字段。對於這種狀況,Django容許自定義生成第3張表,不須要使用默認的表結構。分析下三張表之間的關係:

     models.py中自定義第3張表

# 主機表
class Host(models.Model): hostname = models.CharField(max_length=32) port = models.IntegerField() # admin = models.ManyToManyField(HostAdmin)

# 用戶表 class HostAdmin(models.Model): username = models.CharField(max_length=32) email = models.CharField(max_length=32) host = models.ManyToManyField(Host1,through='HostRelation') # 自定義的第3張表 class HostRelation(models.Model): # 外鍵關係 c1 = models.ForeignKey(Host) c2 = models.ForeignKey(HostAdmin) # 還能夠自定義字段

   自定義第3張表中添加數據(在自定義的第3張表中,咱們在添加數據時其實跟其它兩張表沒有關係了,由於自定義時咱們定義了數據庫類)

def many_to_many(request):
    # 在自定義多對多的第3張表中添加數據
# 第1種方法:對象級別
models.HostRelation.objects.create( c1=models.Host.objects.get(id=1), c2=models.HostAdmin.objects.get(id=2) )
# 第2種方法:數據庫級別
# models.HostRelation.objects.create( # c1_id = 2, # c2_id = 1 # ) return HttpResponse('ok')

  上述兩種方法性能比較:第1種方法中兩次數據庫查詢,1次數據庫插入;第2種方法中0次數據庫查詢,1次數據庫插入。

  4 查詢數據庫

    Django默認生成第3張表的查詢/自定義第3張表的查詢

def many_to_many(request): # 第1種 Django默認生成第3張表的查詢
    # 正向查
    admin_obj = models.HostAdmin.objects.all(id=1) for item in admin_obj: item.host.all() # 反向查
    host_obj = models.Host.objects.get(id=1) host_obj.hostadmin_set.all() # 第2種 自定義第3張表的查詢
    #relation_list = models.HostRelation.objects.all()
    relation_list = models.HostRelation.objects.filter(c2__username='alex') for item in relation_list: print item.c1.hostname print item.c2.username return HttpResponse('ok')

  5 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 user_info(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優化查詢 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") ''' return HttpResponse('ok')

    若是是普通查詢,從執行的SQL語句能夠看出,它只會獲取UserInfo表中的數據;若是使用select_related,從執行的SQL語句能夠看出它會把ForiegnKey關聯的表自動作一次關聯查詢。它既獲取UserInfo表的數據,同時也獲取UserType表的數據。

四 ORM中的F&Q

 1 F — 批量修改或更新數據

   假設數據庫中有1個age字段,我想讓全部age字段自加1或某些人自加1,那麼能夠利用ORM中F:

#導入F模塊
from django.db.models import F #F指代當前行的age字段
model.tb.object.all().update(age=F('age')+1)

 2 Q — 條件查詢,很是有用

   默認狀況下Django的查詢

#查找username='alex'且age=18的對象,條件不是很靈活
models.UserInfo.objects.filter(username='alex',age=18)

    假如要查找username='alex'或username='eric'或username='rain',而且age=18的對象,那麼默認的查詢實現起來比較麻煩,爲了解決這種狀況,Django爲咱們提供了Q操做,下面是操做步驟:

#導入Q模塊
from django.db.models import Q

#
第1步: #生成一個搜索對象 search_q = Q() #再生成兩個搜索對象 search1 = Q() search2 = Q() #第2步: #標記search1中的搜索條件的鏈接符爲'OR'(或條件) search1.connector = 'OR' #把搜索條件加入到search1中,注意都是元組的形式 search1.children.append(('字段名','字段內容')) search1.children.append(('字段名','字段內容')) search1.children.append(('字段名','字段內容')) search1.children.append(('字段名','字段內容')) #標記search2中的搜索條件的鏈接符爲'OR'(或條件) search2.connector = 'OR' #把搜索條件加入到search2中 search2.children.append(('字段名','字段內容')) search2.children.append(('字段名','字段內容')) search2.children.append(('字段名','字段內容')) search2.children.append(('字段名','字段內容')) #第3步: #把多個搜索條件進行合併,以'AND'的形式(與條件) search_q.add(search1,'AND') search_q.add(search2,'AND') #第4步: #執行搜索,仍是利用filter models.HostInfo.objects.filter(search_q)

五 Django ORM總結

 一、一對多
 (1)添加數據
    經過對象級別
    經過數據庫級別(數據在數據庫中的存儲方式,對象字段_id)
 (2)查找
    正向查找
     經過filter跨表,對象__跨表的字段
     獲取值,obj.對象.跨表的字段
    反向查找
          經過filter跨表,Django自動生成與表名相同的對象__跨表的字段
     獲取值,obj.Django自動生成與表名相同的對象_set.filter()/all()[0:]
 二、多對多
 (1)Django自動生成關係表
    正向:一行數據的對象.ManyToMany字段
        反向:一行數據的對象.表名_set
 (2)自定義關係表(推薦)
       不論是添加、查詢,只須要操做自定義關係表
 三、select_related
  優化查詢,一次性將查詢的表和ForiegnKey關聯的表加載到內存。

 四、Django中的F&Q操做

 

參考資料:

   http://www.cnblogs.com/luotianshuai/p/5403513.html

相關文章
相關標籤/搜索