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_at
和 updated_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_id
和 product_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_at
和 created_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_at
和 updated_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 个模型的列表。第一个模型是你要加入的远程表。在本例中,我们将加入一个运往 countries
的 Shipment
,因此我们将 Country
作为第一个列表元素。第二个元素是我们需要从 Shipment
到 Country
的中间表。在本例中,这是 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
很多 comment
的 user
的关系。
因此, 一个 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 个模型的列表。第一个模型是你想要连接的远程表。在本例中,我们将 user
与 comments
连接,所以我们将 Comment
放在列表的第一个元素中。第二个元素是我们需要从 User
到 Comment
获得的中间表。在这种情况下,这是 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
可以与 Comment
或 Article
相关联。
设置
在一个多态表上,我们通常有一个记录类型(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_type
和 record_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_type
和 record_id
字段。这些字段可以任意命名,但应该包含一个 varchar
类型的列,用于映射到一个模型,以及一个列用于存放外键表的主键值。
在这种情况下,likes
表仍然与多个模型具有 one
关联,但是相关的表( articles
和 comments
)有多个记录与 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 ORM
有 morph_to
和 morph_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
日期
Masonite
用 Pendulum
来表示日期。每当使用日期时,它将返回一个 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())
附加关联记录
很多时候,你需要获取多个相关记录,并根据另一个记录将它们都分配给同一个属性。
例如,你可能想要切换文章的作者。
为此,你可以使用 attach
和 save_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
方法关联记录一样,你可以使用 detach
和 detach_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()
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。