Rails before_create 按字母顺序递增 [英] Rails before_create increment in alphabetical order
问题描述
所以我的表中有一个字段 - exposure
.该字段的名称称为setup_code
,每次创建 exposure
时,我都想从 A-Z 增加 setup_code
我怎样才能做到这一点?
So I have a field in my table - exposure
. The name of the field is called setup_code
,
Every time an exposure
is created, I want to increment setup_code
from A-Z how can I achieve that?
根据 另一个答案,这是我到目前为止所做的工作,我看到可以使用 <代码>下一个代码>
Here's what I have done so far, according to this other answer I saw that it is possible to increment using next
class Exposure < ApplicationRecord
before_create :create_setup_code
def create_setup_code
last_setup_code = self.last.setup_code
last_setup_code.next
end
end
如果我已经在数据库中至少有一个 setup_code,这种方法将起作用,比如 setup_code = 'A',然后对于下一个 setup_code,当 before_create
被调用时它将是 'B'.
This approach will work if I already have at least one setup_code in the database, say setup_code = 'A', then for the next setup_code, it will be 'B' when before_create
is called.
如何使用第一个 setup_code = 'A'
进行初始化?
How do I initialize with the first setup_code = 'A'
?
推荐答案
在 Postgres 中实际上有一种很酷的方法,通过将计算的默认值应用到列.
There is actually a pretty cool way to do this in Postgres by applying a computed default value to the column.
首先我们要定义一个 Postgres 函数,它给你一个给定整数 (1 = A, 26 = Z) 的字母,这样我们就可以得到一个给定 ID 的字母.
First we want to define a Postgres function that gives you a letter given an integer (1 = A, 26 = Z) so that we can get a letter given an ID.
class CreateIdToLetterFunction < ActiveRecord::Migration[5.2]
def up
execute <<-SQL
CREATE OR REPLACE FUNCTION id_to_letter(integer) RETURNS varchar
AS 'select chr(64 + $1)'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
SQL
end
def down
execute <<-SQL
DROP FUNCTION id_to_letter(integer);
SQL
end
end
chr(int)
返回具有给定 ASCII 代码的字符.在 ASCII 中,A 为 65,因此我们需要用 64 填充该值.
chr(int)
returns the character with the given ASCII code. In ASCII A is at 65 so we need to pad the value with 64.
然后我们添加计算曝光次数的函数,以便我们可以使用默认值:
We then add function that counts the exposures so that we can use it the default:
class AddExposureCountFunction < ActiveRecord::Migration[5.2]
def up
execute <<-SQL
CREATE OR REPLACE FUNCTION count_exposures() RETURNS bigint
AS 'select count(*) from exposures'
LANGUAGE SQL
VOLATILE;
SQL
end
def down
execute <<-SQL
DROP FUNCTION count_exposures();
SQL
end
end
然后我们想要更改 Exposures.setup_code 列以添加默认值.
We then want to alter the exposures.setup_code column to add a default value.
class AddDefaultSetupCodeToExposures < ActiveRecord::Migration[5.2]
def change
change_column_default(
:exposures,
:setup_code,
from: nil,
to: -> {"id_to_letter(CAST(count_exposures() AS integer) + 1)"}
)
end
end
我们将新的默认值包装在 lambda (->{}
) 中,因为这告诉 ActiveRecord 该值不是文字值,应作为 SQL 添加到语句中.
We wrap the new default in a lambda (->{}
) as this tells ActiveRecord that the value is not a literal value and should be added to the statement as SQL.
由于这是在 DB 级别处理的,因此不需要额外的模型代码.请注意,有关数据库设置的默认值的警告适用 - 在您从数据库重新加载实例之前,该值将为 nil.
As this is handled on the DB level no additional model code is needed. Note that the caveat regarding default values set by the DB applies - the value will be nil until you reload the instance from the db.
但是,如果您希望在功能上与评论中的极差代码相同,您只需要:
However if you want something functionally equal to the epically bad code from your comment you just need:
class Exposure < ApplicationRecord
SETUP_CODES = %w{ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A2 B2 C2 D2 E2 F2 G2 H2 }
def self.next_setup_code(current_code)
if !SETUP_CODES.index(current_code) || current_code == SETUP_CODES.last
"error"
else
SETUP_CODES[ SETUP_CODES.index(current_code) + 1 ]
end
end
end
这篇关于Rails before_create 按字母顺序递增的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!