Rails find_or_create_by more than one attribute?

后端 未结 5 703
广开言路
广开言路 2020-11-27 09:27

There is a handy dynamic attribute in active-record called find_or_create_by:

Model.find_or_create_by_(: => \"\")

相关标签:
5条回答
  • 2020-11-27 10:00

    For anyone else who stumbles across this thread but needs to find or create an object with attributes that might change depending on the circumstances, add the following method to your model:

    # Return the first object which matches the attributes hash
    # - or -
    # Create new object with the given attributes
    #
    def self.find_or_create(attributes)
      Model.where(attributes).first || Model.create(attributes)
    end
    

    Optimization tip: regardless of which solution you choose, consider adding indexes for the attributes you are querying most frequently.

    0 讨论(0)
  • 2020-11-27 10:06

    In Rails 4 you could do:

    GroupMember.find_or_create_by(member_id: 4, group_id: 7)
    

    And use where is different:

    GroupMember.where(member_id: 4, group_id: 7).first_or_create
    

    This will call create on GroupMember.where(member_id: 4, group_id: 7):

    GroupMember.where(member_id: 4, group_id: 7).create
    

    On the contrary, the find_or_create_by(member_id: 4, group_id: 7) will call create on GroupMember:

    GroupMember.create(member_id: 4, group_id: 7)
    

    Please see this relevant commit on rails/rails.

    0 讨论(0)
  • 2020-11-27 10:09

    Multiple attributes can be connected with an and:

    GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
    

    (use find_or_initialize_by if you don't want to save the record right away)

    Edit: The above method is deprecated in Rails 4. The new way to do it will be:

    GroupMember.where(:member_id => 4, :group_id => 7).first_or_create
    

    and

    GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize
    

    Edit 2: Not all of these were factored out of rails just the attribute specific ones.

    https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md

    Example

    GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
    

    became

    GroupMember.find_or_create_by(member_id: 4, group_id: 7)
    
    0 讨论(0)
  • 2020-11-27 10:11

    You can do:

    User.find_or_create_by(first_name: 'Penélope', last_name: 'Lopez')
    User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_create
    

    Or to just initialize:

    User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
    User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize
    
    0 讨论(0)
  • 2020-11-27 10:21

    By passing a block to find_or_create, you can pass additional parameters that will be added to the object if it is created new. This is useful if you are validating the presence of a field that you aren't searching by.

    Assuming:

    class GroupMember < ActiveRecord::Base
        validates_presence_of :name
    end
    

    then

    GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }
    

    will create a new GroupMember with the name "John Doe" if it doesn't find one with member_id 4 and group_id 7

    0 讨论(0)
提交回复
热议问题