Rails4 - Association has_many through - Reader_id - ruby-on-rails-4

I would like with Rails 4 to do a simple association beetween 3 models but i can't succeed to do that.
First model:Reader,Second model:Comments Third model : Books
The relation beetween the 3 models are:
class Book < ActiveRecord::Base
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :book
belongs_to :reader
class Reader < ActiveRecord::Base
has_many :comments
has_many :books, through: :comments
I have no problem for the relation Book has_many :comments but i have some problem with the relation Reader has_many :comments.
Field from CommentsTable : username, message and reader_id (for the has_many association).
Field from ReaderTable : name , email.
I have a file _form.html.erb in view:
<%= form_for [#book, #comment] do |f| %>
<%= f.hidden_field :book_id %>
<%= f.text_field :reader_id, :placeholder => "reader id" %>
<br />
<%= f.text_area :message %>
<br />
<%= f.submit %>
<% end %>
And an other file _comment.html.erb:
<% if comment.reader_id != nil %>
<li><strong><%= link_to comment.reader.name, comment.reader %></strong></li>
<p><%= comment.message %></p>
<% end %>
Like you see i have a form with reader_id and message. When the user will enter this 2 informations, it will appears the reader.name (thanks to reader_id) and the comment.message.
But i don't want that the user enter the integer "reader_id" (it has no sense) but directly the string "reader.name".
How can i do that, knowing that after i will do a link_to on the reader.name to comment.reader ?
Please help me.
Thank you.

I'm guessing the reader is the user. If it is, then you'll probably want to have a whole authentication system (maybe think about Devise) and just assign the comment to the current user within the controller. You'll also want to assign the book in the controller.
EDIT:
Devise will make available a current_user method in your controller. So your create method in your comments controller should look like this:
def create
comment = current_user.comments.new(comment_params)
end
You don't need to assign the reader.name to comments. That's what the association is for. You can do comment.reader.name, or better yet, use delegate (look that up) and do comment.reader_name.

Related

Need Simple Has_Many :through controller code

I'm learning to use Rails has_many :through association. Most of what I'm reading provides only how to set up the models, but not how to set up the controller actions. My app is very basic for the purposes of learning this topic. I have a form that lists some vertical industries. When a "vertical" is created, there is an option to select from a list of apps that apply to that vertical (check boxes). When the vertical gets created, the associations between the vertical and the selected apps should be established.
I have 3 models:
class App < ActiveRecord::Base
has_many :solutions
has_many :verticals, through: :solutions
end
class Vertical < ActiveRecord::Base
has_many :solutions
has_many :apps, through: :solutions
end
class Solution < ActiveRecord::Base
belongs_to :app
belongs_to :vertical
end
Here is my form:
<%= simple_form_for(#vertical) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
<%= f.association :apps, as: :check_boxes %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Here is my verticals_controller create action:
def create
#vertical = Vertical.new(vertical_params)
#solutions = #vertical.apps.build(params[:app])
<respond_to code omitted for brevity>
end
def vertical_params
params.require(:vertical).permit(:name, :description, apps_attributes: [ :name, :description, :developer, :mpp, :partner, :website, :app_id[] ])
end
I am able to create the associations from the rails console this way:
vertical = Vertical.first
app = App.first
vertical.apps << app
But I don't think this is the right way to do it in the controller, nor do I understand how to get the app params that were selected in the form. I'm looking for some basic, clean code examples to follow that adhere to Rails best practices. Also, if you can point me to any recent tutorials that address controller code would be great. Thx.
I was able to get the associations to be created by making the following changes:
In my Create action in my controller:
def create
#vertical = Vertical.new(vertical_params)
#solutions = #vertical.apps.build
<respond_to code omitted for brevity>
end
In my secure parameters I have the following:
def vertical_params
params.require(:vertical).permit(:name, :description, app_ids:[])
end
I am not certain this is done to Rails best practices or not.

building nested resources for a form

I have a class User which has sub classes IndividualUser and BusinessUser (through STI). IndividualUser and BusinessUser have a many-to-many association and are linked through a join table called Employees.
Also, a User can have an Account.
I'm trying to set up a nested form, and am struggling to create the required objects.
My models:
class User
has_one :account
end
class IndividualUser < User
has_many :business_users, :through => :employees
has_many :employees
end
class BusinessUser < User
has_many :individual_users, :through => :employees
has_many :employees
end
class Employee < ActiveRecord::Base
belongs_to :business_user
belongs_to :individual_user
end
class Account
belongs_to :user
end
Now, in my controller, I am able to create a new account for an individual user:
# GET /individual_users/new
def new
#individual_user = IndividualUser.new
#individual_user.build_account
end
This works fine.
However, when I try to create a business_user, then individual_users, and then an account for each individual_user:
# GET /business_users/new
def new
#business_user = BusinessUser.new
#business_user.employees.build()
#business_user.employees.each do |employee|
#individual_user = employee.build_individual_user
#individual_user.build_account
end
end
I get the following error:
undefined method `build_account' for nil:NilClass
It appears as though #individual_user is not being created.
I'm not sure why I'm getting this error. Any ideas?
What's really strange, is that if I instead move the code to the view, it appears to work. i.e. in my view:
<% #business_user.employees.each do |employee| %>
<% employee.individual_user.build_scoot_account %>
<% employee.individual_user.scoot_account.id = 2 %>
<%= employee.individual_user.scoot_account.id %>
<% end %>
This displays "2".

how to specify has_many through vs has_many

So I'm trying to figure out how to specify between calling all instances of an object based on the has_many and has_many :through associations. I have users, groups and members. the member model is a pairing of groups and users. the groups also have a user_id in that model. so this will make more sense after you see the code:
user model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :groups
has_many :groups, :through => :members
end
group model
class Group < ActiveRecord::Base
has_many :users, :through => :members
belongs_to :user
end
member model
class Member < ActiveRecord::Base
belongs_to :groups
belongs_to :users
end
Index View
<!--these are the groups I own (the group.user_id is my id) NEEDS EDITING-->
<% #groups.each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>
<!--I'm a member of these groups (connected through the member model) NEEDS EDITING'-->
<% current_user.groups.order(:created_at).each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>
I'm trying to figure out how to call the different types of associations users and groups in the Index View.
I want to get a list of all the groups that have me listed as admin (group.user_id) and then I want to get a separate list of all the groups that I'm a member of (through the member model).
I know I can query it with a find or where call. However, I was hoping there was a simple way like current_user.groups or something. anyway, thanks!
I would consider calling the 1:N relationship to groups another name. So you would end up with something like
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :owned_groups, class_name: 'Group'
has_many :groups, :through => :members
end
So the index view would look something like:
<% current_user.owned_groups.each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>
<% current_user.groups.order(:created_at).each do |group| %>
<%= link_to group.title, group_path(group) %>
<%= truncate group.desc %>
<% end %>

Rails - move logic from each loop in view to controller

I have two models - book.rb -
class Book < ActiveRecord::Base
belongs_to :user
end
and user.rb -
class User < ActiveRecord::Base
has_many :books
end
books_controller.rb includes
def index
#books = Book.all
end
And in /books/index.html.erb I have
<%= #books.each do |books| %>
<%= book.title %>
<%= User.find(book.user_id).name %>
.....
This all works fine - but should I have more of that logic in the controller? And if so, how do I pass the book id from the block into the controller in order to pass back the user name?
There's no need to do so. Since you already have the belongs_to relation you can write your view as:
<%= #books.each do |books %>
<%= book.title %>
<%= book.user.name %>
.....
An even faster way to do this is:
def index
#books = Book.includes(:user)
end
This will execute 2 SQL queries, one to get all the Books and one to get all associated Users. This is much cheaper in terms of number of queries, because if you have 10 Books you'd have to do 11 queries if you just use Book.all (one to get all the Books and one for each Book to get its User).
The view can stay the same:
<%= #books.each do |book| %>
<%= book.title %>
<%= book.user.name %>
.....

Ruby on rails 'has many' relation error: undefined method

I keep getting this error
undefined method `concerts' for
ActiveRecord::Relation::ActiveRecord_Relation_Artist:0xb50b691c>
When trying to do this:
<% #artists = Artist.where(name: "Test") %>
<% #concertTest = #artists.concerts %> #this line raises the error
Here are my models:
class Concert < ActiveRecord::Base
validates_presence_of :venue
validates_presence_of :date
has_many :reviews
belongs_to :artist
end
class Artist < ActiveRecord::Base
validates_presence_of :name
has_many :concerts
end
I can't seem to figure out what is causing this error, and why I can't reference the concerts of a particular artist this way. Any suggestions? Thanks
Please try like this:
<% #artists = Artist.where(name: "Test").first %>
<% #concertTest = #artists.concerts %>
Note:- where will return Active record relation array.
It works when I do .find_by_name instead of .where
<% #artists = Artist.find_by_name("Test") %>
<% #concertTest = #artists.concerts %>
I'm unsure for the reason of this but maybe someone can comment to clarify it.