How does activerecord change model associations in rails? - ruby-on-rails

I at one point had a simple model where a recipe had many photos, yet I was only creating Recipe objects by setting one photo through carrierwave on the recipe.photo attribute rather than through recipe.photos.
This was in part because I didn't realize I had specified has_many. I was creating Recipe objects through rails_admin gem by assigning to the recipe.photo attribute rather than recipe.photos.
class Recipe < ActiveRecord::Base
validates :title, :content, presence: true
belongs_to :category
has_many :photos, class_name: 'RecipePhoto', dependent: :destroy
mount_uploader :photo, RecipePhotoUploader
accepts_nested_attributes_for :photos, allow_destroy: :true
end
class RecipePhoto < ActiveRecord::Base
belongs_to :recipe
mount_uploader :photo, RecipePhotoUploader
end
So my question is when I began assigning to recipe.photos and then were to access a recipe instances photos via recipe.photos
I would only see photos that were created onto recipe.photos, and not recipe.photo.
Why is this? Shouldn't ActiveRecord access ALL associated photos to the recipe with notation recipe.photos?
Why does it treat it as recipe.photo were assigned via a has_one relationship even though it was has_many the whole time?
Shouldn't ActiveRecord be robust to changing relationships? Like if it at a later date and wanted to change it to has_one, etc.

Related

Finding entries in a join table with identical links

In my chat app I have users and chats. The tables for each of these is connected by a join table:
class User < ApplicationRecord
has_and_belongs_to_many :chats_users
has_and_belongs_to_many :chats, :through => :chats_users
end
class Chat < ApplicationRecord
has_and_belongs_to_many :chats_users
has_and_belongs_to_many :users, :through => :chats_users
end
class ChatsUsers < ApplicationRecord
belongs_to :chat, class_name: 'Chat'
belongs_to :user, class_name: 'User'
validates :ad_id, presence: true
validates :tag_id, presence: true
end
And the inverse in chat.rb.
When creating a new chat with a list of participating list of user_ids, I want to first check a chat doesn't already exist with the exact same list of associated user_ids, but I can't work out a sane way to do this. How can this be done?
has_and_belongs_to_many is only used in the case where you do not need a join model (or where you initially think you don't need it) as its headless. Instead you want to use has_many through::
class User < ApplicationRecord
has_many :chat_users
has_many :chats, through: :chat_users
end
class Chat < ApplicationRecord
has_many :chat_users
has_many :users, through: :chat_users
end
class ChatUser < ApplicationRecord
belongs_to :chat
belongs_to :user
# not needed if these are belongs_to associations in Rails 5+
validates :ad_id, presence: true
validates :tag_id, presence: true
end
You may need to create a migration to change the name of your table to chat_users and make sure it has a primary key.
has_and_belongs_to_many uses an oddball plural_plural naming scheme that will cause rails to infer that the class is named Chats::User since plural words are treated as modules. While you can work around that by explicitly listing the class name its better to just align your schema with the conventions.
If your still just messing about in development roll back and delete the migration that created the join table and run rails g model ChatUser chat:belongs_to user:belongs_to to generate the correct table with a primary key and timestamps.
If you want to select chats connected to a given set of users:
users = [1,2,3]
Chat.joins(:users)
.where(users: { id: users })
.group(:id)
.having(User.arel_table[Arel.star].count.eq(users.length))
.exists?
Note that you don't really need to tell ActiveRecord which table its going through. Thats the beauty of indirect associations.

Rails - Paperclip ROLLBACK due to associations

I have 3 models. The question, answer and photo. I am using paperclip to save images. Questions and answers can have multiple images.
However, I get ROLLBACK when saving images for answer model. I don't get ROLLBACK when saving images for question model. I think I have a problem with model associations.
#photo model
class Photo < ApplicationRecord
belongs_to :question
belongs_to :answer
has_attached_file :image :path => ":rails_root/public/img/:filename", validate_media_type: false
do_not_validate_attachment_file_type :image
end
#answer model
class Answer < ApplicationRecord
belongs_to :question
has_many :photos
end
#question model
class Question < ApplicationRecord
has_many :answers
has_many :photos
end
My Controller :
p.answers.each do |a|
new_answer = q.answers.create(body: a[:body])
if a[:images]
a[:images].each do |e|
new_answer.photos.create(image: URI.parse('www.abc.com/'+e))
end
end
end
Any thoughts?
Since rails 5 the belongs_to associations are by default required.
There are two possible solutions, either write
belongs_to :question, optional: true
in your Photo model, or, as you are saving them through the nested association (a bit like a nested-form), you have to clearly indicate which association is the inverse of which.
So in your answer class, specify the inverse of the :photos association
has_many :photos, inverse_of: :answer
(to allow rails to correctly check that the belongs_to is set correctly)

Rails Polymorphic Association with multiple associations on the same model bug

I have same problem as : Rails Polymorphic Association with multiple associations on the same model
But the solutions on this question aren't working for me. I have a picture model and an event model. Event has many pictures and one cover pic. Here are both the models.
class Picture < ActiveRecord::Base
belongs_to :image, polymorphic: true
end
class Event < ActiveRecord::Base
has_many :pictures, as: :image, :dependent => :destroy
has_one :cover_picture, -> { where image_type: "CoverPicture"},
class_name: Picture, foreign_key: :image_id,
foreign_type: :image_type, dependent: :destroy
end
Issue here is that when I create a new picture and set it as cover_picture of an event, it doesn't set the image_type to "CoverPicture". When I try and save it after specifically setting the image_type to "CoverPicture", it errors out with "NameError: uninitialized constant CoverPicture"
image_type has a specific function in this polymorphic association... it identifies the associated model (just as image_id identifies the id).
You should not be changing image_type as that breaks the association.
Make a new boolean column in the picture model, say cover_picture, and you can do...
has_one :cover_picture, -> {where cover_picture: true} ...
The advantage of this is your cover picture is also included in your pictures association, but if you want that picture excluded from the has_many then you can apply a where clause to that as well...
has_many :pictures, -> {where.not cover_picture: true} ...

Getting ActiveRecord associations belongs_to through

I have models associated with has_many through. I am able to get a relationship in one direction but not the other (there is no belongs_to through setting)
Projects have_many datasets through taskflows. Taskflows are associated to datasets in a join table.
I am able to do Project.datasets simply by using the has_many through setting. I'd also like to call dataset.project to get the associated project of a dataset (through taskflow).
Is this possible? Many thanks for any help.
I have four models (I've tried the delegate setting but it doesn't seem to work):
class Project < ActiveRecord::Base
validates :title, presence: true, length: {minimum: 3}
has_many :taskflows
has_many :datasets, :through => :taskflows
end
class Taskflow < ActiveRecord::Base
belongs_to :project
has_many :dataset_assignments
has_many :datasets, :through => :dataset_assignments
end
class Dataset < ActiveRecord::Base
has_many :dataset_assignments
has_many :taskflows, :through => :dataset_assignments
delegate :project, :to => :taskflows, :has_nil =>true
end
class DatasetAssignment < ActiveRecord::Base
belongs_to :dataset
belongs_to :taskflow
end
has_many_through works both ways.
If you put the has_many_through in reverse in your Dataset Model it should work as you wish.
A good example I use - and I learned on
Recipe Model
Ingredient Model
Component Model
A Recipe has many ingredients through components
An Ingredient has many recipes through components.
In my limited experience with it, your logic makes sense, the has_many through usually flows 'one way' in terms of creation, but in terms of rails it's the same relation type - an ingredient 'has_many' recipes through the components

Join Table Confusion Ruby on Rails

I have read a lot of the questions and answers here about join tables, STI tables, and polymorphic associations, in addition to many articles and documentation spread throughout the internet. While I've learned a lot I'm still confused about what I should do in my situation. I may have read the answer and not known I was reading the answer, but I wanted to see if someone could help me understand what it is I should do here.
I have a Gallery model, an Album model, an Image model and a Category model. These are all nested in a User model.
When you create an Album assign a Category to it and those are saved with an Album_Categories model. I want the Gallery model to be aware of what Categories exist and be able to choose which ones it would like to use.
Once it selects a Category, it should be able to access the Albums associated with the Category and the Album's Images, which are linked through and Album_Images join table. The Category should be able to continue to exist even if the Album or Gallery that it was originally created with is deleted so that another Album or Gallery can take advantage of it later.
My sense is that whenever a unique Category is created is should some how connect to Gallery through a Category_Galleries model, but in my use of Images which is connected to Gallery and Album with their own specific join tables, Gallery is unaware of an Album_images Connection, so I assume sharing the knowledge of a Category created by the other would be the same.
Any way to help me unerstand this would be appreciated.
Edit: model code
class User < ActiveRecord::Base
has_many :images, dependent: :destroy
has_many :galleries, dependent: :destroy
has_many :albums, dependent: :destroy
has_many :categories, dependent: :destroy
accepts_nested_attributes_for :images, :galleries, :albums, :categories, allow_destroy: true
accepts_attachments_for :images, attachment: :file, append: true
end
class Image < ActiveRecord::Base
belongs_to :user
has_many :gallery_images, dependent: :destroy
has_many :galleries, through: :gallery_images
has_many :album_images, dependent: :destroy
has_many :albums, through: :album_images
attachment :file, type: :image
validates :file, presence: true
end
class Album < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
has_many :album_galleries
has_many :galleries, through: :album_galleries # , dependent: :destroy
has_many :album_images, dependent: :destroy
has_many :images, through: :album_images
has_many :album_categories
has_many :categories, through: :album_categories
accepts_attachments_for :images, attachment: :file, append: true
accepts_nested_attributes_for :images
end
class Gallery < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
has_many :gallery_images, dependent: :destroy
has_many :images, through: :gallery_images
has_many :album_galleries, dependent: :destroy
has_many :albums, through: :album_galleries
accepts_attachments_for :images, attachment: :file, append: true
accepts_nested_attributes_for :images
end
class Category < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
has_many :albums, through: :album_categories
has_many :album_categories
end
class GalleryImage < ActiveRecord::Base
belongs_to :gallery
belongs_to :image
end
class AlbumCategory < ActiveRecord::Base
belongs_to :category
belongs_to :album
end
class AlbumGallery < ActiveRecord::Base
belongs_to :gallery
belongs_to :album
end
class AlbumImage < ActiveRecord::Base
belongs_to :album
belongs_to :image
end
It really depends on the requirements you're trying to model. Does this accurately reflect your requirements? (ignoring the user for the current moment and not necessarily detailing rails associations)
A gallery can consist of many categories
A category can contain many albums
An album can have many images
If so, you could simply have:
a has_many through association between galleries and categories
a has_many through association between albums and categories
a has_many through association between albums and images
The has_many through will allow your categories, galleries, albums and images to exist even after relations are destroyed.
At the moment I don't see any need for STI or polymorphism. Usually you use polymorphic associations when two models share (own) the same table. But since you would use has_many through associations, polymorphism wouldn't even be necessary. (It prevents clashes of the owning table ids when occuring as a foreign key in the owned table).
To get to images, from gallery, for example you would be essentially displaying all the images of all albums belonging to all categories that are assigned to a gallery. That can be done through associations and querying.
So basically, I don't think your scenario...based on my understanding...is too complex and has_many through associations should suffice.
An interesting question would by why a user is associated to all of your models. Are they responsible for creating/those model instances a user is associated to?

Resources