Rails before_create 按字母顺序递增 [英] Rails before_create increment in alphabetical order

查看:57
本文介绍了Rails before_create 按字母顺序递增的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我的表中有一个字段 - 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆