【问题标题】:How to validate if a column combination already exists in database, rails 6, postgres如何验证数据库,rails 6,postgres中是否已经存在列组合
【发布时间】:2021-02-12 15:06:48
【问题描述】:

这里是新手!

ruby 2.7.1,gem --version 3.1.4,Rails 6.0.3.4

我的查询出现错误

def entry_uniqueness
    if Entry.where("lower(from) = ? AND lower(to) = ?",
                   from.downcase, to.downcase).exists?
      errors.add(:base, 'Identical combination already exists. YUP.')
    end
  end

错误:

PG::SyntaxError: ERROR: syntax error at or near "from" LINE 1: SELECT 1 AS one FROM "entries" WHERE (lower(from) = 'here' A... ^

完全错误

app/models/entry.rb:35:in entry_uniqueness' app/controllers/entries_controller.rb:9:in create' 开始 POST ::1 在 2021-02-13 16:17:47 +0800 处理的“/entries” EntriesController#create 作为 HTML 参数: {"authenticity_token"=>"98sjupNso6NW5xUonE/414I7ZvJQETMPBNWS+jcN+PffHaAJ3K0pdQofVPgnrBfflYn2SDXMlB17Q2G/gzideA==", "entry"=>{"translation"=>"1", "from"=>"here", "to"=>"there"}, "commit"=>"翻译后"} [1m[36mEntry 存在? (10.2ms)[0m [1m[34mSELECT 1 AS one FROM "entries" WHERE (lower(from) = 'here' AND lower(to) = 'there') LIMIT $1[0m [["LIMIT", 1]] ↳ app/models/entry.rb:35:in `entry_uniqueness' 完成 500 内部 38 毫秒内的服务器错误(ActiveRecord:10.2 毫秒 | 分配:2140)

ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: syntax 在“来自”第 1 行或附近出现错误:从“条目”中选择 1 作为其中之一 (lower(from) = '这里' A... ^ ): app/models/entry.rb:35:in entry_uniqueness' app/controllers/entries_controller.rb:9:in create'


我想要实现的是:


| from  |  to   |
-----------------
| Here  | there |

=> 验证应该阻止用户添加heRE | THERE,但可以添加THERE | heRE

:from:to 列在表 entries 中。


注意:我已经尝试过作用域,它们在唯一性方面起作用,但在不区分大小写时失败了????

validates_uniqueness_of :from, scope: :to

也试过了

validates_uniqueness_of :from, :scope => :to, :case_sensitive => false

也尝试了 @r4cc00n 的实现,但它不起作用

scope :get_entries_by_from_and_to, ->(from, to) { where(arel_table[:from].lower.eq(from)).where(arel_table[:to].lower.eq(to))}
  validates_uniqueness_of :from, if: :entry_uniqueness?

  def entry_uniqueness?
    Entry.get_entries_by_from_and_to('from','to').nil?
  end

【问题讨论】:

  • 区分大小写的错误 => 错误范围?
  • 区分大小写,因此失败。例如输入 Here there 是正确的唯一性验证,但 HeRe TheRe 未能受到限制,因此不区分大小写失败
  • 知道了,如果你想试试我的回答,希望对你有帮助????
  • 能否请您发布您在 postgres 查询中收到的整个错误?
  • 这是一个语法错误,输出只是在那之前。检查附加的新图片

标签: ruby-on-rails ruby validation ruby-on-rails-6


【解决方案1】:

from 是一个reserved name in PostgreSQL。这很有意义,因为读取查询通常包含这个词(想想SELECT value FROM table)。因此,Rails 根据您的条件构建的查询

SELECT 1 AS one FROM "entries" WHERE (lower(from) = 'here' A...
                ^^^^                        ^^^^

混淆了数据库。顺便说一句,to 也是保留名称。

避免遇到此类问题的一种方法是您应该考虑重命名该列。

另一个简单的解决方法是将列名用双引号括起来。只需将您的条件更改为:

Entry.exists?('lower("from") = ? AND lower("to") = ?', from.downcase, to.downcase)

请注意,我在列名周围使用了双引号,在整个条件周围使用了单引号。

【讨论】:

    【解决方案2】:

    除了@Spickermann 提到的列命名问题,您还应该考虑使用citext (case insentive) type column。与您的方法相比,它有许多优点:

    • 您不必使用详细查询。 WHERE foo = ? 不分大小写。
    • 您可以声明一个多列唯一索引以确保数据库级别的唯一性 - 不区分大小写。这可以防止潜在的duplicates due to race conditions

    缺点是,如果您必须切换到其他数据库,如 MySQL 或 Oracle,您的应用程序的可移植性会降低,这可能不是一个真正的问题。

    这当然要求您能够在 Postgres 数据库中启用扩展,并且您还需要确保在测试、开发和生产中使用相同的数据库(无论如何这都是个好主意)。

    class EnableCitext < ActiveRecord::Migration
      def change
        enable_extension("citext")
      end
    end
    
    class ChangeFoosToCitext < ActiveRecord::Migration
      def change
        change_column :foos, :bar, :citext
        change_column :foos, :baz, :citext 
        add_index :foos, [:bar, :baz], unique: true
      end
    end
    

    这将让您使用范围的简单验证:

    validates :bar, uniqueness: { scope: :baz }
    

    您无需使用会生成无效查询的case_sentive: false 选项。

    【讨论】:

    • 如果你愿意,你可以将它压缩成一个迁移。我通常使用单独的迁移来启用扩展,因为如果它崩溃了,它更容易调试。
    【解决方案3】:

    以下是我认为您可以解决查询的方法:

    您可以在 Entry 模型中定义一个范围,然后在任何地方使用该范围,如下所示:

    scope :get_entries_by_from_and_to, ->(from, to) { where(arel_table[:from].lower.eq(from)).where(arel_table[:to].lower.eq(to))}
    

    然后您可以使用它:Entry.get_entries_by_from_and_to('your_from','your_to'),如果返回 nil,那么您的数据库中没有符合您条件的记录。

    话虽如此,如果你想将它与你所拥有的和验证范围结合起来,你可以像下面这样:

    def entry_uniqueness?
        Entry.get_entries_by_from_and_to('your_from','your_to').nil?
    end
    validates_uniqueness_of :from, if: :entry_uniqueness? 
    
    

    请注意 validates_uniqueness_of 不是线程/并发安全的,这意味着在非常奇怪的情况下,您可能会遇到一些情况,您的数据库中没有唯一数据,为避免这种情况,您应该始终在其中创建唯一索引您的数据库,因此数据库将为您避免那些“重复”的场景。

    希望这会有所帮助! ?

    【讨论】:

    • 我已经尝试过您的建议,但它doesn't work 的唯一性或不区分大小写。检查我在原始帖子中对您的实施所做的修改
    • 使用 Arel 构造查询将起作用,因为它会引用 @spickermann 答案中的键。但是,您没有理由必须创建一个实际上只是一个类方法的范围 - 您可以在验证中直接使用 Arel。在 text/varchar 列上创建不区分大小写的唯一索引也有点棘手,因为您必须自己编写约束 - 这就是为什么要使用 citext。
    猜你喜欢
    • 2019-03-11
    • 2020-06-21
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 2014-04-10
    • 1970-01-01
    • 2020-07-15
    • 2014-04-12
    相关资源
    最近更新 更多