python--第二十三天總結(一對多和多對多)


Django 的 ORM 有多種關系:一對一,多對一,多對多。

各自定義的方式為 :
       一對一: OneToOneField
       多對一: ForeignKey
       多對多: ManyToManyField
上邊的描述太過數據而缺乏人性化,我們來更人性化一些:
       多個屬於一個,即 belong to :  ForeignKey,多個屬於一個
       一個有一個,即 has one: OneToOneField
       一個有很多個,即 has many:  lots of A belong to B 與 B has many A,在建立 ForeignKey 時,另一個表會自動建立對應的關系
       一個既有很多個,又屬於很多個,即 has many and belong to : ManyToManyField,同樣只能在一個model類中說明,關聯表會自動建立。

操作數據表總的來說就是:

一對一和一對多
1、查找數據使用 __ 連接
2、獲取數據時使用 . 連接

前提條件-正向和反向的定義:

正向:基於存在ForeignKey或ManyToManyField 字段的表查找為正向

反向:基於不存在ForeignKey或ManyToManyField表查找為反向

 #反向查詢
_set
注意:xx_set中的xx為小寫的表名

例子如下:

#-*- coding:utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.

from django.db import models

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()






class MyUser(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

    def __unicode__(self):
        return self.username

class News(models.Model):
    title = models.CharField(max_length=32)
    content = models.CharField(max_length=32)
    def __unicode__(self):
        return self.title

class Favor(models.Model):
    user_obj = models.ForeignKey(MyUser)
    new_obj = models.ForeignKey(News)

    def __unicode__(self):
        return "%s -> %s" %(self.user_obj.username, self.new_obj.title)




# ########################### 多對多
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)




#=========================== 方式二,自定義關聯表

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

class HostAdmin1(models.Model):
    username = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    host = models.ManyToManyField(Host1, through='HostRelation')

class HostRelation(models.Model):
    c1 = models.ForeignKey(Host1)
    c2 = models.ForeignKey(HostAdmin1)
models.py
#-*- coding:utf-8 -*-
from django.shortcuts import render,HttpResponse

# Create your views here.

from app01 import models

def user_type(request):
    # dic = {'caption':'COO'}
    #
    # models.UserType.objects.create(**dic)

    return HttpResponse("OK")

def user_info(request):
    #添加
    # dic = {"username":'xx',"age":88,'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



    #models.UserType.objects



    #反向查找
    user_type_obj = models.UserType.objects.get(userinfo__username='alex')
    print user_type_obj.caption
    print user_type_obj.userinfo_set.all().count()


    return HttpResponse("OK")

2


def zan(request):
    #女神實例
    #new_list = models.News.objects.all()

    #大力贊的
    new_list = models.News.objects.filter(favor__user_obj__username='dali')


    for item in new_list:
        # item =>一條新聞
        print '==========>'
        print item.title
        print item.content
        print item.favor_set.all().count()





    return HttpResponse("OK")

def host(request):
    # models.HostAdmin.objects.create(username='dali',email='11@11.com')
    # models.HostAdmin.objects.create(username='alex',email='22@11.com')
    # models.HostAdmin.objects.create(username='root',email='33@11.com')

    #正向添加
    """
    admin_obj = models.HostAdmin.objects.get(username='dali')
    host_list = models.Host.objects.filter(id__lt=3)
    admin_obj.host.add(*host_list)
    """
    #反向添加
    """
    host_obj = models.Host.objects.get(id=3)
    admin_list = models.HostAdmin.objects.filter(id__gt=1)
    host_obj.hostadmin_set.add(*admin_list)
    """


    """
    #關聯表
    #一 方式增加
    models.HostRelation.objects.create(
        c1 = models.Host1.objects.get(id=1),
        c2 = models.HostAdmin1.objects.get(id=2)

    )

    #二 方式增加
    models.HostRelation.objects.create(
        c1_id = 2,
        c2_id = 1,
    )
    """
################################################
    """
    #第一種 方式查詢(區分正反向)
    #正向查
    admin_obj = models.HostAdmin.objects.get(id=1)
    admin_obj.host.all()

    #反向查
    host_obj = models.Host.objects.get(id=1)
    print host_obj.hostadmin_set.all()

    """


    #第二種 方式查詢
    #relation_list = models.HostRelation.objects.all()
    relation_list = models.HostRelation.objects.filter(c2__username='dali')
    for item in relation_list:
        print item.c1.hostname
        print item.c2.username

    return HttpResponse("OK")
views.py

一對多

增加
    #一 以對象的方式增加
    #二 以_id方式增加

查詢

    #正向查(雙下划線__

    #反向查 (  _set)

多對多

增加

    #正向添加
    admin_obj = models.HostAdmin.objects.get(username='dali')
    host_list = models.Host.objects.filter(id__lt=3)
    admin_obj.host.add(*host_list)

    #反向添加
    host_obj = models.Host.objects.get(id=3)
    admin_list = models.HostAdmin.objects.filter(id__gt=1)
    host_obj.hostadmin_set.add(*admin_list)

 

查詢

    #正向查
    admin_obj = models.HostAdmin.objects.get(id=1)
    admin_obj.host.all()

    #反向查
    host_obj = models.Host.objects.get(id=1)
    print host_obj.hostadmin_set.all()

 

---------------------------------------------------------------------------------------------

關聯關系字段 (Relationship fields)

例如,一本書由一家出版社出版,一家出版社可以出版很多書。一本書由多個作者合寫,一個作者可以寫很多書。

1
2
3
4
5
6
7
8
class  Author(models.Model):
     name = models.CharField(max_length = 20 )
class  Publisher(models.Model):
     name = models.CharField(max_length = 20 )
class  Book(models.Model):
     name = models.CharField(max_length = 20 )
     pub = models.ForeignKey(Publisher)
     authors = models.ManyToManyField(Author)

1.關聯尚未定義的Model

如果你要與某個尚未定義的 model 建立關聯 ,就使用 model 的名稱,而不是使用 model 對象本身。

例子中,如果Publisher與Author在Book后面定義,需要寫成下面的形式:

1
2
3
4
class  Book(models.Model):
     name = models.CharField(max_length = 20 )
     pub = models.ForeignKey( 'Publisher' )
     authors = models.ManyToManyField( 'Author' )

2.Model關聯自身

Model可以與自身做多對一關系

1
2
3
class  People(models.Model):
     name = models.CharField(max_length = 20 )
     leader = models.ForeignKey( 'self' ,blank = True ,null = True )

Model也可以與自身做多對多關系

1
2
class  Person(models.Model):
     friends  =  models.ManyToManyField("self")

默認情況下,這種關聯關系是對稱的,如果Person1是Person2的朋友,那么Person2也是Person1的朋友

1
2
3
4
5
6
7
p1 = Person()
p1.save()
p2 = Person()
p2.save()
p3 = Person()
p3.save()
p1.friends.add(p2,p3)

上述情況下,要查找p3的朋友,不用p3.person_set.all(),而直接用p3.friends.all()就可以了

如果想取消這種對稱關系,將symmetrical設為False

1
2
class  Person2(models.Model):
     friends = (models.ManyToManyField( "self" ,symmetrical = False )

這樣查詢p3的朋友,就需要p3.person_set.all()了

3.反向名稱related_name

反向名稱,用來從被關聯字段指向關聯字段。

注意,在你定義 抽象 model (abstract models) 時,你必須顯式指定反向名稱; 只有在你這么做了之后, 某些特別語法 (some special syntax) 才能正常使用。

#當關聯同一個模型的字段大於一個時,要使用related_name參數來指定表名

 

1
2
3
4
class  Book(models.Model):
     name = models.CharField(max_length = 20 )
     pub = models.ForeignKey(Publisher,related_name = 'pub' )
     authors = models.ManyToManyField(Author,related_name = 'author' )

這樣用Publisher或者Author反向查詢Book時可以用related_name了:publisher1.pub.all()或者author1.author.all()。

如果不想設置反向關系,設置related_name為'+'或者以'+'結束。

1
user  =  models.ForeignKey(User, related_name='+')

如果有多個ManyToManyField指向同一個Model,這樣反向查詢FOO_set的時候就無法弄清是哪個ManyToManyField字段了,可以禁止反向關系:

1
2
users  =  models.ManyToManyField(User, related_name='u+')
referents  =  models.ManyToManyField(User, related_name='ref+')

4.數據庫表現 (Database Representation)

多對一:Django 使用ForeignKey字段名稱+ "_id" 做為數據庫中的列名稱。在上面的例子中,BOOK model 對應的數據表中會有 一個 publisher_id 列。

你可以通過顯式地指定 db_column 來改變該字段的列名稱,不過,除非你想自定 義 SQL ,否則沒必要更改數據庫的列名稱。

多對多:Django 創建一個中間表來表示ManyToManyField關系。默認情況下,中間表的名稱由兩個關系表名結合而成。

由於某些數據庫對表名的長度有限制,所以中間表的名稱會自動限制在64個字符以內,並包含一個不重復的哈希字符串。這 

意味着,你可能看到類似 book_authors_9cdf4 這樣的表名稱。你可以使用 db_table 選項手動指定中間表名稱。

但是,如果你想手動指定中間表,你可以用 through 選項來指定model 使用另外某個 model 來管理多對多關系。而這個 model 就是中間表所對應的 model :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class  Person(models.Model):
     name  =  models.CharField(max_length=128)
     def  __unicode__(self):
         return  self.name
class  Group(models.Model):
     name  =  models.CharField(max_length=128)
     members  =  models.ManyToManyField(Person, through='Membership')
     def  __unicode__(self):
         return  self.name
class  Membership(models.Model):
     person  =  models.ForeignKey(Person)
     group  =  models.ForeignKey(Group)
     date_joined  =  models.DateField()
     invite_reason  =  models.CharField(max_length=64)

這樣,就可以記錄某個person何時加入group了。

要建立Person與Group的關系就不能用add,create,remove了,而是需要通過Membership進行。

1
2
3
4
5
6
7
>>> ringo  =  Person.objects.create(name="Ringo Starr")
>>> paul  =  Person.objects.create(name="Paul McCartney")
>>> beatles  =  Group.objects.create(name="The Beatles")
>>> m1  =  Membership(person=ringo, group=beatles,
...     date_joined = date( 1962 ,  8 ,  16 ),
...     invite_reason =  "Needed a new drummer.")
>>> m1.save()

clear()還是可以使用的

1
>>> beatles.members.clear()

當多對多關系關聯自身時,中間表的ForeignKey是可以指向同一個Model的,但是它們必須被看做ManyToManyField的兩邊,而不是對稱的,需要設置 symmetrical=False。

5.其它參數 (Arguments)

5.1 ForeignKey 接受下列這些可選參數,這些參數定義了關系是如何運行的。

ForeignKey.limit_choices_to

它是一個包含篩選條件和對應值的字典,用來在 Django 管理后台篩選 關聯對象。例如,利用 Python 的 datetime 模塊,過濾掉不符合篩選條件關聯對象:

limit_choices_to = {'pub_date__lte': datetime.date.today}

只有 pub_date 在當前日期之前的關聯對象才允許被選。

也可以使用 Q 對象來代替字典,從而實現更復雜的篩選。當limit_choices_to為Q對象時,如果把此外鍵字段放在ModelAdmin的raw_id_fields時是不可用的。

ForeignKey.to_field

指定當前關系與被關聯對象中的哪個字段關聯。默認情況下,to_field 指向被關聯對象的主鍵。

ForeignKey.on_delete

當一個model對象的ForeignKey關聯的對象被刪除時,默認情況下此對象也會一起被級聯刪除的。

1
user  =  models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)

CASCADE:默認值,model對象會和ForeignKey關聯對象一起被刪除

SET_NULL:將model對象的ForeignKey字段設為null。當然需要將null設為True。

SET_DEFAULT:將model對象的ForeignKey字段設為默認值。

Protect:刪除ForeignKey關聯對象時會生成一個ProtectedError,這樣ForeignKey關聯對象就不會被刪除了。

SET():將model對象的ForeignKey字段設為傳遞給SET()的值。

1
2
3
4
5
def  get_sentinel_user():
     return  User.objects.get_or_create(username='deleted')[0]
 
class  MyModel(models.Model):
     user  =  models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))

DO_NOTHING:啥也不做。

5.2 ManyToManyField 接受下列可選參數,這些參數定義了關系是如何運行的。

ManyToManyField.limit_choices_to

和 ForeignKey.limit_choices_to 用法一樣。

limit_choices_to 對於通過 through 參數指定了中介表的 ManyToManyField 不起作用。

ManyToManyField.symmetrical

只要定義遞歸的多對多關系時起作用。

ManyToManyField.through

手動指定中間表

ManyToManyField.db_table

指定數據庫中保存多對多關系數據的表名稱。如果沒有提供該選項,Django 就會根據兩個關系表的名稱生成一個新的表名,做為中間表的名稱。

6.OneToOneField

class OneToOneField(othermodel[, parent_link=False, **options])

用來定義一對一關系。籠統地講,它與聲明了 unique=True 的 ForeignKey 非常相似,不同的是使用反向關聯的時候,得到的不是一個對象列表,而是一個單獨的對象。

在某個 model 擴展自另一個 model 時,這個字段是非常有用的;例如: 多表繼承 (Multi-tableinheritance) 就是通過在子 model 中添加一個指向父 model 的一對一關聯而實現的。

必須給該字段一個參數:被關聯的 model 類。工作方式和 ForeignKey 一樣,連遞歸關聯 (recursive) 和 延后關聯 (lazy) 都一樣。

此外,OneToOneField 接受 ForeignKey 可接受的參數,只有一個參數是 OnetoOneField 專有的:OneToOneField.parent_link

如果為 True ,並且作用於繼承自某個父 model 的子 model 上(這里不能是延后繼承,父 model 必須真實存在 ),那么該字段就會變成指向父類實例的引用(或者叫鏈接),

而不是象其他OneToOneField 那樣用於擴展父類並繼承父類屬性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from  django.db import models, transaction, IntegrityError
 
class  Place(models.Model):
     name  =  models.CharField(max_length=50)
     address  =  models.CharField(max_length=80)
 
     def  __unicode__(self):
         return  u"%s the place" % self.name
 
class  Restaurant(models.Model):
     place  =  models.OneToOneField(Place, primary_key=True)
     serves_hot_dogs  =  models.BooleanField()
     serves_pizza  =  models.BooleanField()
 
     def  __unicode__(self):
         return  u"%s the restaurant" % self.place.name
 
class  Waiter(models.Model):
     restaurant  =  models.ForeignKey(Restaurant)
     name  =  models.CharField(max_length=50)
 
     def  __unicode__(self):
         return  u"%s the waiter at %s" % (self.name, self.restaurant)

使用反向關聯的時候,得到的不是一個對象列表,而是一個單獨的對象:

1
2
3
4
5
6
7
8
9
>>> p1  =  Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> r  =  Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Place.objects.get(restaurant__place__name__startswith = "Demon" )
<Place: Demon Dogs the place>
>>> Waiter.objects. filter (restaurant__place__name__startswith = "Demon" )
 


7.自定義多對多關系表

Django除了能自動創建多對多的第三張表,同樣也可以自定義創建多對多的第三張表,而且操作和管理擴展等難易程度要比自動創建的好許多。所以,在之后的models表結構中,推薦使用自定義的方式。僅僅在創建時添加一個字段即可:through。

 

#=========================== 方式二,自定義關聯表

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

class HostAdmin1(models.Model):
    username = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    host = models.ManyToManyField(Host1, through='HostRelation')

class HostRelation(models.Model):
    c1 = models.ForeignKey(Host1)
    c2 = models.ForeignKey(HostAdmin1) 

#自定義創建第三張表,其中的外鍵都為一對多的關系


增加數據(直接以id的方式向第三張表中添加數據)
models.HostRelation.objects.create(c1_id=2, c2_id=1,)  

查詢數據(雙__)
  relation_list = models.HostRelation.objects.filter(c2__username='dali')
    for item in relation_list:
        print item.c1.hostname
        print item.c2.username

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM