2014-11-23 21 views
6

Mam projekt w Ruby on Rails 4.1.4, stosując activeadmin 1.0.0.pre from git://github.com/activeadmin/activeadmin, pg 0.17.1, PostgreSQL 9.3Szyny + ActiveAdmin - filtrowanie z ransacker zgłasza błąd PG :: SyntaxError: error: błąd składni na lub blisko ""

W projekcie mam te modele:

  1. class User has_one :account

  2. class Account belongs_to :user has_many :project_accounts has_many :projects, :through => :project_accounts

  3. class Project # the project has a boolean attribute 'archive' has_many :project_accounts

  4. class ProjectAccount belongs_to :account belongs_to :project

Mam zadanie do realizacji filtru ActiveAdmin na stronie indeksu, zwany „by_active_projects”, a więc ma pokazać użytkownikom, że mają określoną liczbę aktywnych projekty, to znaczy takie projekty, które mają archive == false. E.g. jeśli wpiszę "2" w filtrze, musi znaleźć takie konta, które mają dokładnie 2 aktywne projekty.

Na razie mam zarejestrowaną „Konto” zasobu ActiveAdmin iw ciągu admin/account.rb Dodałem filter :by_active_projects_eq

Potem już zdefiniowany zakres having_active_projects dla modelu rachunku (modele/account.rb):

scope :having_active_projects, ->(number) { joins(:projects).where("projects.archive = ?", false).having("count(projects) = ?", number).group("accounts.id") } 

Następny krok, ja zdefiniował ransacker dla modelu konta tak:

ransacker :by_active_projects, formatter: proc{ |v| 
    data = Account.having_active_projects(v).map(&:id) 
    data ||= nil 
    } do |parent| 
    parent.table[:id] 
    end 

W rozwoju DB jest jedno konto, które ma dokładnie 8 aktywnych projektów, a filtrowanie działa świetnie. Ale gdy próbowałem filtrować konta przez 2 aktywne projekty, stanąłem przed błędem. W DB istnieją trzy takie rachunki, a strona błędu poinformował mnie, że składnia zapytanie jest źle:

SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114' LIMIT 30 OFFSET 0) subquery_for_count 

Jak widać, zamiast

"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114') 

ta rzecz jest generowany:

"accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114' 

Próbowałem zagłębić się w kod źródłowy, poruszając się z punktami przerwania na kawałkach kodu w obrębie bibliotek ActiveRecord, ActiveAdmin i Ransack i odkryłem, że relacja jest tworzona z pomocą Arel :: Węzły :: Równość. Nie jestem pewien, to jest powód, ale jedno mogę powiedzieć na pewno:

lib/active_record/relation/query_methods.rb `

560 def where!(opts = :chain, *rest) 
561  if opts == :chain 
562  WhereChain.new(self) 
563  else 
564  references!(PredicateBuilder.references(opts)) if Hash === opts 
565  self.where_values += build_where(opts, rest) 
566  self 
567  end 
568 end` 

self tutaj jest Active Record relacja na Rachunku;

przed wywołaniem build_where w wierszu # 565, self.to_sql równa

SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL ORDER BY "accounts"."created_at" desc 

po wywołaniu go i przypisując wynik self.where_values,

self.to_sql równa

SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114' ORDER BY "accounts"."created_at" desc 

pomocy lub informacje na ten temat są doceniane! Dzięki!

Odpowiedz

4

Więc znalazłem rozwiązanie:

Po pierwsze, ja zmieniłem filtr w admin/account.rb z

filter :by_active_projects_eq 

do

filter :by_active_projects_in, 
     :as => :string 

Takie podejście spowodowało poprawnej generacji SQL

"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114') 

Po tym miałem też zmienić swoje ransacker z

ransacker :by_active_projects, formatter: proc{ |v| 
    data = Account.having_active_projects(v).map(&:id) 
    data ||= nil 
    } do |parent| 
    parent.table[:id] 
    end 

do

ransacker :by_active_projects, formatter: proc{ |v| 
    data = Account.having_active_projects(v).pluck(:id) 
    data.present? ? data : nil 
    } do |parent| 
    parent.table[:id] 
    end 

ponieważ sposób, w jaki został wdrożony również spowodowane nieprawidłową zapytanie: na przykład nie ma takich kont, które mają dokładnie 5 aktywnych projektów . W takim przypadku

data = Account.having_active_projects(v).pluck(:id) 

powrócił „pustą tablicę”, oraz radzenia sobie z tej tablicy z data ||= nil nigdy nie powrócił nil, a to spowodowało SQL tak:

SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" IN() LIMIT 30 OFFSET 0) subquery_for_count 

Zawiadomienie "accounts"."id" IN() część, która była powodując kłopoty.

Po wymianie data ||= nil z data.present? ? data : nil jeśli data nie było obecnych było przypisać nil i część SQL wytworzono corectly: "accounts"."id" IN (NULL)

Powiązane problemy