Callback详解
Callbacks
Callback Registration
在 Rails 中,回调(Callbacks)是一种在模型对象的生命周期中执行特定代码的机制。回调可以在模型对象的创建、更新、删除等操作中执行特定的代码,例如保存对象前执行某些逻辑,或者在对象被删除前执行清理操作。
Rails 中的回调分为两种类型:前置回调(before callbacks)和后置回调(after callbacks)。前置回调在操作执行之前执行,后置回调在操作执行之后执行。可以使用 before_ 和 after_ 前缀来指定回调的类型。
以下是一些常见的回调类型:
before_validation和after_validation:在验证对象之前和之后执行回调。before_save和after_save:在保存对象之前和之后执行回调。before_create和after_create:在创建对象之前和之后执行回调。before_update和after_update:在更新对象之前和之后执行回调。before_destroy和after_destroy:在删除对象之前和之后执行回调。
要注册回调,可以在模型类中使用 before_ 或 after_ 前缀来指定回调的类型,然后指定要执行的方法:
class User < ApplicationRecord
before_save :normalize_email
private
def normalize_email
self.email = email.downcase
end
end
在上面的例子中,我们将 normalize_email 方法注册为 before_save 回调。这意味着在保存 User 对象之前,Rails 将自动调用 normalize_email 方法。在 normalize_email 方法中,我们将 email 属性转换为小写字母,以确保所有邮件地址都是小写的。
需要注意的是,注册回调时必须指定要执行的方法的名称,可以是一个实例方法或一个类方法。回调方法中可以使用模型对象的任何属性或方法来执行特定的逻辑,例如更新其他对象、发送电子邮件等。
使用回调可以让我们更灵活地控制模型对象的行为,可以在对象的生命周期中执行任意的操作。同时,回调也可以提高代码的可读性和可维护性,使代码更易于理解和修改。
Available Callbacks
在 Rails 中,可以注册多种类型的回调来在模型对象的生命周期中执行特定代码。以下是可用的回调类型:
创建和保存对象
before_validation:在验证对象之前执行回调。after_validation:在验证对象之后执行回调。before_save:在保存对象之前执行回调,包括新建和更新操作。around_save:在保存对象之前和之后执行回调,使用yield方法来执行保存操作。after_save:在保存对象之后执行回调,包括新建和更新操作。before_create:在创建对象之前执行回调。around_create:在创建对象之前和之后执行回调,使用yield方法来执行创建操作。after_create:在创建对象之后执行回调。
更新和删除对象
before_update:在更新对象之前执行回调。around_update:在更新对象之前和之后执行回调,使用yield方法来执行更新操作。after_update:在更新对象之后执行回调。before_destroy:在删除对象之前执行回调。around_destroy:在删除对象之前和之后执行回调,使用yield方法来执行删除操作。after_destroy:在删除对象之后执行回调。
关联对象
before_add_association:在添加关联对象之前执行回调。after_add_association:在添加关联对象之后执行回调。before_remove_association:在删除关联对象之前执行回调。after_remove_association:在删除关联对象之后执行回调。
其他
after_initialize:在实例化对象之后执行回调。after_find:在从数据库中查找对象之后执行回调。
要注册回调,可以在模型类中使用相应的回调方法来指定回调的类型,然后指定要执行的方法。例如,要在保存对象之前执行特定的逻辑,可以使用 before_save 方法:
class User < ApplicationRecord
before_save :normalize_email
private
def normalize_email
self.email = email.downcase
end
end
在上面的例子中,我们将 normalize_email 方法注册为 before_save 回调。这意味着在保存 User 对象之前,Rails 将自动调用 normalize_email 方法。在 normalize_email 方法中,我们将 email 属性转换为小写字母,以确保所有邮件地址都是小写的。
需要注意的是,回调方法中可以使用模型对象的任何属性或方法来执行特定的逻辑,例如更新其他对象、发送电子邮件等。使用回调可以让我们更灵活地控制模型对象的行为,可以在对象的生命周期中执行任意的操作。同时,回调也可以提高代码的可读性和可维护性,使代码更易于理解和修改。
回调前更新属性会怎么办
如果在回调中尝试更新属性,可能会导致一些问题。因为回调的执行顺序是不确定的,所以在某些情况下,属性的更新可能会被其他回调覆盖或被数据库中的持久化数据覆盖。
例如,如果我们在 before_save 回调中尝试更新某个属性,而在 after_save 回调中有另一个回调也尝试更新同一个属性,那么最终属性值可能会是不确定的,因为最后执行的回调会覆盖之前的值。
为了避免这种情况,应该尽量避免在回调中更新属性。如果确实需要更新属性,可以使用 update_column 方法来更新属性,该方法可以直接将属性更新到数据库中,而不触发其他回调。但是需要注意,使用 update_column 方法将跳过所有的验证,包括模型定义的验证,因此需要谨慎使用。
另外,如果在回调中需要使用其他模型对象的数据,可以将逻辑移动到控制器或服务对象中,以确保数据正确性和可维护性。
after_initialize and after_find
after_initialize 和 after_find 都是 ActiveRecord 模型中的回调方法。
after_initialize 方法会在创建新的 ActiveRecord 对象或从数据库中加载现有对象时被调用。该方法可以用来执行任意初始化逻辑,例如设置默认值或初始化关联对象。与其他回调不同,after_initialize 方法不需要接收任何参数,因为它是在对象创建之后立即调用的。
以下是一个示例,演示如何使用 after_initialize 方法在创建新对象时设置默认值:
class User < ApplicationRecord
after_initialize :set_defaults
private
def set_defaults
self.status ||= "active"
end
end
在上面的例子中,我们将 set_defaults 方法注册为 after_initialize 回调。在 set_defaults 方法中,我们检查 status 属性是否为 nil,如果是,则将其设置为默认值 "active"。
after_find 方法会在从数据库中查找 ActiveRecord 对象之后被调用。该方法可以用来执行任意的后处理逻辑,例如计算属性或更新关联对象。after_find 方法接收一个参数,即从数据库中加载的 ActiveRecord 对象。
以下是一个示例,演示如何使用 after_find 方法计算用户的年龄:
class User < ApplicationRecord
after_find :calculate_age
private
def calculate_age
self.age = Date.today.year - birthday.year
end
end
在上面的例子中,我们将 calculate_age 方法注册为 after_find 回调。在 calculate_age 方法中,我们使用从数据库中加载的用户对象的生日属性计算用户的年龄,并将结果保存到年龄属性中。
需要注意的是,after_find 方法只会在从数据库中加载对象时被调用,而不会在实例化新对象时被调用。如果需要在对象创建后执行某些逻辑,应该使用 after_initialize 方法。
after_touch什么 意思
after_touch是Rails中的一个回调方法,它会在一个已关联的对象被touch操作更新后被触发。在Rails中,touch操作指的是在更新一个对象时,同时更新关联对象的更新时间戳(updated_at)字段。这个操作可以用来实现缓存失效、重新计算统计数据等功能。
例如,假设你有一个User模型和一个Post模型,一个用户可以拥有多篇文章。当你更新某篇文章时,你可能需要更新相关用户的更新时间戳,以便在用户列表或其他地方正确地排序。你可以使用touch选项来实现这一点,如下所示:
class Post < ApplicationRecord
belongs_to :user, touch: true
end
class User < ApplicationRecord
has_many :posts
after_touch :update_sorting
def update_sorting
# 更新用户排序,例如更新`updated_at`字段
self.touch
end
end
这段代码定义了两个Active Record模型,Post和User,它们之间存在一个一对多的关联关系。
在Post模型中,使用belongs_to :user, touch: true声明了一个属于关联,表示一篇文章属于一个用户。touch: true选项表示当文章被更新时,自动更新与之关联的用户的updated_at字段,以便在用户列表或其他地方正确地排序。
在User模型中,使用has_many :posts声明了一个拥有多个关联,表示一个用户可以拥有多篇文章。after_touch :update_sorting声明了一个after_touch回调方法,表示当与之关联的一篇文章被touch操作更新时,自动调用update_sorting方法更新用户排序,例如更新updated_at字段。
在update_sorting方法中,使用self.touch方法更新用户的updated_at字段,以便在用户列表或其他地方正确地排序。
需要注意的是,这段代码中使用了touch操作来实现自动更新关联对象的更新时间戳,这可以用来实现缓存失效、重新计算统计数据等功能。在Rails中,touch操作是一种常见的技巧,可以用来简化代码并提高性能。
需要注意的是,after_touch回调方法只会在touch操作触发更新时被调用。如果你手动更新了updated_at字段,after_touch回调方法不会被调用。
运行回调
这些是Rails中常用的Active Record模型操作方法,下面分别介绍它们的作用:
create(attributes = {}):创建一个新的模型对象并将其保存到数据库中。可以传入一个哈希表参数attributes表示要创建的模型对象的属性值。create!(attributes = {}):与create相同,但如果保存失败会抛出异常。destroy:从数据库中删除当前模型对象。destroy!:与destroy相同,但如果删除失败会抛出异常。destroy_all:删除符合条件的所有模型对象,不进行任何回调和验证。destroy_by(conditions):根据条件删除符合条件的单个模型对象,不进行任何回调和验证。save:将当前模型对象的属性值保存到数据库中。如果对象不存在,则创建一个新的对象。save!:与save相同,但如果保存失败会抛出异常。save(validate: false):与save相同,但不进行模型对象的验证。toggle!:将当前模型对象的布尔类型属性取反并保存到数据库中。touch:更新当前模型对象的updated_at字段,并保存到数据库中。这个方法通常用于更新缓存或触发回调方法。update_attribute(name, value):更新当前模型对象的单个属性值,并直接保存到数据库中,不进行任何验证。update(attributes):更新当前模型对象的属性值,并保存到数据库中。可以传入一个哈希表参数attributes表示要更新的属性值。update!(attributes):与update相同,但如果更新失败会抛出异常。valid?:检查当前模型对象是否通过验证。如果验证失败,可以使用errors方法查看错误信息。
这些方法是Rails中常用的Active Record模型操作方法,可以用于创建、更新、删除和验证模型对象。需要注意的是,这些方法中有些会触发回调方法、进行验证或抛出异常,具体使用时需要根据实际情况选择合适的方法。
Callback详解的更多相关文章
- JAVA回调机制(CallBack)详解
序言 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义.当然了,我在理解了回 ...
- [转]JAVA回调机制(CallBack)详解
看见一篇博客比较通俗的解释了回调机制,转载一下,感谢原文作者Bro__超,原文地址:http://www.cnblogs.com/heshuchao/p/5376298.html 序言 最近学习jav ...
- Android关于SurfaceView,SurfaceHolder,SurfaceHolder.CallBack详解
官方的定义: 1.SurfaceView SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface.你可以控制这个Surface的格式和尺寸.Surfacev ...
- Node.js npm 详解
一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...
- (转载) Linux IO模式及 select、poll、epoll详解
注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案 ...
- Android 网络框架之Retrofit2使用详解及从源码中解析原理
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
- JavaScript事件详解-Zepto的事件实现(二)【新增fastclick阅读笔记】
正文 作者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致.此文的fastclick理解上在看过博客园各个大神 ...
- spring-mvc注解(mvc:annotation-driver,JSON,配置详解)
一.DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter 的使用已经过时! spring 3.1 开始我们应该用 Reque ...
- jQuery Validate验证框架详解
转自:http://www.cnblogs.com/linjiqin/p/3431835.html jQuery校验官网地址:http://bassistance.de/jquery-plugins/ ...
- AJAX请求详解 同步异步 GET和POST
AJAX请求详解 同步异步 GET和POST 上一篇博文(http://www.cnblogs.com/mengdd/p/4191941.html)介绍了AJAX的概念和基本使用,附有一个小例子,下面 ...
随机推荐
- WV电影网站的设计与实现-可行性研究分析报告
引言 WV(Wonderful View)电影网站--奇景电影网. 1.1编写目的 1.2背景 在信息发展的时代,地球人口越来越多,人们相比去拥挤的电影院,更喜欢待在舒适的家中,通过互联网访问本站,实 ...
- ubuntu安装matplotlib失败:Can't rollback pillow, nothing uninstalled.
今天在ubuntu1804上面使用pip安装matplotlib,安装失败,报错如下: ---------------------------------------- Can't rollback ...
- (二).JavaScript的运算符和表达式,数据类型转化
4. 运算符和表达式 4.3 赋值运算符和表达式 1.赋值运算符 = 作用:赋值运算符就是将右边的内容赋值给左边的变量或属性. var result = 1 + 2; 2.复合赋值运算符 +=,-=, ...
- opengl编程天天踩的坑
1. VBO 的 target 是 GL_ARRAY_BUFFER 不是 GL_VERTEX_BUFFER 2. glUniform()用来给uniform传变量 别用成 glProgramUnif ...
- 小米盒子TV变装魔法
最近从一位台湾的朋友那里白嫖了一个 v2 节点, 恰好家里有一台家用的 小米盒子, 就寻思着能不能折腾一下, 共享上网 先将小米盒子开启adb调试, 参照这里: https://www.jb51.ne ...
- Cmake 把 CGAL的demo 编译生成 .sln文件 遇到的一些问题
尝试了N个版本后,选择了CGAL5.02 为啥去官网或者github下载的CGAl只是一个库,没有窗口,而这个却有呢 链接:https://pan.baidu.com/s/1TvrWQRc9yYD ...
- mergehex tools安装
(1)nRF5x command line tools包括Jlink驱动以及Nordic自己开发的一些命令行工具,具体包括Jlink驱动,nrfjprog,nrfutil以及mergehex等. 下载 ...
- Linux命令之定时调度-crontab
以上cron命令格式为:* * * * * [root run-parts] command 前面五个*号代表时间,用数字,数字的取值范围和含义如下: 第一个*:分钟 (0-59)[整数] 第二个*: ...
- Blog作业01
目录 前言 设计与分析 踩坑心得 改进建议 总结 前言 这三次作业的知识点覆盖的很全面,从最开始的int赋值变量,循环,到Boolean值,sort等一些函数,到后来的对象,类的创建和声明,gette ...
- 使用request对象实现注册示例,请求方式的编码问题
get提交方式: method="get"和地址栏请求方式默认都属于get提交方式 get方式在地址栏显示请求信息﹐(但是地址栏能够容纳的信息有限,4-5KB;如果请求数据存在大文 ...