Nested models and forms with has one through relationship in Rails - ruby-on-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.).

Related

How to access the "Grandchild" of 2 has_many associations in rails without adding another association?

I'm having trouble figuring out how to access the "grandchild" of an object in rails. Here's my example:
I have a product model that can have options (such as "size" or "color") and each option can have choices (such as "small, medium, large" or "red, green, blue").
class Product < ApplicationRecord
has_many :options
end
class Option < ApplicationRecord
belongs_to :product
has_many :choices
end
class Choice < ApplicationRecord
belongs_to :option
end
I'd like to access the "choices" from the Product controller, but I can't figure out how to chain them together.
For instance, Product.first.options returns all of the associated options that belong to the Product, but I'd like to do something like Product.first.options.choices.
That returns NoMethodError (undefined method 'choices')
Is this even possible with active record or do I need to create another association that connects Product and Choice.
#engineersmnky came through with the comment that solved this.
Here's what I added to get this working.
has_many :choices, through: :options was added to the Product model:
class Product < ApplicationRecord
has_many :options
has_many :choices, through: :options
end
And then here was the simple_form code that gave me the user experience I was looking for in the product#edit view:
<% #product.options.each do |option| %>
<div class='form-row'>
<div class='col'>
<%= f.association :choices,
collection: option.choices,
as: :radio_buttons,
label: option.name %>
</div><!-- /.col -->
</div><!-- /.row -->
<% end %>

Modelling nested models in 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

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

Rails model design

I want to create a model similar to reddit where users can upvote or downvote a link or article. I am having trouble wrapping my head around this: how do I made my models so that a user can like vote up or down a link only once and be able to change their mine (switch to a downvote) but never be able to vote multiple times no matter how much time has passed/logging out does not matter
has_many :through
You'd create something like this:
#app/models/post.rb
Class Post < ActiveRecord::Base
has_many :votes do
def user(user)
find_by user_id: user.id
end
end
has_many :voters, through: votes, class_name: "User", foreign_key: "user_id"
end
#app/models/vote.rb
Class Vote < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :votes
has_many :posts, through: :votes
end
Standard has_many :through relationship - giving you the ability to use a join model to associate two or more other models together:
--
User
This would allow you to call the following:
#app/views/posts/show.html.erb
<% if #post.votes.user(current_user).present? %>
<% link_path = #votes.votes.user(current_user).value == "up" ? vote_down_path : vote_up_path %>
<%= link_to "Vote", link_path %>
<% else %>
# empty vote link
<% end %>
The tricky bit is to associate a single vote with a single user - hence why I included an ActiveRecord Association Extension for your votes association
You can use relationships to capture this...
An Article has many votes
A Vote belongs to a User
A Vote has one article
An article has many voters (users) through Votes but they must be unique. (validation rule)

Proper way to set up models and relationships for a rails app, has_many through

I am having a hard time getting my head around the best way to set up my models and relationships in rails as I'm need to rails, mvc, and active record conventions.
Consider three tables:
users
rails generate scaffold User username:string email:string password:string
games
rails generate scaffold Game user_id:integer name:string description:string type:string
game_rosters
rails generate scaffold GameRoster user_id:integer game_id:integer level:integer status:string
Users can create games (this is the user_id field in the games table for the "owner"). The creator may or may not be in the actual game (optional record for the creator in the game_rosters table).
Each game has many users which are apart of the game (located in game_rosters).
So I am thinking I should have two relationships for the user - has_many: games for the games they have created, and has_many: games, through: game_rosters for the games that user is a part of..
class User < ActiveRecord::Base
has_many :game_rosters
has_many :games, through: :game_rosters
has_many :games
end
class Game < ActiveRecord::Base
has_many :game_rosters
has_many :users, through: :game_rosters
end
class GameRoster < ActiveRecord::Base
belongs_to :user
belongs_to :game
end
But I'm not sure if this is the right set up, and I can't get it working properly.. I have the above set up, and the following for trying to print results:
<p>
<strong>My Games:</strong>
<% #user.games.each do |game| %>
<br><%= game.name %>
<% end %>
</p>
<p>
<strong>Participants:</strong>
<% #game.users do |user| %>
<br><%= game.user.name %>
<% end %>
</p>
I can print the "My Games" from the has_many: games relationship, but can't print the "Participants" from the has_many :through relationship.
Any direction would be appreciated, thanks.
Also want to add that you should not have the same name games for different association in the User model. As last one is overwriting the first. Smthing like this should work.
class User < ActiveRecord::Base
has_many :owned_games, :foreign_key => "user_id", :class_name => "Game"
has_many :game_rosters
has_many :games, through: :game_rosters
end
class Game < ActiveRecord::Base
belongs_to :owner, :foreign_key => "user_id", :class_name => "Game"
has_many :game_rosters
has_many :users, through: :game_rosters
end
class GameRoster < ActiveRecord::Base
belongs_to :user
belongs_to :game
end
Accessing the associations for a user
# All the games a user is involved in
user.games
# All the games a user owns
user.owned_games
Accessing associations for a game
#All the users involved in the game
game.users
# Owner of the game
game.owner
Try:
<p>
<strong>Participants:</strong>
<% #game.users.each do |user| %>
<br><%= user.name %>
<% end %>
</p>

Resources