Rails find_or_create_by nhiều hơn một thuộc tính?


202

Có một thuộc tính động tiện dụng trong bản ghi hoạt động được gọi là find_or_create_by:

Model.find_or_create_by_<attribute>(:<attribute> => "")

Nhưng nếu tôi cần find_or_create bởi nhiều hơn một thuộc tính thì sao?

Giả sử tôi có một mô hình để xử lý mối quan hệ M: M giữa Nhóm và Thành viên được gọi là GroupMember. Tôi có thể có nhiều trường hợp thành viên_id = 4, nhưng tôi không bao giờ muốn nhiều hơn một lần trong đó thành viên_id = 4 và group_id = 7. Tôi đang cố gắng tìm hiểu xem có thể làm điều gì đó như thế này không:

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

Tôi nhận ra có thể có nhiều cách tốt hơn để xử lý việc này, nhưng tôi thích sự tiện lợi của ý tưởng find_or_create.

Câu trả lời:


464

Nhiều thuộc tính có thể được kết nối với một and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(sử dụng find_or_initialize_bynếu bạn không muốn lưu bản ghi ngay lập tức)

Chỉnh sửa: Phương pháp trên không được dùng trong Rails 4. Cách mới để thực hiện sẽ là:

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

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

Chỉnh sửa 2: Không phải tất cả những thứ này được bao gồm ngoài đường ray chỉ là những thuộc tính cụ thể.

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

Thí dụ

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

đã trở thành

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

Bạn cũng có thể thích github.com/seamusabshere/upsert - đối số đầu tiên là hàm băm của các thuộc tính được sử dụng để tìm hoặc tạo bản ghi
Seamus abshere

1
Sẽ rất hữu ích khi lưu ý rằng khởi tạo cuộc gọi tạo () thay vì mới () có thể được gọi trong trường hợp First_or_create
Sumit Bisht

Ai đó có thể chia sẻ một ví dụ chính của việc sử dụng này? Tôi không quen với vị trí và gọi của nó.
6ft Dan

Tôi đã hoàn nguyên việc xóa mã Rails 3 vì nhiều người chưa sử dụng Rails 4.
Kyle Heironimus

Chỉ cần thêm: các hàm find_or_create_by _ * () đã không được dùng trong Rails 4, tuy nhiên find_or_create_by () thì KHÔNG. Bạn có thể sử dụng find_or_create_by (). Kiểm tra cam kết nơi đã được thêm github.com/rails/rails/commit/ vào và tài liệu Rails 4.2 chính thức cho việc sử dụng: guide.rubyonrails.org/v4.2.0/iêu
CodeExpress 6/1/2015

33

Đối với bất kỳ ai khác tình cờ phát hiện ra chủ đề này nhưng cần tìm hoặc tạo một đối tượng với các thuộc tính có thể thay đổi tùy theo hoàn cảnh, hãy thêm phương thức sau vào mô hình của bạn:

# 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

Mẹo tối ưu hóa: bất kể bạn chọn giải pháp nào, hãy xem xét thêm chỉ mục cho các thuộc tính bạn đang truy vấn thường xuyên nhất.


8
Một điều cần lưu ý là điều này sẽ không xử lý các thuộc tính không thể được gán khối lượng, trong khi find_or_create_bysẽ.
x1a4

1
Marco, cố gắng Model.where(attributes).instance_eval{|q| q.first || q.create}.
hiroshi

3
find_or_createcũng có lợi ích của việc sử dụng một giao dịch, trong đó where….first || createđưa ra một điều kiện cuộc đua
mrm

Tôi sẽ nói def self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) endđể bạn không cần lặp lạiModel
Augustin Riedinger

@AugustinRiedinger Bạn cũng không cần selftrong thân phương thức (vì bạn đang tìm cách loại bỏ các từ!).
David Tuite

31

Trong Rails 4 bạn có thể làm:

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

Và sử dụng wherelà khác nhau:

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

Điều này sẽ gọi createvề GroupMember.where(member_id: 4, group_id: 7):

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

Ngược lại, những find_or_create_by(member_id: 4, group_id: 7)sẽ gọi createvề GroupMember:

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

Xin vui lòng xem điều này có liên quan cam kết trên đường ray / đường ray.


16

Bằng cách vượt qua một khối để find_or_create , bạn có thể truyền các tham số bổ sung sẽ được thêm vào đối tượng nếu nó được tạo mới. Điều này hữu ích nếu bạn xác thực sự hiện diện của một lĩnh vực mà bạn không tìm kiếm.

Giả định:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

sau đó

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

sẽ tạo một GroupMember mới với tên "John Doe" nếu nó không tìm thấy một với member_id 4 and group_id 7


4

Bạn có thể làm:

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

Hoặc chỉ khởi tạo:

User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.