Istnieją sytuacje, w których ActiveRecord ustawia nazwę tabeli aliasów, jeśli istnieje wiele połączeń z tą samą tabelą. Utknąłem w sytuacji, w której te połączenia zawierają zakresy (używając "scalania").Dołącz do tej samej tabeli dwa razy z warunkami
Mam wiele do wielu relacji:
modele nazwa_tabeli:
users
drugie modele nazwa_tabeli:
posts
Dołącz nazwa tabeli:
access_levels
posta ma wielu użytkowników przez access_levels i na odwrót.
Zarówno model użytkownika i model post akcja taka sama relacja:
has_many :access_levels, -> { merge(AccessLevel.valid) }
Zakres wewnątrz modelu AccessLevel wygląda następująco:
# v1
scope :valid, -> {
where("(valid_from IS NULL OR valid_from < :now) AND (valid_until IS NULL OR valid_until > :now)", :now => Time.zone.now)
}
# v2
# scope :valid, -> {
# where("(#{table_name}.valid_from IS NULL OR #{table_name}.valid_from < :now) AND (#{table_name}.valid_until IS NULL OR #{table_name}.valid_until > :now)", :now => Time.zone.now)
# }
Chciałbym zadzwonić coś takiego:
Post.joins(:access_levels).joins(:users).where (...)
Aktywny Record tworzy alias dla drugiego sprzężenia ("access_levels_users"). Chcę odwołać się do tej nazwy tabeli wewnątrz "poprawnego" zakresu modelu AccessLevel.
V1 oczywiście generuje PG::AmbiguousColumn
-Error. V2 powoduje, że przedrostek obu stanów jest z access_levels.
, co jest semantycznie błędne.
ten sposób wygenerować zapytania: (uproszczony)
# inside of a policy
scope = Post.
joins(:access_levels).
where("access_levels.level" => 1, "access_levels.user_id" => current_user.id)
# inside of my controller
scope.joins(:users).select([
Post.arel_table[Arel.star],
"hstore(array_agg(users.id::text), array_agg(users.email::text)) user_names"
]).distinct.group("posts.id")
Wygenerowany zapytanie wygląda następująco (przy użyciu valid
zakresu v2 od góry):
SELECT "posts".*, hstore(array_agg(users.id::text), array_agg(users.email::text)) user_names
FROM "posts"
INNER JOIN "access_levels" ON "access_levels"."post_id" = "posts"."id" AND (("access_levels"."valid_from" IS NULL OR "access_levels"."valid_from" < '2014-07-24 05:38:09.274104') AND ("access_levels"."valid_until" IS NULL OR "access_levels"."valid_until" > '2014-07-24 05:38:09.274132'))
INNER JOIN "users" ON "users"."id" = "access_levels"."user_id"
INNER JOIN "access_levels" "access_levels_posts" ON "access_levels_posts"."post_id" = "posts"."id" AND (("access_levels"."valid_from" IS NULL OR "access_levels"."valid_from" < '2014-07-24 05:38:09.274675') AND ("access_levels"."valid_until" IS NULL OR "access_levels"."valid_until" > '2014-07-24 05:38:09.274688'))
WHERE "posts"."deleted_at" IS NULL AND "access_levels"."level" = 4 AND "access_levels"."user_id" = 1 GROUP BY posts.id
ActiveRecord ustawia alias propriate "access_levels_posts" dla drugiego sprzężenia tabeli access_levels. Problem polega na tym, że scalony valid
-scope prefiksuje kolumnę z "access_levels" zamiast "access_levels_posts". Próbowałem również użyć języka isl do wygenerowania zakresu:
# v3
scope :valid, -> {
where arel_table[:valid_from].eq(nil).or(arel_table[:valid_from].lt(Time.zone.now)).and(
arel_table[:valid_until].eq(nil).or(arel_table[:valid_until].gt(Time.zone.now))
)
}
Wynikowe zapytanie pozostaje takie samo.
Twoje pytanie jest trochę mylące, ale myślę, że wiem, co chcesz zrobić. zmień "poprawny" zakres na 'join (: user) .where (" (valid_from IS NULL OR valid_from <: now) ORAZ (valid_until IS NULL OR valid_until>: now) ", teraz: Time.zone.now) .where (users: {active: true, lub: something}) ' – jvnill