代码之家  ›  专栏  ›  技术社区  ›  Lee McAlilly

如何在Rails中验证表单实例而不是整个数据库的唯一性?

  •  0
  • Lee McAlilly  · 技术社区  · 7 年前

    如何验证rails中的表单字段,使该值对该表单实例是唯一的,但在整个数据库中不是唯一的?

    我有一张表格 有用户可以创建报价的rails。关于我是如何工作的更多细节 here (模型、控制器、架构、表单)。

    该表单允许用户共享一位艺术家(如DavidBowie)对另一位艺术家(如LouReed)的引用。因此,引用可能如下所示:

    Topic: David Bowie 
    Content: "He was a master." 
    Speaker: Lou Reed
    

    演讲者和主题都是来自我的艺术家模型的关联。模型如下所示:

    class Quote < ApplicationRecord
      default_scope -> { order(created_at: :desc) }
    
      belongs_to :user
    
      belongs_to :speaker, class_name: "Artist"
      belongs_to :topic,   class_name: "Artist"
    
      validates :speaker, presence: true
      validates :topic,   presence: true
      validates :content, presence: true, length: { maximum: 1200 }
      validates :source,  presence: true, length: { maximum: 60 }
      validates :user_id, presence: true
    
    end
    
    class Artist < ApplicationRecord
      default_scope -> { order(name: :asc) }
    
      belongs_to :user
    
      has_many :spoken_quotes, class_name: "Quote", foreign_key: :speaker_id
      has_many :topic_quotes,  class_name: "Quote", foreign_key: :topic_id
    
      validates :user_id, presence: true
      validates :name,    presence: true, length: { maximum: 60 },
                          uniqueness: { case_sensitive: false }
    end
    

    表单如下所示:

    <%= form_for(@quote) do |f| %>
      <%= render 'shared/error_messages', object: f.object %>
      <div class="field">
    
        <div class="form-group">
          <label>Topic</label>
          <%= f.collection_select :topic_id, Artist.all, :id, :name %>
        </div>
    
        <div class="form-group">
          <label>Speaker</label>
          <%= f.collection_select :speaker_id, Artist.all, :id, :name %>
        </div>
    
        <div class="form-group">
          <%= f.text_area :content, class: "form-control",
          placeholder: "Share a new quote..." %>
        </div>
    
        <div class="form-group">
          <%= f.url_field :source, class: "form-control",
          placeholder: "http:// Link to your source" %>
        </div>
      </div>
      <%= f.submit "Post", class: "btn btn-primary btn-block" %>
    <% end %>
    

    首先,我尝试将这些验证添加到报价模型中:

    validates :speaker, uniqueness: {scope: :topic}
    validates :topic,   uniqueness: {scope: :speaker}
    

    这在第一次起作用,但一旦你尝试在第二次引用中重复使用艺术家作为演讲者或主题,例如,分享Lou Reed所说的关于David Bowiee以外的其他人的引用,这将验证Lou Reed的独特性,并且不会让我在第二次引用中重复使用Lou Reed,因为他在第一次引用中已经被用作演讲者。这不是预期的行为,因此接下来我尝试将其添加到报价模型中:

    validates :speaker, presence: true, uniqueness: true, if: :speaker_and_topic_are_the_same?
    
    def speaker_and_topic_are_the_same?
      :topic_id == :speaker_id
    end
    

    这种方法背后的思想是,只有当说话人和主题在同一形式上相同时,它才会强制进行唯一性验证。

    但不幸的是,这不起作用,让我可以创建一个引语,其中演讲者和主题是同一位艺术家。

    那么,在rails中实施这种验证的正确方法是什么呢?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Bartosz Pietraszko    7 年前

    艺术家作为演讲者和主题的多次出现是模型逻辑的核心;我想没有 uniqueness 根本无法验证-既没有范围也没有条件。您想要的验证只考虑报价单的单个实例的上下文;我认为应该是这样 a custom one .

    validate :topic_cant_be_a_speaker
    
    def topic_cant_be_a_speaker
      errors.add(:speaker, "Topic can't be a speaker") if speaker == topic
    end