2012-08-14 12 views
14

Tak, więc mam polimorficzne skojarzenie, które pozwala faworyzować różne typy obiektów. Tak więc osoba może faworyzować produkt, osobę lub cokolwiek innego. Co chcę zrobić, to chronić się przed kimś, kto powiela ulubione za pomocą sprawdzania wyjątkowości w modelu Ulubione.Sprawdzanie unikatowości zakresu w polimorficznych modelach asocjacyjnych

class Favorite < ActiveRecord::Base 
belongs_to :favoritable, :polymorphic => true 
belongs_to :user 

attr_accessible :user 

validates_presence_of :user 
validates :user_id, :uniqueness => { :scope => [:favoritable_type, :favoritable_id] } 
end 

Walidacja wydaje się działać, ale z jakiegoś powodu nowego Ulubiony wiersz jest nadal tworzone z user_id gdy podejmowana jest próba powielać ten wpis.

enter image description here

Czy istnieje sposób, aby zatrzymać ten początkowy uratować?

Wydaje się, że Rails jest stworzenie wpisu DB i następnie aktualizowanie go z favoritable_id i favoritable_type następująco:

SQL (28.3ms) INSERT INTO "favorites" ("created_at", "favoritable_id", "favoritable_type", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Tue, 14 Aug 2012 10:26:31 UTC +00:00], ["favoritable_id", nil], ["favoritable_type", nil], ["updated_at", Tue, 14 Aug 2012 10:26:31 UTC +00:00], ["user_id", 23]] 
    (7.8ms) COMMIT 
    (0.1ms) BEGIN 
    Favorite Exists (0.3ms) SELECT 1 AS one FROM "favorites" WHERE ("favorites"."user_id" = 23 AND "favorites"."id" != 123 AND "favorites"."favoritable_type" = 'Style' AND "favorites"."favoritable_id" = 29) LIMIT 1 
    (0.2ms) UPDATE "favorites" SET "favoritable_id" = 29, "favoritable_type" = 'Style', "updated_at" = '2012-08-14 10:26:31.943937' WHERE "favorites"."id" = 123 
    (6.7ms) COMMIT 
    (0.1ms) BEGIN 
+0

Abram: czy kiedykolwiek tego dowiedzieć? – MAckerman

Odpowiedz

21

Jeśli uważnie obserwować można stwierdzić, że sprawdzanie unikatowości prostu działać poprawnie :)

validates :user_id, :uniqueness => { :scope => [:favoritable_type, :favoritable_id] } 

Spójrz na obrazie danych dodanej. wewnątrz obrazu można się przekonać, że drugi rekord nie ma wartości favouritable, podczas gdy pierwsza ma inną wartość, dlatego 2 rekordy to uniq i nie ma problemu z uniqueness, ale jest to luka logiczna.

Jeśli ściśle chciał uniknąć drugi wpis następnie zachować favouritable jako pole obowiązkowe

validates :favoritable_type, :favoritable_id, :presence => true 
+0

Niestety, ten wynik w Ulubionych istnieje (0,4 ms) WYBIERZ 1 JAK Z "ulubionych" GDZIE ("ulubione". "Identyfikator_użytkownika" = 36 ORAZ "ulubione". "Favoritable_type" JEST NULL I "ulubione". "Favoritable_id" IS NULL) LIMIT 1 (0,1 ms) ROLLBACK Zakończony 500 Internal Server Error w 243ms ActiveRecord :: RecordInvalid (Validation failed: Favoritable typ nie może być puste, Favoritable nie może być puste): – Abram

+1

można też dodać wyjątkowość ograniczenie do twojej tabeli poprzez migrację: 'add_index (: ulubiony, [: id_użytkownika,: favoritable_type,: favoritable_id],: unique => true,: name => 'allowed_one_favorite')' – Darme

0

Czy można przejść przez podobnym stanowisku

Rails 3 uniqueness validation with scope on polymorphic table

To wydaje to add_index ma znaczenie.

+0

Awesome tak, to zrobiło. Daje ci kleszcza w kilka minut, gdy jest dozwolone. – Abram

+0

Argh. Nie, cofam to. To nie zadziałało. Próbowałem add_index (: ulubione, [: user_id,: favoritable_id,: favoritable_type],: unique => true,: name => 'cannot_duplicate_favorite') – Abram

+0

Jak wspomniano w odnośniku - jeśli masz już ten model, musisz zrobić indeks ręcznie przez SQL. Zazwyczaj indeks zostanie dodany w momencie migracji. Sprawdź to raz. – pdpMathi

5
class Favorite < ActiveRecord::Base 

    belongs_to :user 
    belongs_to :favoritable, polymorphic: true 

    validates :user_id, :favoritable_id, presence: true, 
    numericality: { only_integer: true } 

    validates :favoritable_type, presence: true, 
    inclusion: { 
     in: %w(FirstModelName SecondModelName), 
     message: "%{value} is not a valid" 
    } 

    validates :user_id, uniqueness: { scope: [ :favoritable_type, :favoritable_id ] } 
end 
Powiązane problemy