Modelling nested models in rails - ruby-on-rails

I've been grappling with a problem which is proving to be quite hard. I've got a User model, a Photo model and a comment model. Now the way my website works is that a User can have many comments on a particular photo. On the reverse side, a comment can only belong to a particular user on a particular photo.
I've read through the Active Record Associations docs and from what I've gathered is that we can't use a has_many :through associations since it appears to work for polymorphic associations between models. I'm wondering if one can use has_many :through association on one side and belongs_to association on the reverse side.
Any tips, pointers and advice? I'm just beginning in Ruby on Rails
Thanks.

Wouldn't this work?
class User
has_many :photos
has_many :comments
end
class Photo
belongs_to :user
has_many :comments
end
class Comment
belongs_to :user
belongs_to :photo
end
User has many photos and comments (the ones he uploaded/written), and each comment belongs to user (writer) and a photo which was commented on.

#app/models/user.rb
class User < ActiveRecord::Base
has_many :photos
has_many :comments, through: :photos #-> probably won't work but I'd try it first
end
#app/models/photo.rb
class Photo < ActiveRecord::Base
belongs_to :user
has_many :comments do
def current_user #-> photo.comments.current_user
where user_id: self.user_id
end
end
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :photo
belongs_to :user
end
--
You could access the photo's comments as follows:
<% #user.photos.each do |photo| %>
<%= photo.comments.each do |comment| %>
<%= comment %>
<% end %>
<% end %>
If you wanted to show only the user's comments, you'd be able to use the current_user ActiveRecord Association Extension:
<% #user.photos.each do |photo| %>
<%= photo.comments.current_user.each do |comment| %>
<%= comment %> #-> comments where user_id will be the same as photo's user_id
<% end %>
<% end %>

You can do it like this:
User
has_many :comments
Photo
has_many :comments
belongs_to :user
Comment
belongs_to :user
belongs_to :photo

Related

Solution for nested form

I have been stuck on this problem for a while.
Need to make a form for competitions category with custom inputs. It should take all values from Information table and build the inputs, but the tricky part is that it should be saved to Category_informations table.
class Competition < ApplicationRecord
has_many :categories
has_many :informations
end
class Category < ApplicationRecord
belongs_to :competetion
has_many :category_informations
has_many :information, through: competition
end
class CategoryInformation
belongs_to :catagory
belongs_to :information
end
class Information < ApplicationRecord
belongs_to :competetion
has_many :category_informations
end
Competition -> name
Category -> name, competition_id
Information -> name, competition_id
Category_informations -> value, category_id, information_id
Take a look at this gem: https://github.com/plataformatec/simple_form
Simple Form aims to be as flexible as possible while helping you with powerful components to create your forms.
Let's take a simple example:
class Machine < ActiveRecord::Base
has_many :parts , inverse_of: :machine
accepts_nested_attributes_for :parts
end
class Part < ActiveRecord::Base
# name:string
belongs_to :machine
end
With these models, we can use simple_form to update the machine and its associated parts in a single form:
<%= simple_form_for #machine do |m| %>
<%= m.simple_fields_for :parts do |p| %>
<%= p.input :name %>
<% end %>
<% end %>
For 'new' action, build the nested model from the controller:
class MachinesController < ApplicationController
def new
#machine = Machine.new
#machine.parts.build
end
end
Source: https://github.com/plataformatec/simple_form/wiki/Nested-Models
Sounds to me like you're looking for accepts_nested_attributes_for
See:
https://apidock.com/rails/v3.2.3/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
https://rubyplus.com/articles/3681-Complex-Forms-in-Rails-5
Also, check out the cocoon gem.

Rails - Issue in has_many through with nested attributes

I am having issue with saving a has_many through relation with nested attributes. Due to complexity and requirment in the application the relation is as follows
Table structure,
agreements:
id
agreement_rooms:
id
agreement_id
room_id
details:
id
agreement_rooms_id
For more clarification, agreement_rooms table is related to many other models which will be having agreement_rooms_id in them.
Rails Associations,
class Agreement < ActiveRecord::Base
has_many :details,:through => :agreement_rooms
accepts_nested_attributes_for :details
end
class AgreementRoom < ActiveRecord::Base
has_many :details
end
class Detail < ActiveRecord::Base
belongs_to :agreement_room
accepts_nested_attributes_for :agreement_room
end
When i try to create a agreements record with details hash in it, i get the following error,
Agreement.last.details.create()
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'agreement#details' because the source reflection class 'Detail' is associated to 'agreementRoom' via :has_many.
I am not sure how to get this nested attributed working with has_many through relation for the above example. Please help out to figure the issue.
Thanks in advance.
#app/models/aggreement.rb
class Agreement < ActiveRecord::Base
has_many :agreement_rooms
accepts_nested_attributes_for :agreement_rooms
end
#app/models/agreement_room.rb
class AgreementRoom < ActiveRecord::Base
belongs_to :agreement
belongs_to :room
has_many :details
accepts_nested_attributes_for :details
end
#app/models/room.rb
class Room < ActiveRecord::Base
has_many :agreement_rooms
has_many :agreements, through: :agreement_rooms
end
#app/models/detail.rb
class Detail < ActiveRecord::Base
belongs_to :agreement_room
end
--
#app/controllers/agreements_controller.rb
class AgreementsController < ApplicationController
def new
#agreement = Agreement.new
#agreement.agreement_rooms.build.details.build
end
def create
#agreement = Agreement.new agreement_params
#agreement.save
end
private
def agreement_params
params.require(:agreement).permit(:agreement, :param, agreement_rooms_attributes: [ details_attributes: [:x] ])
end
end
#app/views/agreements/new.html.erb
<%= form_for #agreement do |f| %>
<%= f.fields_for :agreement_rooms do |ar| %>
<%= ar.fields_for :details do |d| %>
<%= d.text_field :x %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
you need to define both associations:
class Agreement < ActiveRecord::Base
has_and_belongs_to_many :agreement_rooms # or has_many if you prefer
has_many :details,:through => :agreement_rooms
accepts_nested_attributes_for :details
end
check the docs
As i said before the model association design we has was not proper and due to poor maintenance it has to be in the same way, atleast for now. So i had to write a dirty patch to fix it.
Its simply skipping nested attributes for this specific model alone, so it can be saved separately by passing the master record id to this record.
As its a dirty solution i'm not marking it as the answer. Just added it hoping someone can have a solution if needed.
Thanks for the help

How to get acts_as_list to work with a has_many through association?

I am having trouble figuring how to add drag and drop to a has_many through association?
I have a Model for Boards and each Board has many Lists.
Each List has many Cards and each Card has many Lists through a join model called ListCard
I am trying to add drag and drop to cards on each list. I have the front end working with jQuery UI i just don't know how to save the position through ajax to the position integer column in my ListCard Model.
I have looked at these below but i can't figure out how to setup the controller for a has_many through association?
Railscast for setting up drag and drop with acts_as_list
Using acts_as_list with has_many :through in rails
https://github.com/swanandp/acts_as_list/issues/95
https://github.com/swanandp/acts_as_list/issues/86
Models
class List < ActiveRecord::Base
belongs_to :user
belongs_to :board
has_many :list_cards, dependent: :destroy
has_many :cards, through: :cards
accepts_nested_attributes_for :list_cards
end
class ListCard < ActiveRecord::Base
belongs_to :list
belongs_to :card
acts_as_list :scope => :list_card
end
class Card < ActiveRecord::Base
belongs_to :user
has_many :list_cards
has_many :lists, through: :list_cards
accepts_nested_attributes_for :list_cards
end
Routes
resources :boards do
resources :lists do
collection { post: sort }
end
end
resources :cards
Lists Controller
def sort
# not sure what to put here
render nothing: true # this is a POST action, updates sent via AJAX
end
Board Show Page (for example www.example.com/board/1)
<% #lists.each do |list| %>
<ul id="cardwrap">
<%= list.title %>
<% list.cards.each do |card| %>
<%= content_tag_for :li, card do %>
<%= card.title %>
<% end %>
</ul>
<% end %>
jQuery UI Sortable Coffescript
jQuery ->
$('cardwrap').sortable
axis: 'y'
update ->

Nested models and forms with has one through relationship in Rails

I am trying to setup a form that takes in some song information. Right now the song title and the song artist.
Here is some of my code so far.
Song Model *EDIT
class Song < ActiveRecord::Base
has_one :song_artist_map
has_one :artist, :through => :song_artist_map
accepts_nested_attributes_for :artist
end
Artist Model
class Artist < ActiveRecord::Base
has_many :song_artist_maps
has_many :songs, :through => :song_artist_maps
end
SongArtistMap Model
class SongArtistMap < ActiveRecord::Base
belongs_to :song
belongs_to :artist
end
Songs Controller
def new
#song = Song.new
#song.artist.build
end
And inside my form I added this code
<% f.fields_for :artist do |a| %>
<li><%= a.label :name %></li>
<li><%= a.text_field :name %></li>
<% end %>
Right now nothing shows up in my form for artists.
So I need a way to be able to add an Artist from the form when a song is add and then make the mapping or just make the mapping if the artist already exists in my database.
I know I'm doing something very wrong here, can anyone please help? Thanks!
In your Song model, it should be has_many :artists?
If you do
"artist".pluralize
=> "artists"
That's what Rails uses for the auto-lookups of a few things, especially with has_many relationships, so it could be the source of your problems.
EDIT
In that case, the problem is in your controller. Instead of #song.artist.build, you should have #song.build_artist.
With a has_many relationship, Rails uses an object that allows you to instantiate new ones, with a has_one, it just returns it (which can be nil.).

accepts_nested_attributes_for - belongs_to, has_many, fields_for

Is there any possible way to use nested_attributes_for in the way show below?
Basically I want to create a person, one or more cars and add details to each car. This is just a mock up, not a very realistic example. I get snagged when trying to build the details for the car as it hasn't been created yet.
Models:
class Person < ActiveRecord::Base
has_many :cars
accepts_nested_attributes_for :car
end
class Car < ActiveRecord::Base
belongs_to :person
has_many :details
accepts_nested_attributes_for :details
end
class Detail < ActiveRecord::Base
belongs_to :car
end
Form:
form_for #person do |f|
#fields
f.fields_for :car do |car|
#fields
car.fields_for :details |detail|
=detail.text_field :content
end
end
end
Have a look at that http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast

Resources