Mam standardu wiele-do-wielu między użytkownikami i rolami w moim app szyn:Szyny idiom, aby uniknąć duplikatów w has_many: poprzez
class User < ActiveRecord::Base
has_many :user_roles
has_many :roles, :through => :user_roles
end
chcę, aby upewnić się, że użytkownik może być przypisany tylko jakakolwiek rola raz. Każda próba wstawienia duplikatu powinna zignorować żądanie, nie może powodować błędu ani powodować niepowodzenia sprawdzania poprawności. To, co naprawdę chcę reprezentować, to "zbiór", w którym wstawienie elementu, który już istnieje w zestawie, nie ma żadnego efektu. {1,2,3} U {1} = {1,2,3}, a nie {1,1,2,3}.
Zdaję sobie sprawę, że mogę to zrobić tak:
user.roles << role unless user.roles.include?(role)
lub tworząc metodę otoki (np add_to_roles(role)
), ale miałem nadzieję na jakiś idiomatycznym sposób, aby to automatyczny poprzez stowarzyszenia, tak że Mogę napisać:
user.roles << role # automatically checks roles.include?
i to po prostu działa za mnie. W ten sposób nie muszę pamiętać, aby sprawdzić dups lub użyć niestandardowej metody. Czy jest coś w ramie, której mi brakuje? Najpierw myślałem, że opcja: uniq do has_many to zrobi, ale jest to po prostu "wybierz odrębny".
Czy istnieje sposób, aby to zrobić w sposób deklaratywny? Jeśli nie, może korzystając z rozszerzenia powiązania?
Oto przykład tego, jak domyślne zachowanie nie powiedzie się:
>> u = User.create User Create (0.6ms) INSERT INTO "users" ("name") VALUES(NULL) => #<User id: 3, name: nil> >> u.roles << Role.first Role Load (0.5ms) SELECT * FROM "roles" LIMIT 1 UserRole Create (0.5ms) INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3) Role Load (0.4ms) SELECT "roles".* FROM "roles" INNER JOIN "user_roles" ON "roles".id = "user_roles".role_id WHERE (("user_roles".user_id = 3)) => [#<Role id: 1, name: "1">] >> u.roles << Role.first Role Load (0.4ms) SELECT * FROM "roles" LIMIT 1 UserRole Create (0.5ms) INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3) => [#<Role id: 1, name: "1">, #<Role id: 1, name: "1">]
To nie działa w ten sposób. Zaktualizuję post, aby dołączyć test. – KingPong
Dzięki, spróbuję rozszerzenia stowarzyszenia. – KingPong
To działało idealnie. Dzięki! Część, której mi brakowało, gdy próbowałam czegoś takiego, to był kawałek proxy_owner. – KingPong