本篇將分享一些技巧,用戶改進 Model 的設計。其中有很多與命名約定有關, 這可以大大的提高代碼的可讀性。
PEP8規範, 廣泛用於 Python 領域, 因此我建議你在項目中使用它.
除了 PEP8 , 我更喜歡Django編程風格.
本篇目錄:
命名 Model
模型定義使用CapWords約定(沒有下劃線). 例如: User
, Permission
, ContentType
.
模型的屬性使用 snake_case. 例如: first_name
, last_name
.
如:
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屬性.
from django.db import models class Company(models.Model): # ... companies = models.Manager()
而後, 可以通過下面語句來使用 Django ORM QuerySet 查詢.
Company.companies.filter(name='Google')
這樣看起來代碼就很有可讀性了
Model 定義順序
Django Coding Style 建議內部類,方法和屬性的順序爲:
- 如果字段有
choices
參數, 則每個選項定義爲元祖中元祖.並使用全大寫的名稱作爲值屬性。 - 所有數據庫fields
- Custom manager attributes
- class Meta
- def __str__()
- def save()
- def get_absolute_url()
- 其他自定義方法
如:
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的模型的複數形式.
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,其中包含與此公司相關的所有員工實例
google = Company.objects.get(name='Google') google.employees.all()
你也可以通過反向關係, 來更新Company的employees字段.
vitor = Employee.objects.get(first_name='Vitor') google = Company.objects.get(name='Google') google.employees.add(vitor)
related_query_name
這種關係也是用於查詢過濾器, 比如我們要查詢僱傭名爲「Vitor」的所有公司:
companies = Company.objects.filter(employee__first_name='Vitor')
如果你想自定義此關係的查詢名稱可以這樣
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' )
然後這樣查詢
companies = Company.objects.filter(person__first_name='Vitor')
代碼要保持一致, related_name是複數, related_query_name是單數.
Blank 和 Null
我在另一篇文章有講過兩者的區別 Blank or Null,在這裏我會總結一下.
- null: 數據庫相關; 定義數據庫字段的值是否接受空值。
- blank: 驗證相關, 當調用
form.is_valid()
時, 將會判斷值是否爲空.
雖然兩者的是有區別的, 但一個擁有null=True
和blank=False
的字段是完全沒有問題的。 在數據庫級別上, 該字段可以爲 NULL, 但在應用程序級別上, 它是必填字段(前提你通過 Django 標準的 Form 進行判斷)。
大多數開發人員都對基於字符串的字段(CharField
和TextField
)定義null=True
, 這其實是沒有必要的, 應該避免這樣做,因爲 Django約定使用空字符串設置空值, 而非Null.
所以, 如果你想設置一個基於字符的字段可以爲空,那麼你應該這樣做:
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.