Django小技巧22: 設計一個好的模型

翻譯整理自: simpleisbetterthancomplex.com

本篇將分享一些技巧,用戶改進 Model 的設計。其中有很多與命名約定有關, 這可以大大的提高代碼的可讀性。

PEP8規範, 廣泛用於 Python 領域, 因此我建議你在項目中使用它.

除了 PEP8 , 我更喜歡Django編程風格.

本篇目錄:

命名 Model

模型定義使用CapWords約定(沒有下劃線). 例如: User, Permission, ContentType.

模型的屬性使用 snake_case. 例如: first_name, last_name.

如:

Python

from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=30)
    vat_identification_number = models.CharField(max_length=20)

始終使用單數明明你的模型, 用Company代替Companies. 模型的定義是對單個對象的表示, 而不是公司的集合.

這通常會導致混淆,因爲我們傾向於通過數據庫思考。模型最終被翻譯成table.該表使用其複數形式命名的.

在 DJango 中,我們可以通過Company.objects來訪問集合. 我可以通過定義models.Manager重命名objects屬性.

Python

from django.db import models

class Company(models.Model):
    # ...
    companies = models.Manager()

而後, 可以通過下面語句來使用 Django ORM QuerySet 查詢.

Python

Company.companies.filter(name='Google')

這樣看起來代碼就很有可讀性了

Model 定義順序

Django Coding Style 建議內部類,方法和屬性的順序爲:

  • 如果字段有choices參數, 則每個選項定義爲元祖中元祖.並使用全大寫的名稱作爲值屬性。
  • 所有數據庫fields
  • Custom manager attributes
  • class Meta
  • def __str__()
  • def save()
  • def get_absolute_url()
  • 其他自定義方法

如:

Python

from django.db import models
from django.urls import reverse

class Company(models.Model):
    # CHOICES
    PUBLIC_LIMITED_COMPANY = 'PLC'
    PRIVATE_COMPANY_LIMITED = 'LTD'
    LIMITED_LIABILITY_PARTNERSHIP = 'LLP'
    COMPANY_TYPE_CHOICES = (
        (PUBLIC_LIMITED_COMPANY, 'Public limited company'),
        (PRIVATE_COMPANY_LIMITED, 'Private company limited by shares'),
        (LIMITED_LIABILITY_PARTNERSHIP, 'Limited liability partnership'),
    )

    # DATABASE FIELDS
    name = models.CharField('name', max_length=30)
    vat_identification_number = models.CharField('VAT', max_length=20)
    company_type = models.CharField('type', max_length=3, choices=COMPANY_TYPE_CHOICES)

    # MANAGERS
    objects = models.Manager()
    limited_companies = LimitedCompanyManager()

    # META CLASS
    class Meta:
        verbose_name = 'company'
        verbose_name_plural = 'companies'

    # TO STRING METHOD
    def __str__(self):
        return self.name

    # SAVE METHOD
    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

    # ABSOLUTE URL METHOD
    def get_absolute_url(self):
        return reverse('company_details', kwargs={'pk': self.id})

    # OTHER METHODS
    def process_invoices(self):
        do_something()

反向關係

related_name

ForeignKey 的 related_name 可以爲反向關係定義一個有意義的名稱

經驗法則: 如果你不確定related_name是什麼, 請使用包含所定義ForeignKey的模型的複數形式.

Python

class Company:
    name = models.CharField(max_length=30)

class Employee:
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='employees')

上面代碼意味着, Company 有一個employees特殊屬性, 該屬性將返回一個 QuerySet,其中包含與此公司相關的所有員工實例

Python

google = Company.objects.get(name='Google')
google.employees.all()

你也可以通過反向關係, 來更新Company的employees字段.

Python

vitor = Employee.objects.get(first_name='Vitor')
google = Company.objects.get(name='Google')
google.employees.add(vitor)

related_query_name

這種關係也是用於查詢過濾器, 比如我們要查詢僱傭名爲「Vitor」的所有公司:

Python

companies = Company.objects.filter(employee__first_name='Vitor')

如果你想自定義此關係的查詢名稱可以這樣

Python

class Employee:
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    company = models.ForeignKey(
        Company,
        on_delete=models.CASCADE,
        related_name='employees',
        related_query_name='person'
    )

然後這樣查詢

Python

companies = Company.objects.filter(person__first_name='Vitor')

代碼要保持一致, related_name是複數, related_query_name是單數.

Blank 和 Null

我在另一篇文章有講過兩者的區別 Blank or Null,在這裏我會總結一下.

  • null: 數據庫相關; 定義數據庫字段的值是否接受空值。
  • blank: 驗證相關, 當調用form.is_valid()時, 將會判斷值是否爲空.

雖然兩者的是有區別的, 但一個擁有null=Trueblank=False的字段是完全沒有問題的。 在數據庫級別上, 該字段可以爲 NULL, 但在應用程序級別上, 它是必填字段(前提你通過 Django 標準的 Form 進行判斷)。

大多數開發人員都對基於字符串的字段(CharFieldTextField)定義null=True, 這其實是沒有必要的, 應該避免這樣做,因爲 Django約定使用空字符串設置空值, 而非Null.

所以, 如果你想設置一個基於字符的字段可以爲空,那麼你應該這樣做:

Python

class Person(models.Model):
    name = models.CharField(max_length=255)  # 強制填寫
    bio = models.TextField(max_length=500, blank=True)  # 可選填寫 (不要設置null=True)
    birth_date = models.DateField(null=True, blank=True) # 可選填寫 (這裏你應該設置null=True)

進階

模型定義是應用程序重要的一部分, 請務必使用合適的字段類型. 這裏是 Django 支持的所有字段類型.

如果你對代碼風格規範感興趣, 可以讀一讀Django Coding Style. 當然也可以看一看Flake8.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章