ORM 模型

未匹配的标注

模型

模型

模型是和数据库表交互最为简单的方法。模型是你与Python类交互的最简单优雅的方法,并且在底层帮你实现了所有高开销的工作。模型可用于查询表格中的数据、创建新记录、或者获取表格之间的相关记录,以及其他一些特性。

创建模型

使用模型的第一步便是创建模型。你可以使用如下命令创建模型:

$ python masonite-orm model Post

你可以使用 --directory 标志去指定这些模型的存放目录

这条命令会创建如下的 post 模型:

from masoniteorm.models import Model

class Post(Model):
    """Post Model"""
    pass

以此为基础,你可以根据需要进行基础查询或者高级查询。不过你可能还需要根据需要配置模型。

据此,你可以开始查询记录:

user = User.first()
users = User.all()
active_users = User.where('active', 1).first()

接下来,我们将进一步谈谈模型的设置

规范与配置

Masonite ORM 会做一些假定,让模型具有最为简单的接口。

首先是表名。表名假定为你模型名称的复数形式。如果你有一个用户模型 User,那么它就假定你的表名是 users; 如果你的模型是 Company,它就假定你有一个 companies 表。你应该知道 Masonite ORM 足够智能,能够知道 Company 的复数形式不是 Companys,因此不必担心 Masonite 没办法对接到你的表名。

表名

如果你的表名并不是模型的复数,你可以使用 __table__ 属性进行修改:

class Clients:
  __table__ = "users"

主键

Masonite 也会假定主键。Masonite ORM 假定的主机名是 id。你也可以修改主键名:

class Clients:
  __primary_key__ = "user_id"

数据库连接

接下来,Masonite 假定你会使用配置文件中设置的 default 数据库连接。你也可以在模型上对其进行修改:

class Clients:
  __connection__ = "staging"

批量赋值

默认情况下,为了防止用户修改你不希望被修改的值,Masonite ORM 对批量赋值进行了保护。

这用在 create 和 update 方法中。你可以设置允许批量赋值的字段:

class Clients:
  __fillable__ = ["email", "active", "password"]

Guarded 属性可被用于指定哪些字段不能被批量赋值。这样你就能防止某些字段被批量赋值了:

class Clients:
  __guarded__ = ["password"]

时间戳

同时,Masonite 也假定你的表格中有 crated_atupdated_at 字段。你可以禁用此项:

class Clients:
  __timestamps__ = False

时区

模型使用 UTC 作为默认时区。你可以使用 __timezone__ 属性修改时区:

class User(Model):
    __timezone__ = "Europe/Paris"

查询

几乎所有的模型查询方法可用于查询构造器。如果你想查看查询构造器(query builder)的所有可用方法,请查看此处QueryBuilder文档

单条记录

查询结果可以是一条或者多条记录。如果你的查询结果是单条记录,那么这个结果便是该模型的实例。因此你可以访问改模型实例的属性。例子如下:

from app.models.User import User

user = User.first()
user.name #== 'Joe'
user.email #== 'joe@masoniteproject.com'

你也可以通过主键获取记录:

from app.models.User import User

user = User.find(1)
user.name #== 'Joe'
user.email #== 'joe@masoniteproject.com'

集合

如果你的查询结果返回多条记录,那么这个结果就会被包装成集合实例,你可以迭代使用:

from app.models.User import User

users = User.where('active', 1).get()
for user in users:
  user.name #== 'Joe'
  user.active #== '1'
  user.email #== 'joe@masoniteproject.com'

如果你想根据模型主键获取多条记录的集合,你可以传入列表(list)到 find 方法中:

users = User.find([1,2,3])
for users in users:
  user.name #== 'Joe'
  user.active #== '1'
  user.email #== 'joe@masoniteproject.com'

集合类也有一些便捷的方法,你可以用于和数据进行交互:

user_emails = User.where('active', 1).get().pluck('email') #== Collection of email addresses

如果你想要了解更多像 pluck 一样可用的方法,请阅读集合文档

删除

你也可以这样快速删除记录:

from app.models.User import User

user = User.delete(1)

这段代码会删除主键值为1的记录。

你也可以基于查询删除记录:

from app.models.User import User

user = User.where('active', 0).delete()

子查询

使用 lambda 表达式,你也可以使用子句查询进行更高级的查询:

from app.models.User import User

users = User.where(lambda q: q.where('active', 1).where_null('deleted_at'))
# == SELECT * FROM `users` WHERE (`active` = '1' AND `deleted_at` IS NULL)

Select 查询

默认情况下,Masonite ORM 使用 SELECT * 查询。你可以使用多种方法进行修改。

第一种方法是使用字段名列表指定 __selects__ 属性。在列表中你可以直接使用 as 关键字对字段名设置别名:

class Store(Model):
    __selects__ = ["username", "administrator as is_admin"]

这样,当你模型查询时,这些字段名会自动被包含在内:

store.all() 
#== SELECT `username`, `administrator` as is_admin FROM `users`

另一种方法时直接在 all() 方法中指定:

store.all(["username", "administrator as is_admin"]) 
#== SELECT `username`, `administrator` as is_admin FROM `users`

这种方法在 get 方法中同样适用:

store.where("active", 1).get(["username", "administrator as is_admin"]) 
#== SELECT `username`, `administrator` as is_admin FROM `users` WHERE `active` = 1

关联

使用模型的另一个特性是,可以让多个模型一起相关联(就像表关联一样)。

Belongs To (一对一)

Belongs To 关联是两条表格记录之间的一对一关联。

你可以这样添加一个一对一关联:

from masoniteorm.relationships import belongs_to
class User:

  @belongs_to
  def company(self):
    from app.models.Company import Company
    return Company

它假定 users 表和 companies 表之间的关联的主键是 {method_name}_id -> id。如果并非如此,你也可以修改相关字段名:

from masoniteorm.relationships import belongs_to
class User:

  @belongs_to('company_id', 'primary_key_id')
  def company(self):
    from app.models.Company import Company
    return Company

第一个参数 总是 当前当前模型对应表格的字段名,第二个参数是另一个表格的相关字段。

Has One (一对一)

你也可以定义 Belongs To 的反转:

from masoniteorm.relationships import has_one
class User:

  @has_one
  def company(self):
    from app.models.Company import Company
    return Company

注意,此处的键是相反的。这也是唯一一个键名反转的关联。

from masoniteorm.relationships import has_one
class User:

  @has_one('other_key', 'local_key')
  def company(self):
    from app.models.Company import Company
    return Company

Has Many (一对多)

另一种关联是一对多关联,即一条记录对应其他表格的多条记录:

from masoniteorm.relationships import has_many
class User:

  @has_many('company_id', 'id')
  def posts(self):
    from app.models.Post import Post
    return Post

第一个参数_总是_当前模型表的字段名,第二个参数是其他表格的关联字段。

Belongs to Many (多对多)

多对多关联时,它们之间有一个中间表。在底层,Masonite ORM 会完全帮你处理这个中间表。

真实的世界中可能有这么一个场景,你有一些产品和多个店铺。

每个店铺可以有多个产品并且每种产品也可以放在多个店铺中。比如,店铺可以销售衬衫,衬衫也可以在不同的店铺中销售。

在数据库中,它们就会像这样:

stores
-------
id
name

product_store
--------------
id
store_id
product_id

product
--------
id
name

请注意,此处在店铺(store)和产品(product)之间,有一个叫做 product_store 的中间表。

使用 belongs_to_many 关联,我们可以很容易地获取一个店铺的所有产品。让我们从 Store 模型开始吧:

from masoniteorm.models import Model
from masoniteorm.relationships import belongs_to_many
class Store(Model):

  @belongs_to_many
  def products(self):
    from app.models.Product import Product
    return Product

我们可以修改装饰器中的签名,指定外键。请看下例:

from masoniteorm.models import Model
from masoniteorm.relationships import belongs_to_many
class Store(Model):

  @belongs_to_many("store_id", "product_id", "id", "id")
  def products(self):
    from app.models.Product import Product
    return Product

前两个键是店铺和产品在中间表中的关联外键。后面两个则是店铺和产品表中的外键。

中间表额外字段Extra Fields On Pivot Table

如果你的中间表有其他额外字段需要获取,你可以像这样将这些额外字段添加到中间表记录中:
If there are additional fields on your pivot table you need to fetch you can add the extra fields to the pivot record like so:

@belongs_to_many("store_id", "product_id", "id", "id", with_fields=['is_active'])
  def products(self):
    from app.models.Product import Product
    return Product

这将获取我们可以访问的中间表上的其他字段。

一旦我们创建了这种关系,我们就可以开始从 stores 直接查询到 products

store = Store.find(1)
for product in store.products:
    product.name #== Red Shirt

在每条获取的记录上,你还可以获取中间表并对其执行查询。该中间表记录是中间表(product_store)中 store_idproduct_id 匹配的连接记录。默认情况下,此属性为 pivot

store = Store.find(1)
for product in store.products:
    product.pivot.updated_at #== 2021-01-01
    product.pivot.update({"updated_at": "2021-01-02"})

更改选项

已经创建了很多默认值,但有一些方法可以覆盖它们。

第一个默认值是中间表有一个名为 id 的主键。这用于混合记录,以便你可以更新中间表记录。如果你没有中间表主键,你可以关闭此功能:

@belongs_to_many(pivot_id=None)

你还可以将 ID 更改为id以外的其他内容:

@belongs_to_many(pivot_id="other_column")

下一个默认值是中间表的名称。中间表的名称是按字母顺序排列的两个表名称的单数形式。例如,如果你要关联 persons 表和 houses 表,则假定表名称为 house_person 。你可以更改此命名:

@belongs_to_many(table="home_ownership")

下一个默认设置是中间表上没有时间戳( updated_atcreated_at )。如果你希望 Masonite 管理时间戳,你可以:

@belongs_to_many(with_timestamps=True)

下一个默认设置是模型上的 Pivot 属性将被称为 pivot 。你可以改变这个设置:

@belongs_to_many(attribute="ownerships")

现在,当你需要获得 pivot 关系时,你可以通过以下方式执行此操作:

store = Store.find(1)
for product in store.products:
    product.ownerships.updated_at #== 2021-01-01
    product.ownerships.update({"updated_at": "2021-01-02"})

如果你的中间表上有时间戳,则它们必须被称为 created_atupdated_at

Has One Through (一对一)

HasOneThrough 关联通过一个中间表定义了两个表之间的关系。例如,你可能有一个从 Port 出发的Shipment,并且该 Port 位于特定的 Country

因此,Shipment 可以通过 Port 与特定的 Country 相关联。

该关联方式类似如下:

shipments
  shipment_id - integer - PK
  from_port_id - integer

ports
    port_id - integer - PK
    country_id - integer
    name - string

countries
    country_id - integer - PK
    name - string

要创建这种类型的关联,你只需要导入 Relationship 类并返回一个包含 2 个模型的列表。第一个模型是你要加入的远程表。在本例中,我们将加入一个运往 countriesShipment,因此我们将 Country 作为第一个列表元素。第二个元素是我们需要从 ShipmentCountry 的中间表。在本例中,这是 Port 模型,因此我们将其作为列表中的第二个元素。

from masoniteorm.relationships import has_one_through

class Shipment(Model):

    @has_one_through(
      "port_id", # ports 表上的外键
      "country_id", # countries 表上的外键
      "from_port_id", # shipments 表上的本地键
      "country_id" # ports 表上的本地键
    )
    def from_country(self):
        from app.models.Country import Country
        from app.models.Port import Port

        return [Country, Port]

然后,你可以像使用任何其他关联一样使用此关联:

shipment = Shipment.find(1)
shipment.from_country.name #== China
shipment.with_("from_country").first() #== 立即加载模型
shipment.has("from_country").first() #== 检查模型是否存在

Has Many Through (一对多)

HasManyThrough 关联通过一个中间表定义了 2 个表之间的关联。例如,可能有一个 likes 很多 commentuser 的关系。

因此, 一个 User 可以通过 Like 与多个 Comment 相关联。

架构看起来是这样:

users
  user_id - integer - PK
  name - varchar

likes
    like_id - integer - PK
    user_id - integer - FK
    comment_id - comment_id

comments
    comment_id - integer - PK
    body - text

要创建这种类型的关联,你只需要导入关联类并返回一个包含 2 个模型的列表。第一个模型是你想要连接的远程表。在本例中,我们将 usercomments 连接,所以我们将 Comment 放在列表的第一个元素中。第二个元素是我们需要从 UserComment 获得的中间表。在这种情况下,这是 Like 模型,所以我们将其放在列表的第二个元素中。

from masoniteorm.relationships import has_many_through

class User(Model):

    @has_many_through(
      "like_id", #中间表(likes)表的外键
      "comment_id", #远程(comments)表在中间表的外键
      "user_id", #(users) 表上的本地键
      "user_id" #中间表(likes)表上的本地键
    )
    def liked_comments(self):
        from app.models.Comment import Comment
        from app.models.Like import Like

        return [Comment, Like]

然后,你可以像使用任何其他关联一样使用此关联:

user = User.find(1)
for comment in user.liked_comments:
  comment.body
user.with_("liked_comments").first() #== 立即加载comments
user.has("liked_comments").first() #== 拥有liked_comments的所有用户

使用关联

你可以轻松使用关联关系来获取相关记录。以下是如何获取公司记录的示例:

user = User.first()
user.company #== <app.models.Company>
user.company.name #== Masonite X Inc.

for post in user.posts:
    post.title

With Count

with_count 方法可以用于获取关联关系中记录的数量。

例如,如果你想获取角色所拥有的权限数:

Role.with_count('permissions').get()

这将返回每个记录上带有 {relationship}_count 属性的集合。你可以按如下方式获取此属性:

roles = Role.with_count('permissions').get()
for role in roles:
  role.permissions_count #== 7

这个方法也适用于单个记录。

roles = Role.with_count('permissions').find(1).permissions_count #== 7

你还可以传递一个可选地 lambda 函数作为可调用函数,以针对关联对象传入附加的查询过滤器。

Role.with_count(
    'permissions',
    lambda q: (
        q.where_like("name", "%Creates%")
     )

多态关联

多态关联是指单个记录可以与任何其他表建立关联。例如,一个 Like 可以与 CommentArticle 相关联。

设置

在一个多态表上,我们通常有一个记录类型(record_type)字段表示一个表(或模型),一个记录ID(record_id)字段表示相关表的主键值。Masonite ORM 需要知道哪个记录类型映射到哪个模型。

我们将在连接解析器中创建这个映射。这通常是数据库配置文件中的 DB 变量:

DB = ConnectionResolver().set_connection_details(DATABASES)
# ...
DB.morph_map({
    "Article": Article,
    "Comment": Comment,
})

一对一(多态关联)

在设置多态关联时,与普通关联非常相似。主要的区别在于你将具有多个模型指向单个多态表。在多态一对一关系设置中,你将设置如下的表:

comments
  - comment_id - PK
  - description - Varchar

article:
  - article_id - PK
  - title - Varchar

images
  - id - PK
  - record_type - Varchar
  - record_id - Unsigned Int

images 表中有 record_typerecord_id 字段。它们可以命名为任何名称,但应该包含一个 varchar 类型的列,该列将用于映射到模型,以及一个用于放置外部表主键值的列。

模型的设置如下:

from masoniteorm.relatinships import morph_to, morph_many

class Image(Model):

  @morph_to
  def record(self):
    return

class Article(Model):

  @morph_one("record_type", "record_id")
  def image(self):
    return Like

class Comment(Model):

  @morph_one("record_type", "record_id")
  def image(self):
    return Like

一对多(多态关联)

当设置多态关联时,它与普通关系非常相似。主要区别在于你将有多个模型指向单个多态表。在多态一对多关联设置中,你将有一个类似下面的表:

comments
  - comment_id - PK
  - description - Varchar

articles:
  - article_id - PK
  - title - Varchar

likes
  - id - PK
  - record_type - Varchar
  - record_id - Unsigned Int

注意,likes 表有 record_typerecord_id 字段。这些字段可以任意命名,但应该包含一个 varchar 类型的列,用于映射到一个模型,以及一个列用于存放外键表的主键值。

在这种情况下,likes 表仍然与多个模型具有 one 关联,但是相关的表( articlescomments )有多个记录与 likes 表关联。

模型的设置如下所示:

from masoniteorm.relatinships import morph_to, morph_many

class Likes(Model):

  @morph_to
  def record(self):
    return

class Article(Model):

  @morph_many("record_type", "record_id")
  def likes(self):
    return Like

class Comment(Model):

  @morph_many("record_type", "record_id")
  def likes(self):
    return Like

Morph To 和 Morph To Many

Masonite ORMmorph_tomorph_to_many 两个关联类型,用于将多个记录与多态表关联起来。这些关联用于多态模型,以关联相关模型。morph_to 将从相关模型返回一个结果,而 morph_to_many 将返回多个结果。

模型示例如下:

from masoniteorm.relatinships import morph_to, morph_many

class Likes(Model):

  @morph_to
  def record(self):
    return

class User(Model):

  @morph_to_many
  def record(self):
    return

预加载

你可以预加载任何相关记录。预加载是指预先加载模型结果,而不是每次调用数据库。

让我们以获取用户电话的示例为例:

users = User.all()
for user in users:
    user.phone

这将导致以下查询:

SELECT * FROM users
SELECT * FROM phones where user_id = 1
SELECT * FROM phones where user_id = 2
SELECT * FROM phones where user_id = 3
SELECT * FROM phones where user_id = 4
...

这将导致很多数据库调用。现在让我们看一下具有预加载的相同示例:

users = User.with_('phone').get()
for user in users:
    user.phone

现在会得到以下查询:

SELECT * FROM users
SELECT * FROM phones where user_id IN (1, 2, 3, 4)

这样只产生了两个查询。任何随后的调用都会从预加载的结果集中获取结果。

你还可以在模型上使用 __with__ 属性将所有模型调用默认为使用预加载:

from masoniteorm.models import Model
from masoniteorm.relationships import belongs_to_many
class Store(Model):

  __with__ = ['products']

  @belongs_to_many
  def products(self):
    from app.models.Product import Product
    return Product

嵌套预加载

你可以一次预加载多个关联模型。举个更高级的例子…

比如你想获取一个用户的电话和联系人。代码如下:

users = User.all()
for user in users:
    for contact in user.phone:
        contact.name

将返回这样的查询语句:

SELECT * FROM users
SELECT * FROM phones where user_id = 1
SELECT * from contacts where phone_id = 30
SELECT * FROM phones where user_id = 2
SELECT * from contacts where phone_id = 31
SELECT * FROM phones where user_id = 3
SELECT * from contacts where phone_id = 32
SELECT * FROM phones where user_id = 4
SELECT * from contacts where phone_id = 33
...

可以看到,我们遍历了成百上千个用户,这样的查询语句肯定会变得很大。

我们可以使用嵌套预加载来解决这个问题,使用 . 符号指定关系链即可:

users = User.with_('phone.contacts').all()
for user in users:
    for contact in user.phone:
        contact.name

这会返回这样的查询语句:

SELECT * FROM users
SELECT * FROM phones where user_id IN (1,2,3,4)
SELECT * from contacts where phone_id IN (30, 31, 32, 33)

可以看到,无论用户有多少个,查询语句始终只有 3 条。

关联查询

如果你的模型之间有关联,你可以轻松地进行关联查询:

例如,如果你有这样一个模型:

from masoniteorm.relationships import has_many
class User:

  @has_many('company_id', 'id')
  def posts(self):
    from app.models.Post import Post
    return Post

你可以使用 joins 方法:

User.joins('posts')

这将构建出 join 方法。

你也可以指定连接的类型(inner、left、right),默认为 inner join

User.joins('posts', clause="right")

此外,如果你想指定额外的 where 子句,可以使用 join_on 方法:

User.join_on('posts', lambda q: (
  q.where('active', 1)
))

查询作用域

查询作用域是一种获取你可能正在执行的常见查询并将其浓缩为一种方法的方法,然后你可以在该方法中链接到它们。假设你正在执行一个查询,例如频繁获取活动用户:

user = User.where('active', 1).get()

我们可以将这个查询添加为一个作用域:

from masoniteorm.scopes import scope
class User(Model):

  @scope
  def active(self, query):
    return query.where('active', 1)

现在我们可以简单地调用活动方法:

user = User.active().get()

你也可以传入参数:

from masoniteorm.scopes import scope
class User(Model):

  @scope
  def active(self, query, active_or_inactive):
    return query.where('active', active_or_inactive)

然后传递一个参数给它:

user = User.active(1).get()
user = User.active(0).get()

软删除

Masonite ORM 还具有可以对模型进行软删除的全局作用域。

只需继承 SoftDeletesMixin 作用域即可:

from masoniteorm.scopes import SoftDeletesMixin

class User(Model, SoftDeletesMixin):
  # ..

现在,每当你删除一条记录时将不会删除它,而是将 deleted_at 记录更新为当前时间戳:

User.where("id", 1).delete()
# == UPDATE `users` SET `deleted_at` = '2020-01-01 10:00:00' WHERE `id` = 1

当你获取记录时,它也只会获取未删除的记录:

User.all() #== SELECT * FROM `users` WHERE `deleted_at` IS NULL

你也可以禁用此行为:

User.with_trashed().all() #== SELECT * FROM `users`

你也可以只获取已删除的记录:

User.only_trashed().all() #== SELECT * FROM `users` WHERE `deleted_at` IS NOT NULL

你还可以恢复记录:

User.where('admin', 1).restore() #== UPDATE `users` SET `deleted_at` = NULL WHERE `admin` = '1'

最后,你可以覆盖此行为并强制执行 delete 查询:

User.where('admin', 1).force_delete() #== DELETE FROM `users` WHERE `admin` = '1'

注意:**你仍然需要添加 deleted_at 字段到你的数据库表,以使此功能工作

还有一个 soft_deletes() 帮助器,你可以在迁移中使用它来快速添加该字段。

# 用户迁移
with self.schema.create("users") as table:
  # ...
  table.soft_deletes()

如果列名不是 deleted_at ,你可以将列更改为其它名称:

from masoniteorm.scopes import SoftDeletesMixin

class User(Model, SoftDeletesMixin):
  __deleted_at__ = "when_deleted"

删除

你可以直接在模型上使用 truncate

User.truncate()

更新

你可以更新记录:

User.find(1).update({"username": "Joe"}, {'active': 1})

在更新记录时,只更新有修改改的属性。如果没有修改,则不会触发更新。

你可以用不同的方式重写这个行为:

  • 你可以将 force=True 传递给 update() 方法
User.find(1).update({"username": "Joe"}, force=True)
  • 你可以在模型类中定义 __force_update__ 属性
class User(Model):
    __force_update__ = True

User.find(1).update({"username": "Joe"})
  • 你可以在模型上使用 force_update() 方法:
User.find(1).force_update({"username": "Joe"})

你还可以更新或创建记录:

User.update_or_create({"username": "Joe"}, {
    'active': 1
})

如果存在用户名为「Joe」的记录,它将更新该记录,如果不存在,则创建该记录。

请注意,当记录被创建时,这两个字典将被合并在一起。因此,如果此代码要创建一个记录,它将创建一个同时包含用户名为 「Joe」和 active=1的记录。

更新记录时,updated_at 列将自动更新。你可以使用 activate_timestamps 方法控制此行为:

User.activate_timestamps(False).update({"username": "Sam"})  # updated_at won't be modified during this update

创建

你可以通过传递字典轻松创建记录:

User.create({"username": "Joe"})

这将把记录插入表中,创建并返回新的模型实例。

请注意,这将仅创建一个新的模型实例,但不会包含表上的任何其他字段。它将只有你传递给它的字段。

你可以在创建后重新获取模型,以获取记录的其余部分。这将使用 find 方法获取完整的记录。假设你有这样的情况,即数据库中的 active 标志默认为 1。如果我们创建记录,active 属性将无法获取,因为 Masonite ORM 不知道此属性。

在这种情况下,我们可以在创建后使用 .fresh() 重新获取记录:

user = User.create({"username": "Joe"}).fresh()

user.active #== 1

批量创建

你也可以使用查询构造器的 bulk_create 方法进行批量创建:

User.bulk_create([
  {"username": "Joe"},
  {"username": "John"},
  {"username": "Bill"},
  {"username": "Nick"},
])

这将返回一个已创建的用户集合。

由于批量创建涉及到所有相关模型的数据填充,所以当处理大量记录时,这可能会变得更慢。如果你要处理大量记录,那么直接使用查询构造器而不进行模型填充会更快。你可以获取一个新的查询构造器,然后调用所需的任何方法:

User.builder.new().bulk_create([
  {"username": "Joe"},
  {"username": "John"},
  {"username": "Bill"},
  {"username": "Nick"},
])

序列化

你可以快速地序列化一个模型:

User.serialize()
# returns {'id': 1, 'account_id': 1, 'first_name': 'John', 'last_name': 'Doe', 'email': 'johndoe@example.com', 'password': '$2b$12$pToeQW/1qs26CCozNiAfNugRRBNjhPvtIw86dvfJ0FDNcTDUNt3TW', 'created_at': '2021-01-03T11:35:48+00:00', 'updated_at': '2021-01-08T22:06:48+00:00' }

这将返回一个字典,其中包含所有模型字段。需要注意一些重要事项:

  • 日期字段将以 ISO 格式进行序列化
  • 预加载的关系将被序列化
  • __appends__ 中定义的属性将被添加

如果你想隐藏模型字段,可以在你的模型上使用 __hidden__ 属性:

# User.py
class User(Model):
  # ...
  __hidden__ = ["password", "created_at"]

同样,你可以在你的模型上使用 __visible__ 属性来明确指定哪些字段应包含在序列化中:

# User.py
class User(Model):
  # ...
  __visible__ = ["id", "name", "email"]

注意:你不能同时在模型上使用 __hidden____visible__

如果你需要更高级的序列化或构建复杂的 API,你应该使用 masonite-api 包。

将主键更改为使用 UUID

Masonite ORM 还提供了另一个全局作用域,使你可以在模型中使用 UUID 作为主键。

只需继承 UUIDPrimaryKeyMixin 作用域即可:

from masoniteorm.scopes import UUIDPrimaryKeyMixin

class User(Model, UUIDPrimaryKeyMixin):
  # ..

你可以在迁移文件中定义一个具有正确主键约束的UUID列。

with self.schema.create("users") as table:
    table.uuid('id')
    table.primary('id')

你的模型现在已设置为使用 UUID 作为主键。它将在创建时自动生成。

你可以更改要使用的 UUID 版本标准:

import uuid
from masoniteorm.scopes import UUIDPrimaryKeyMixin

class User(Model, UUIDPrimaryKeyMixin):
  __uuid_version__ = 3
  # the two following parameters are only needed for UUID 3 and 5
  __uuid_namespace__ = uuid.NAMESPACE_DNS
  __uuid_name__ = "domain.com

并且你还可以强制生成UUID返回字节而不是字符串:

from masoniteorm.scopes import UUIDPrimaryKeyMixin

class User(Model, UUIDPrimaryKeyMixin):
  __uuid_bytes__ = True

属性转换

并非所有数据都是你需要的格式。如果你发现自己将属性转换为不同的值,比如将 active 转换为 int,那么你可以在模型中将其设置为正确的类型:

class User(Model):
  __casts__ = {"active": "int"}

现在,当你在模型上获得 active 属性时,它将是一个 int

其他有效值:

  • int
  • bool
  • json

日期

MasonitePendulum 来表示日期。每当使用日期时,它将返回一个 Pendulum 实例。

你可以指定哪些字段是模型上的日期。这将用于序列化和其他逻辑需求:

class User(Model):

    __dates__ = ["verified_at"]

重写日期

如果要更改此行为,可以重写两个方法:Get_new_Date()Get_new_Datetime_String()

get_new_date() 方法接受 1 个参数,该参数是 datetime.datetime 的实例。你可以使用它来解析和返回你想要的任何日期。

class User(Model):

    def get_new_date(self, datetime=None):
        # return new instance from datetime instance.

如果 datetime 参数为 None,则返回当前日期。

get_new_datetime_string() 方法接受相同的 datetime 参数,但这次应该返回一个要在表中使用的字符串。

class User(Model):

    def get_new_datetime_string(self, datetime=None):
        return self.get_new_date(datetime).to_datetime_string()

访问器和修改器( Getter 和 Setter )

访问器和修改器是一种很好的方法,可以在获取和设置模型属性时对其进行微调。

要创建一个访问器,我们只需使用 get_{name}_attribute 方法名创建一个方法:

class User:

    def get_name_attribute(self):
        return self.first_name + ' ' + self.last_name

user = User.find(1)
user.first_name #== "Joe"
user.last_name #== "Mancuso"
user.name #== "Joe Mancuso"

对属性进行更改或设置也是如此:

class User:

    def set_name_attribute(self, attribute):
        return str(attribute).upper()

user = User.find(1)
user.name = "joe mancuso"
user.name #== "JOE MANCUSO"

模型事件

模型在其生命周期的不同阶段触发各种事件。可用的事件有:

  • booting
  • booted
  • creating
  • created
  • deleting
  • deleted
  • hydrating
  • hydrated
  • saving
  • saved
  • updating
  • updated

模型观察者

你可以通过观察者来监听各种事件。观察者是一个简单的类,它包含了与你想要监听的事件相同的方法。

例如,如果你想在创建用户时监听,你将创建一个包含 created 方法的 UserObserver 类。

你可以通过运行以下命令来构建一个观察者类:

masonite-orm observer User --model User

如果你没有指定模型选项,它会假设模型名称与观察者名称相同

一旦观察者被创建,你就可以将你的逻辑添加到事件方法中:

class UserObserver:
    def created(self, user):
        pass

    def creating(self, user):
        pass

    #..

在每个事件方法中接收的模型对象将是该时间点的模型。

然后,你可以将观察者设置为特定的模型。

如果你正在使用 Masonite,这可以在服务提供者中完成:

from app.models.User import User
from app.observers.UserObserver import UserObserver
from masonite.providers import Provider

class ModelProvider(Provider):
    #..

    def register(self):
        User.observe(UserObserver())
        #..

如果你在 Masonite 之外使用 Masonite ORM,则只需在模型定义的底部执行此操作:

from masoniteorm.models import Model
from some.place.UserObserver import UserObserver

class User(Model):
    #..

User.observe(UserObserver())

附加关联记录

很多时候,你需要获取多个相关记录,并根据另一个记录将它们都分配给同一个属性。

例如,你可能想要切换文章的作者。

为此,你可以使用 attachsave_many 方法。假设你有一个 User 模型,它有一个与 Articles 模型相关的 articles 方法。

user = User.find(1)
articles = Articles.where('user_id', 2).get()

user.save_many('articles', articles)

这将获取 user_id 为 2 的所有文章,并为它们分配用户和文章( user_id )之间的相关记录。

对于一对一的关联,你也可以这样做:

user = User.find(1)
phone = Phone.find(30)

user.attach('phone', phone)

分离关联记录

就像 attach 方法关联记录一样,你可以使用 detachdetach_many 记录来分离记录的关联。

你可以分离单个记录:

role = Role.find(1)
permissions = Permission.find(1)

role.detach('permissions', permission)

你还可以分离许多记录:

role = Role.find(1)
permissions = Permission.where('section', "dashboard").get()

role.detach_many('permissions', permissions)

属性

有一些属性用于处理模型数据。

脏属性

当你在模型上设置属性时,模型就会变得 「dirty」。这意味着模型的属性发生了变化。你可以很容易地检查模型是否脏了:

user = User.find(1)
user.is_dirty() #== False
user.name = "Joe"
user.is_dirty() #== True

你会得到一个脏属性:

user = User.find(1)
user.name #== Bill
user.name = "Joe"
user.get_dirty("name") #== Joe

这将获得脏属性的值,而不是模型上设置的属性。

原始数据

这将跟踪最初在模型上设置的原始数据。此数据在模型的整个生命周期中不会更改:

user = User.find(1)
user.name #== Bill
user.name = "Joe"
user.get_original("name") #== Bill

保存方法

一旦你在模型上设置了属性,你可以使用 save 方法将它们保存到表中:

user = User.find(1)
user.name #== Bill
user.name = "Joe"
user.save()

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/masonite/4.0/or...

译文地址:https://learnku.com/docs/masonite/4.0/or...

上一篇 下一篇
贡献者:5
讨论数量: 0
发起讨论 只看当前版本


暂无话题~