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 %>
.....
Related
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.
Models: I have a Company model that has_many Tasks. Each Task has_one Employee.
Task model:
class Task < ActiveRecord::Base
belongs_to :company
has_one :employee, dependent: :destroy
accepts_nested_attributes_for :employee
validates_associated :noteholder
end
Employee model:
class Employee < ActiveRecord::Base
belongs_to :task
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
Form: I created a nested form for Task that accepts_nested_attributes_for the Employee model. The form has an Employee name field with an autocomplete function that loads all Company.employees.
_form.html.erb form:
<%= simple_form_for(#task) do |f| %>
<%= f.simple_fields_for :employee do |employee| %>
<%= employee.input :name, input_html: { data: { autocomplete_source: #employees.pluck(:name).to_json } } %>
<% end %>
<%= f.button :submit, 'Save' %>
<% end %>
This saves a new record for each employee, regardless of whether or not an employee with that name exists
Desired behaviour:
If the user fills in a new Employee the model should create a new record, whereas if it would provide an existing Employee name, it should not.
My attempt: I thought the find_or_create_by method would be useful here, but have had no success with implementing it.
Question: How do I correctly set up my model sothat the Employee model only saves a new employee if the employee name does not yet exist?
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.
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 %>
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.