through rails many has_one has_many has_and_belongs_to_many has example belongs_to belongs and active ruby-on-rails ruby duplicates has-many-through has-many

ruby-on-rails - has_one - rails has_many through example



¿Cómo evitar duplicados en una relación has_many: through? (8)

¿Cómo puedo lograr lo siguiente? Tengo dos modelos (blogs y lectores) y una tabla de JOIN que me permitirán tener una relación N: M entre ellos:

class Blog < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :readers, :through => :blogs_readers end class Reader < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :blogs, :through => :blogs_readers end class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader end

Lo que quiero hacer ahora es agregar lectores a diferentes blogs. La condición, sin embargo, es que solo puedo agregar un lector a un blog UNA VEZ. Por lo tanto, no debe haber ningún duplicado (mismo readerID , mismo blogID ) en la tabla BlogsReaders . ¿Cómo puedo conseguir esto?

La segunda pregunta es, ¿cómo obtengo una lista de blog a la que los lectores ya no están suscritos (por ejemplo, para completar una lista de selección desplegable, que luego puede usarse para agregar el lector a otro blog)?


La forma Rails 5.1

class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { distinct }, through: :blogs_readers end class Reader < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :blogs, -> { distinct }, through: :blogs_readers end class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader end


Esto debería encargarse de tu primera pregunta:

class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader validates_uniqueness_of :reader_id, :scope => :blog_id end


Estoy pensando que alguien vendrá con una mejor respuesta que esta.

the_reader = Reader.find(:first, :include => :blogs) Blog.find(:all, :conditions => [''id NOT IN (?)'', the_reader.blogs.map(&:id)])

[editar]

Por favor, vea la respuesta de Josh a continuación. Es el camino a seguir. (Sabía que había una mejor manera de salir de allí;)



Una solución más simple que está integrada en Rails:

class Blog < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :readers, :through => :blogs_readers, :uniq => true end class Reader < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :blogs, :through => :blogs_readers, :uniq => true end class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader end

Tenga en cuenta que agrega la opción :uniq => true a la llamada has_many .

También es posible que desee considerar has_and_belongs_to_many entre Blog y Reader, a menos que tenga otros atributos que le gustaría tener en el modelo de unión (que no tiene actualmente). Ese método también tiene :uniq opiton :uniq .

Tenga en cuenta que esto no le impide crear las entradas en la tabla, pero sí garantiza que cuando consulte la colección obtendrá solo una de cada objeto.

Actualizar

En Rails 4 la forma de hacerlo es a través de un bloque de alcance. Lo anterior cambia a.

class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { uniq }, through: :blogs_readers end class Reader < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :blogs, -> { uniq }, through: :blogs_readers end class BlogsReaders < ActiveRecord::Base belongs_to :blog belongs_to :reader end


La forma más fácil es serializar la relación en una matriz:

class Blog < ActiveRecord::Base has_many :blogs_readers, :dependent => :destroy has_many :readers, :through => :blogs_readers serialize :reader_ids, Array end

Luego, al asignar valores a los lectores, los aplica como

blog.reader_ids = [1,2,3,4]

Al asignar relaciones de esta manera, los duplicados se eliminan automáticamente.



La respuesta principal actualmente dice usar uniq en el proceso:

class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { uniq }, through: :blogs_readers end

Sin embargo, esto inicia la relación en una matriz y puede romper cosas que están esperando realizar operaciones en una relación, no una matriz.

Si usa distinct lo mantiene como una relación:

class Blog < ActiveRecord::Base has_many :blogs_readers, dependent: :destroy has_many :readers, -> { distinct }, through: :blogs_readers end