STI 모델에 대한 연결을 미리 로드하는 방법
14761 단어 railsoptimisationruby
preload
방법으로 연관을 미리 로드할 수 없다는 것입니다. 이제 처리 방법을 보여 드리겠습니다.사용자가 정보를 업로드하고 게시물을 작성할 수 있는 Instagram 스타일 애플리케이션이 있다고 상상해 보십시오. 다양한 유형의 게시물이 있습니다.
코드베이스는 STI 패턴을 기반으로 합니다.
# base class
class Post < ApplicationRecord
end
# standard post with one image
class ImagePost < Post
has_one :image, dependent: :destroy, foreign_key: :post_id
end
# multi images post
class MultiImagePost < Post
has_many :images, dependent: :destroy, foreign_key: :post_id
end
# post with video
class VideoPost < Post
has_one :video, dependent: :destroy, foreign_key: :post_id
end
또한
Image
클래스는 Asset
와 관련이 있습니다.class Image < ApplicationRecord
belongs_to :post, foreign_key: :post_id
has_one :asset
end
class Asset < ApplicationRecord
belongs_to :image
end
게시물 유형마다 피드가 다르며 애플리케이션이 완벽하게 작동합니다.
ImagePost.preload(image: :asset).each do |item|
render 'image_post', item: item
end
# database queries:
# ImagePost Load (0.8ms) SELECT "posts".* FROM "posts" WHERE "posts"."type" = ? [["type", "ImagePost"]]
# Image Load (1.4ms) SELECT "images".* FROM "images" WHERE "images"."post_id" IN (?, ?) [["post_id", 21], ["post_id", 22]]
# Asset Load (0.9ms) SELECT "assets".* FROM "assets" WHERE "assets"."image_id" IN (?, ?) [["image_id", 5], ["image_id", 6]]
MultiImagePost.preload(images: :asset).each do |item|
render 'multi_image_post', item: item
end
# database queries:
# MultiImagePost Load (0.7ms) SELECT "posts".* FROM "posts" WHERE "posts"."type" = ? [["type", "MultiImagePost"]]
# Image Load (1.1ms) SELECT "images".* FROM "images" WHERE "images"."post_id" IN (?, ?) [["post_id", 20], ["post_id", 25]]
# Asset Load (1.0ms) SELECT "assets".* FROM "assets" WHERE "assets"."image_id" IN (?, ?, ?, ?) [["image_id", 3], ["image_id", 4], ["image_id", 7], ["image_id", 8]]
VideoPost.preload(:video).each do |item|
render 'video_post', item: item
end
# database queries:
# VideoPost Load (0.9ms) SELECT "posts".* FROM "posts" WHERE "posts"."type" = ? [["type", "VideoPost"]]
# Video Load (0.9ms) SELECT "videos".* FROM "videos" WHERE "videos"."post_id" IN (?, ?) [["post_id", 23], ["post_id", 24]]
어느 날 한 고객이 모든 유형의 게시물을 하나의 피드에 렌더링하도록 요청했습니다. 그러나 한 가지 문제가 있습니다. 표준을 사용할 수 없습니다
.preload
.ActiveRecord::AssociationNotFoundError (Association named 'image' was not found on MultiImagePost; perhaps you misspelled it?)
당신은 무엇을해야합니까?
ActiveRecord::Associations::Preloader
를 사용하여 새 피드에 대한 사용자 정의 사전 로드 로직을 작성할 수 있습니다. 클래스의 새 인스턴스를 생성한 다음 preload
메소드를 사용해야 합니다. 두 개의 매개변수가 필요합니다.코드는 rails-magic처럼 보이지 않으며 모든 관계를 명시적으로 설명해야 하지만 완벽하게 작동합니다.
posts = Post.all
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(posts.select{ |i| i.type == 'ImagePost' }, image: :asset)
preloader.preload(posts.select{ |i| i.type == 'MultiImagePost' }, images: :asset)
preloader.preload(posts.select{ |i| i.type == 'VideoPost' }, :video)
posts.each |item|
render 'post', item: item
end
로그를 살펴보면
Preloader
각 연결에 대해 하나의 쿼리를 보내고 N+1 쿼리가 없습니다.# 1) take posts from DB
Post Load (0.4ms) SELECT "posts".* FROM "posts"
# 2) load images for all ImagePosts
Image Load (1.5ms) SELECT "images".* FROM "images" WHERE "images"."post_id" IN (?, ?) [["post_id", 21], ["post_id", 22]]
# 3) load assets for them
Asset Load (1.0ms) SELECT "assets".* FROM "assets" WHERE "assets"."image_id" IN (?, ?) [["image_id", 5], ["image_id", 6]]
# 4) load images for all MultiImagePost
Image Load (0.4ms) SELECT "images".* FROM "images" WHERE "images"."post_id" IN (?, ?) [["post_id", 20], ["post_id", 25]]
# 4) load assets for them
Asset Load (0.2ms) SELECT "assets".* FROM "assets" WHERE "assets"."image_id" IN (?, ?, ?, ?) [["image_id", 3], ["image_id", 4], ["image_id", 7], ["image_id", 8]]
# 5) load videos for all VideoPosts
Video Load (0.4ms) SELECT "videos".* FROM "videos" WHERE "videos"."post_id" IN (?, ?) [["post_id", 23], ["post_id", 24]]
Reference
이 문제에 관하여(STI 모델에 대한 연결을 미리 로드하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/kopylov_vlad/how-to-preload-associations-for-sti-model-422a텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)