Django丨模版层 - 1. 模型和字段

模型和字段

一个模型(model)就是一个单独的、确定的数据的信息源,包含了数据的字段和操作方法。通常,每个模型映射为一张数据库中的表。

基本的原则,如下:

  • 每个模型在Django中的存在形式为一个Python类;
  • 每个类都是django.db.models.Model的子类;
  • 模型(类)的每个字段(属性)代表数据表的某一列;
  • Django自动为你生成访问数据库的API;

1. 简单示例

下面的模型定义了一个“人”,它具有first_namelast_name字段:

1
2
3
4
5
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

每一个字段都是一个类属性,每个类属性表示数据表中的一个列。

上面的代码,相当于下面的原生SQL语句:

1
2
3
4
5
CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30NOT NULL,
    "last_name" varchar(30NOT NULL
);

注意:

  1. 表名myapp_person由Django自动生成,默认格式为“项目名称+下划线+小写类名”,你可以重写这个规则。
  2. Django会自动创建自增主键id,当然,你也可以自己指定主键。
  3. 上面的SQL语句基于PostgreSQL语法。

通常,我们会将模型编写在其所属app下的models.py文件中,没有特别需求时,请坚持这个原则,不要自己给自己添加麻烦。

创建了模型之后,在使用它之前,你需要先在settings文件中的INSTALLED_APPS处,注册models.py文件所在的myapp。看清楚了,是注册app,不是模型,也不是models.py。如果你以前写过模型,可能已经做过这一步工作,可跳过。

1
2
3
4
5
INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

当你每次对模型进行 时,请务必执行命令python manage.py migrate,让操作实际应用到数据库上。这里可以选择在执行migrate之前,先执行python manage.py makemigrations让修改动作保存到记录文件中,方便github等工具的使用。

2. 模型的属性

每个模型都可以有很多属性,其中有Django内置的,也可以有你自定义的。

模型当中最重要的属性Manager管理器。它是 Django 模型和数据库查询操作之间的API接口,用于从数据库当中获取数据实例。如果没有指定自定义的 Manager ,那么它默认名称是 objects,这是Django自动为我们提供和生成的。Manager 只能通过模型类来访问,不能通过模型实例来访问,也就是说,只能Person.objects,不可以jack.objects

模型还有一个不为人知的隐藏属性_state

_state属性指向一个ModelState类实例,它持续跟踪着模型实例的生命周期。

_state自己又有2个属性:addingdb

  • adding:一个标识符,如果当前的模型实例还没有保存到数据库内,则为True,否则为False
  • db:一个字符串指向某个数据库,当前模型实例是从该数据库中读取出来的。

所以:

  • 对于一个新创建的模型实例:adding=True并且db=None
  • 对于从某个数据库中读取出来的模型实例:adding=False并且db='数据库名'
1
2
3
4
5
6
7
>>> blog = Blog.create('mary''ss')
>>> blog._state
<django.db.models.base.ModelState object at 0x00000203CD717D30>
>>> blog._state.adding
True
>>> blog._state.db
# None

3. 模型方法

模型的方法其实就是Python的实例方法。Django内置了一些,我们也可以自定义一些。

在模型中添加自定义方法会给你的模型提供自定义的“行级”数据操作能力,也就是说每个模型的实例都可以调用模型方法。与之对应的是类 Manager 的方法提供的是“表级”的数据操作。

在后面的章节有对Django内置API方法的详细介绍。

建议:如果你有一段需要针对每个模型实例都有效的业务代码,应该把它们抽象成为一个函数,放到模型中成为模型方法,而不是在大量视图中重复编写这段代码,或者在视图中抽象成一个函数。

下面的例子展示了如何自定义模型方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()
    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(194581):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(196511):
            return "Baby boomer"
        else:
            return "Post-boomer"
    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
  • baby_boomer_status作为一个自定义的模型方法,可以被任何Person的实例调用,进行生日日期判断
  • full_name模型方法被Python的属性装饰器转换成了一个类属性

具体使用操作:

1
2
3
4
5
>>>jack = Person.objects.get(pk=1)
>>>jack.baby_boomer_status()    # 以执行函数的方式调用
# ...
>>>jack.full_name   # 以属性的方式调用
# jack Tomas

Django内置了一些模型方法,有些我们直接使用即可,有些会进行自定义重写:

  • __str__(): 这个其实是Python的魔法方法,用于返回实例对象的打印字符串。为了让显示的内容更直观更易懂,我们往往自定义这个方法:
1
2
3
4
5
6
class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()
    def __str__(self):
        return self.first_name + self.last_name
  • get_absolute_url(): 这个方法是返回每个模型实例的相应的访问url。
  • __hash__()
    • 实际上,Django在内部还为models.Model实现了__hash__()魔法方法,给模型实例提供唯一的哈希值。
    • 这个方法的核心是hash(obj.pk),通过模型主键的值,使用内置的hash方法生成哈希值。如果实例还未保存,没有主键值,显然会发生错误。哈希值一旦生成就不允许修改。

4. 模型字段fields

字段是模型中最重要的内容之一,也是唯一必须的部分。字段在Python中表现为一个类属性,体现了数据表中的一个列。请不要使用cleansavedelete等Django内置的模型API名字,防止命名冲突。

下面是一个展示,注意字段的写法:

1
2
3
4
5
6
7
8
9
10
11
12
from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

4.1 字段命名约束

Django不允许下面三种字段名:

  1. 与Python关键字冲突。这会导致语法错误。例如:

    1
    2
    class Example(models.Model):
        pass = models.IntegerField() # 'pass'是Python保留字!
  2. 字段名中不能有两个以上下划线在一起,因为两个下划线是Django的查询语法。例如:

    1
    2
    class Example(models.Model):
        foo__bar = models.IntegerField() # 'foo__bar' 有两个下划线在一起!
  3. 字段名不能以下划线结尾,原因同上。
    由于你可以自定义表名、列名,上面的规则可能被绕开,但是请养成良好的习惯,一定不要那么起名。

5. 模型字段类型

下篇再出


文章链接:https://www.liujiangblog.com/course/django/95