Django之model外鍵


外鍵和表關系

外鍵:

MySQL中,表有兩種引擎,一種是InnoDB,另外一種是myisam如果使用的是InnoDB引擎,是支持外鍵約束的。外鍵的存在使得ORM框架在處理表關系的時候異常的強大。因此這里我們首先來介紹下外鍵在Django中的使用。

類定義為class ForeignKey(to,on_delete,**options)。第一個參數是引用的是哪個模型,第二個參數是在使用外鍵引用的模型數據被刪除了,這個字段該如何處理,比如有CASCADESET_NULL等。

這里以一個實際案例來說明。比如有一個User和一個Article兩個模型。一個User可以發表多篇文章,一個Article只能有一個Author,並且通過外鍵進行引用。那么相關的示例代碼如下:

class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)


class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

    author = models.ForeignKey("User",on_delete=models.CASCADE)

 

以上使用ForeignKey來定義模型之間的關系。即在article的實例中可以通過author屬性來操作對應的User模型。這樣使用起來非常的方便。示例代碼如下:

article = Article(title='abc',content='123')
author = User(username='張三',password='111111')
article.author = author
article.save()

# 修改article.author上的值
article.author.username = '李四'
article.save()

 

為什么使用了ForeignKey后,就能通過author訪問到對應的user對象呢。因此在底層,DjangoArticle表添加了一個屬性名_id的字段(比如author的字段名稱是author_id),這個字段是一個外鍵,記錄着對應的作者的主鍵。以后通過article.author訪問的時候,實際上是先通過author_id找到對應的數據,然后再提取User表中的這條數據,形成一個模型。

如果想要引用另外一個app的模型,那么應該在傳遞to參數的時候,使用app.model_name進行指定。以上例為例,如果UserArticle不是在同一個app中,那么在引用的時候的示例代碼如下

# User模型在user這個app中
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)

# Article模型在article這個app中
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

    author = models.ForeignKey("user.User",on_delete=models.CASCADE)

 

如果模型的外鍵引用的是本身自己這個模型,那么to參數可以為'self',或者是這個模型的名字。在論壇開發中,一般評論都可以進行二級評論,即可以針對另外一個評論進行評論,那么在定義模型的時候就需要使用外鍵來引用自身。示例代碼如下:

class Comment(models.Model):
    content = models.TextField()
    origin_comment = models.ForeignKey('self',on_delete=models.CASCADE,null=True)
    # 或者
    # origin_comment = models.ForeignKey('Comment',on_delete=models.CASCADE,null=True)

 

外鍵刪除操作:

如果一個模型使用了外鍵。那么在對方那個模型被刪掉后,該進行什么樣的操作。可以通過on_delete來指定。可以指定的類型如下:

  1. CASCADE:級聯操作。如果外鍵對應的那條數據被刪除了,那么這條數據也會被刪除
  2. PROTECT:受保護。即只要這條數據引用了外鍵的那條數據,那么就不能刪除外鍵的那條數據
  3. SET_NULL:設置為空。如果外鍵的那條數據被刪除了,那么在本條數據上就將這個字段設置為空。如果設置這個選項,前提是要指定這個字段可以為空
  4. SET_DEFAULT:設置默認值。如果外鍵的那條數據被刪除了,那么本條數據上就將這個字段設置為默認值。如果設置這個選項,前提是要指定這個字段一個默認值。
  5. SET():如果外鍵的那條數據被刪除了。那么將會獲取SET函數中的值來作為這個外鍵的值。SET函數可以接收一個可以調用的對象(比如函數或者方法),如果是可以調用的對象,那么會將這個對象調用后的結果作為值返回回去。
  6. DO_NOTHING:不采取任何行為。一切全看數據庫級別的約束。

以上這些選項只是Django級別的,數據級別依舊是RESTRICT!

 

表關系:

表之間的關系都是通過外鍵來進行關聯的。而表之間的關系,無非就是三種關系:一對一、一對多(多對一)、多對多等。以下將討論一下三種關系的應用場景及其實現方式。

一對多:

  1. 應用場景:比如文章和作者之間的關系。一個文章只能由一個作者編寫,但是一個作者可以寫多篇文章。文章和作者之間的關系就是典型的多對一的關系。
  2. 實現方式:一對多或者多對一,都是通過ForeignKey來實現的。還是以文章和作者的案例進行講解。

 class User(models.Model):
     username = models.CharField(max_length=20)
     password = models.CharField(max_length=100)

 class Article(models.Model):
     title = models.CharField(max_length=100)
     content = models.TextField()
     author = models.ForeignKey("User",on_delete=models.CASCADE)

那么以后在給Article對象指定author,就可以使用以下代碼來完成:

 

article = Article(title='abc',content='123')
author = User(username='zhiliao',password='111111')
# 要先保存到數據庫中
author.save()
article.author = author
article.save()

並且以后如果想要獲取某個用戶下所有的文章,可以通過article_set來實現。示例代碼如下:

user = User.objects.first()
# 獲取第一個用戶寫的所有文章
articles = user.article_set.all()
for article in articles:
    print(article)

 

一對一:

  1. 應用場景:比如一個用戶表和一個用戶信息表。在實際網站中,可能需要保存用戶的許多信息,但是有些信息是不經常用的。     如果把所有信息都存放到一張表中可能會影響查詢效率,因此可以把用戶的一些不常用的信息存放到另外一張表中我們叫做UserExtension。但是用戶表User和用戶信息表UserExtension就是典型的一對一了。

  2. 實現方式:Django為一對一提供了一個專門的Field叫做OneToOneField來實現一對一操作。示例代碼如下:

 class User(models.Model):
     username = models.CharField(max_length=20)
     password = models.CharField(max_length=100)

 class UserExtension(models.Model):  
     birthday = models.DateTimeField(null=True)  
     school = models.CharField(blank=True,max_length=50)  
     user = models.OneToOneField("User", on_delete=models.CASCADE)

UserExtension模型上增加了一個一對一的關系映射。其實底層是在UserExtension這個表上增加了一個user_id,來和user表進行關聯,並且這個外鍵數據在表中必須是唯一的,來保證一對一。

 

 

多對多:

  1. 應用場景:比如文章和標簽的關系。一篇文章可以有多個標簽,一個標簽可以被多個文章所引用。因此標簽和文章的關系是典型的多對多的關系。

  2. 實現方式:Django為這種多對多的實現提供了專門的Field。叫做ManyToManyField。還是拿文章和標簽為例進行講解。示例代碼如下:

 class Article(models.Model):
     title = models.CharField(max_length=100)
     content = models.TextField()
     tags = models.ManyToManyField("Tag",related_name="articles")

 class Tag(models.Model):
     name = models.CharField(max_length=50)

在數據庫層面,實際上Django是為這種多對多的關系建立了一個中間表。這個中間表分別定義了兩個外鍵,引用到articletag兩張表的主鍵。

 

related_name和related_query_name:

related_name:

還是以UserArticle為例來進行說明。如果一個article想要訪問對應的作者,那么可以通過author來進行訪問。但是如果有一個user對象,想要通過這個user對象獲取所有的文章,該如何做呢?這時候可以通過user.article_set來訪問,這個名字的規律是模型名字小寫_set。示例代碼如下:

user = User.objects.get(name='張三')
user.article_set.all()

如果不想使用模型名字小寫_set的方式,想要使用其他的名字,那么可以在定義模型的時候指定related_name。示例代碼如下:

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    # 傳遞related_name參數,以后在方向引用的時候使用articles進行訪問
    author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='articles')

以后在方向引用的時候。使用articles可以訪問到這個作者的文章模型。示例代碼如下:

user = User.objects.get(name='張三')
user.articles.all()

如果不想使用反向引用,那么可以指定related_name='+'。示例代碼如下:

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    # 傳遞related_name參數,以后在方向引用的時候使用articles進行訪問
    author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='+')

后將不能通過user.article_set來訪問文章模型了。

related_query_name:

在查找數據的時候,可以使用filter進行過濾。使用filter過濾的時候,不僅僅可以指定本模型上的某個屬性要滿足什么條件,還可以指定相關聯的模型滿足什么屬性。比如現在想要獲取寫過標題為abc的所有用戶,那么可以這樣寫:

users = User.objects.filter(article__title='abc')

如果你設置了related_namearticles,因為反轉的過濾器的名字將使用related_name的名字,那么上例代碼將改成如下:

users = User.objects.filter(articles__title='abc')

可以通過related_query_name將查詢的反轉名字修改成其他的名字。比如article。示例代碼如下:

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    # 傳遞related_name參數,以后在方向引用的時候使用articles進行訪問
    author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='articles',related_query_name='article')

那么在做反向過濾查找的時候就可以使用以下代碼:

users = User.objects.filter(article__title='abc')

 

 

指定外鍵字段:

class Game(models.Model):
    GAME_AREAS = (

    )
    GAME_VALIDS = (
        (0, '無效'),
        (1, '有效'),
    )
    appid = models.IntegerField(primary_key=True, unique=True, verbose_name="游戲標識")
    name = models.CharField(max_length=32, verbose_name="游戲名稱")
    area = models.CharField(max_length=32, choices=GAME_AREAS, verbose_name="區域")
    is_valid = models.SmallIntegerField(choices=GAME_VALIDS, verbose_name="是否有效")

    def __str__(self):
        return self.name

    class Meta:
        db_table = "common_game"
        verbose_name = verbose_name_plural = "游戲表"


class GameService(models.Model):
    appid = models.ForeignKey(Game, db_column='appid', null=True, on_delete=models.SET_NULL, verbose_name='游戲')  # 指定db_column字段

 


免責聲明!

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



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