Django 模型设计上的一些建议

Python 2019-05-17 1057 次浏览 次点赞

起步

在本章中将分享一些模型设计上的技巧和命名约定,以便提高代码的可读性。

首当其冲的是遵守 PEP8 中的规范,其次是可以了解下 Django的编码风格

模型的命名规范

模型定义就是定义一个类,所以总是用驼峰法来命名(没有下划线),例如: User, Permission, ContentType 等。

属性及方法名则用匈牙利命名法,如: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 。模型定义是单个对象的表示(此示例中的对象是公司),而不是公司的集合。

模型属性方法的定义顺序

Django编码样式建议以下内部类,方法和属性的顺序:

  1. 定义模型字段选项,如定义字段的 choices 信息;
  2. 所有数据库字段;
  3. 自定义管理器属性;
  4. class Meta 代码段;
  5. def __str__() 方法;
  6. def save() 方法;
  7. def get_absolute_url();
  8. 其他自定义方法。

例如:

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

class Company(models.Model):
    # 定义模型选项
    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'),
    )

    # 数据库字段
    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)

    # 设置模型管理器
    objects = models.Manager()
    limited_companies = LimitedCompanyManager()

    # 定义元数据
    class Meta:
        verbose_name = 'company'
        verbose_name_plural = 'companies'

    # __str__ 方法
    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})

    # 其他方法
    def process_invoices(self):
        do_something()

外键相关的建议

禁用物理外键

这是某个DBA给的建议,在海量的数据库中想都不要去想外键,试想,一个程序每天要insert数百万条记录,当存在外键约束的时候,每次要去扫描此记录是否合格,非常耗时。所以在有外键关联时,尽量选择逻辑外键。我们可以使用 db_constraint=False 不让它们在 mysql 层面建立外键关联,也不影响开发者在ORM上的操作:

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, db_constraint=False)

related_name使用复数形式

ForeignKey 中的 related_name 属性有时非常有用它让我们为反向关系定义一个有意义的名称。

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, db_constraint=False, related_name='employees')

这将为 Company 模型添加一个名为 employees 特殊属性,改属性将返回与该公司相关的所有员工的数据:

google = Company.objects.get(name='Google')
google.employees.all()   # 若没设置 related_name 则默认为 google.employee_set.all()
google.employees.filter(is_active=True)

new_user = Employee.objects.create(first_name='Vitor')
google.employees.add(new_user)   # 反向关联

NULL 和 空字符串

基于文本的字段不要用 null=True 。否则,会有两个情况都表示“无数据”即 NULL 和空字符串。但可以设置 blank=True 的。

null=True 与 blank=True的区别是什么?

  • null=bool:它与数据相关,定义该字段在数据库是否允许为空,属于数据库层面的;
  • blank=bool:它与验证相关,表现在表单中是否必填,属于业务层面的。
class Person(models.Model):
    name = models.CharField(max_length=255)  # Mandatory
    bio = models.TextField(max_length=500, blank=True, default='')  # Optional (don't put null=True)

本文由 hongweipeng 创作,采用 署名-非商业性使用-相同方式共享 3.0,可自由转载、引用,但需署名作者且注明文章出处。

赏个馒头吧