I am fairly new to RoR and trying to get a basic app to work - I have a 'books' model and a 'genre' model. I wish to create a page that randomly generates books of different genre's for a user to select.
I have created a 'random_book' controller, but am unsure on how to proceed with the random selection and display.
Any help/pointers would be appreciated.
Edit:
Here's the work I've been doing in the random_book model:
" load 'user.rb'
class random_book < ActiveRecord::Base
belongs_to :book
belongs_to :genre
def get_random_book
find(:all).sample(5)
end
"
Thank you.
Based on discussion
4 models
Book
Genre
UserBook
User
They will look something like this:
class User < ActiveRecord::Base
has_many :user_books
has_many :books, through: :user_books
end
class Genre < ActiveRecord::Base
has_many :books
def fetch_random_books(qty)
#you want to make sure you don't error out by requesting a sample of an empty list of books so check first. The qty argument lets you control the number of books for the search
unless self.books.empty?
self.books.limit(qty).sample
end
end
end
class Book < ActiveRecord::Base
belongs_to :genre
has_many :user_books
end
class UserBook < ActiveRecord::Base
belongs_to :book
end
I would most likely use a different route for the random book section, because it's not very url-friendly to say code-descriptive things like user_book.
There are 4 things to do
Create a new route to get a list of genre_ids that a user chooses.
Create an action that correlates to the route you created that renders a list of boos and adds those books to a users list
Create a form in a view (any view, like a sidebar in an existing books view, doesn't matter) this form will post the route and action you just made
Create a view to render the book list (the easy / DRY way is to add a few elements to the existing books index to let users know its a random generated list of books based on their genre pics)
Add the route
post "/random-books", to: "books#random_books", as: :random_books
Create the action in the books_controler
def random_books
if params[:genre_ids]
genres = Genre.where(id: params[:genre_ids])
#books = []
genres.each do |genre|
#books << genre.fetch_random_books(10)
end
else
#books = nil
end
respond_to do |format|
format.html { render action: :index }
end
end
Then create a form that makes a post request to the index action of the books_controller -- You can parse the form and update the UserBook model inside that action, and then display list of books all at the same time.
<%= form_tag(random_books_path, method: :post) do %>
<ul>
<% Genre.all.each do |genre| %>
<li>
<label class='genre-select'>
<%= check_box_tag 'genre_ids[]', genre.id -%>
<%= genre.name %>
</label>
</li>
<% end %>
</ul>
<%= submit_tag "Fetch Book List"%>
<% end %>
-- The last one I'm sure you can do, it returns a books object list so parse it however works best for you. In the controller action you can automatically add the ids for the books to the UserBook model by adding this inside the controller action:
#books.each{ |book| book.user_books.create(user: user)}
Related
I am working on my first rails app in which hotel owners can add types of rooms (premium, budget, single rooms, double rooms etc.) incl. the # of rooms of that type (room_count).
I am currently working with 3 tables,
(i) hotels table, where the user can create a new hotel,
(ii) accommodation_categories table, where the user can enter a new type of rooms incl. room_count &
(iii) an accommodations table, with the individual rooms per type.
My question is how to use the room_count input of the accommodation_categories table (obtained via a form) can be used to automatically create these room records in my accommodations table
e.g. how to translate the accommodation_category table input room_count into room_count*records of accommodations?
~ I am really sorry if this explanation is too elaborate, but I am not sure how else to explain it given that my technical vocabulary is rather limited ~
routes.rb
Rails.application.routes.draw do
resources :hotels do
resources :accommodation_categories do
resources :accommodations
end
end
hotel.rb
class Hotel < ApplicationRecord
has_many :accommodation_categories, dependent: :destroy
end
accommodation_category.rb
class AccommodationCategory < ApplicationRecord
belongs_to :hotel
has_many :accommodations, dependent: :destroy
end
accommodation.rb
class Accommodation < ApplicationRecord
belongs_to :accommodation_category
end
accommodation_categories_controller.rb
def new
#hotel = Hotel.find(params[:hotel_id])
#accommodation_category = AccommodationCategory.new
end
def create
#accommodation_category = AccommodationCategory.new(accommodation_category_params)
#hotel = Hotel.find(params[:hotel_id])
#accommodation_category.hotel = #hotel
#accommodation_category.save
redirect_to hotel_accommodation_categories_path
end
views/accommodation_categories/new.html.erb
<%= simple_form_for [#hotel, #accommodation_category] do |f|%>
<%= f.input :name %>
<%= f.input :room_count %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
I indeed used the cocoon gem with which you can create multiple nested objects via one form. It works perfectly.
To create multiple objects without details you can use controller lines such as:
#accommodation_category[:accommodation_count].times {#accommodation_category.accommodations.build}
I have a model named 'Assessment':
class Assessment < ApplicationRecord
has_many :assessment_students
has_many :students, through: :assessment_students
end
Join table is:
class AssessmentStudent < ApplicationRecord
belongs_to :student
belongs_to :assessment
end
There is another model:
class Classroom < ApplicationRecord
has_many :classroom_students
has_many :students, through: :classroom_students
has_many :assessments
end
In show,html.erb of classrooms, I have a dropdown which shows all assessments (generated from assessment table).
Code is:
<%= collection_select(:assessment :assessment_id, Assessment.all, :id, :assessment_name , :prompt => true) %>
Requirement of the project is: Based on the assessment chosen by the user in the show.html.erb page, we have to show all students details like name etc assigned to that particular assessment. I have stored this data in join table 'AssessmentStudent '. However, I am not sure how to pass id from the above collection_select to classroom controller. I have below code:
show.html.erb:
<%= collection_select(:assessment :assessment_id, Assessment.all, :id, :assessment_name , :prompt => true) %>
<div id="divResult">
<% #assessmentstudents1.each do |t| %>
<% t.assessment_students.each do |record| %>
<%= record.student_id %>
<% end %>
<% end %>
</div>
classroom controller:
def show
#assessmentstudents1 = Assessment.find(params[:assessment][:assessment_id]).preload(:assessment_students)
end
def classroom_params
params.require(:classroom).permit(:classroom_name, :classroom_year, :customer_id, :classroom_student, :student_ids => [])
params.require(:assessment).permit(:assessment_id)
end
I would first recommend to make a change to the controller structure of your application. Because the responsibility of the show action of your ClassroomsController should be to display the details of the classroom. When you want to show the details of an Assessment, that should be handled by an AssessmentsController.
First of all, I'm gonna assume that your Assessment model has a belongs_to :classroom association. Then I would suggest creating the following structure.
config/routes.rb
resources :classrooms
resources :assessments
app/views/classrooms/show.html.erb
<% #classroom.assessments.each do |assessment| %>
<%= link_to assessment_name, assessment_path(assessment) %>
<% end %>
app/controllers/assessments_controller.rb
class AssessmentsController < ApplicationController
def show
#assessment = Assessment.find(:id)
end
end
app/views/assessments/show.html.erb
<h1>Students for <%= #assessment.assessment_name %></h1>
<% #assessment.students.each do |student| %>
<p><%= student.student_name %></p>
<% end %>
<h2>In classroom:</h2>
<p><%= #assessment.classroom.classroom_name %></p>
So to explain what is happening here, we have configured the routes to allow the server to respond to the url /assessments/:id which will lead to the AssessmentsController#show action being called.
That action is being called when the user clicks on any of the links that we have setup in the classrooms/show template. Note that I used individual links for now instead of a select dropdown, because it is easier to setup and understand how it works. And like the previous answer suggested, using a select tag requires a little bit of JavaScript to get working.
And lastly, when the assessments/show template is being rendered, it will list out all of the students related to that particular assignment. And I also included which classroom it is assigned to (if my assumption of belongs_to was correct).
On a side note, if your question tag of ruby-on-rails-3 is correct, then the usage of params.permit and params.require is invalid, because that is something that was introduced in Rails 4 (unless I'm mistaken). And in any case, you only have to use permit when database updates take place, which means the create and update actions, not index, show, etc, because it is a way of restricting which changes are allowed.
Tag select does nothing by itself. So there are two possible options.
The first is wrapping select in form tag then submitting the form will lead to request. The second is writing a handler using JavaScript which will listen to select changes.
I am trying to develop ratings for my application, where a User is able to set a specific rating for a comment. I have followed the following tutorial in order to do so.
Here are my associations:
class Rating < ActiveRecord::Base
belongs_to :comment
belongs_to :user
end
class Comment < ActiveRecord::Base
has_many :ratings
belongs_to :user
end
class User < ActiveRecord::Base
has_many :ratings
has_many :comments
end
My problem here is that, in the index action of my comments controller, I need to include the rating that the user has done for that comment. In the tutorial is just shown how to select a particular rating by doing this:
#rating = Rating.where(comment_id: #comment.id, user_id: #current_user.id).first
unless #rating
#rating = Rating.create(comment_id: #comment.id, user_id: #current_user.id, score: 0)
end
However, I will have several ratings, because in my controller I have:
def index
#comments = #page.comments #Here each comment should have the associated rating for the current_user, or a newly created rating if it does not exist.
end
You want to find the comment's rating where the rating's user_id matches the current user.
<%= comment.ratings.where(user_id: current_user.id).first %>
However this sort of logic is pretty cumbersome in the views, a better strategy would be to define a scope in Rating that returns all ratings made by a specific user.
class Rating
scope :by_user, lambda { |user| where(user_id: user.id) }
end
class Comment
# this will return either the rating created by the given user, or nil
def rating_by_user(user)
ratings.by_user(user).first
end
end
Now in your view, you have access to the rating for the comment created by the current user:
<% #comments.each do |comment| %>
<%= comment.rating_by_user(current_user) %>
<% end %>
If you want to eager load all ratings in your index page, you can do the following:
def index
#comments = page.comments.includes(:ratings)
end
You can then find the correct rating with the following:
<% #comments.each do |comment| %>
<%= comment.ratings.find { |r| r.user_id == current_user.id } %>
<% end %>
This would return the correct rating without generating any extra SQL queries, at the expense of loading every associated rating for each comment.
I'm not aware of a way in ActiveRecord to eager load a subset of a has_many relationship. See this related StackOverflow question, as well as this blog post that contains more information about eager loading.
I am trying to build a form inside the show action of a controller. Here is my setup.
Category Model
class Category < ActiveRecord::Base
# Associations
has_many :feeds
has_many :subscriptions
# Nested attributes
accepts_nested_attributes_for :subscriptions
end
Subscription Model
class Subscription < ActiveRecord::Base
# Associations
belongs_to :profile
belongs_to :category
belongs_to :feed
end
Using a Subscription namespace:
# Subscriptions
namespace :subscriptions do
resources :categories
end
Inside the show action for a category (http://URL.com/subscriptions/categories/1), I want to build the subscriptions model form and list the available feeds for that category. I want the form to submit the checked off feeds to the Subscription model.
CategoriesController
def show
#feeds = Feed.where("category_id = ?", #category)
end
Trying to build the form in the Show action of the Categories view:
<%= simple_form_for [:subscriptions, #category] do |f| %>
<%= f.association :feeds, collection: #feeds, value_method: :id, as: :check_boxes %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Right now it is rendering the appropriate information, but it is rendering as an edit form, not a create form. How do I fix this?
What else do I need to add to my routes / controller to actually get the information to save to the Subscription model?
Thanks.
first thing i am not sure where you initialize the #category object in show action and in view. if you want to render a new page than it should be map to new action of the controller and you path with will be
http://URL.com/subscriptions/categories/new
the path you are using
http://URL.com/subscriptions/categories/1
will always map to the show action according to rails convention though you can override that for you accordingly but that is not a good practice. also in new action initialize the instance by
#category = Category.new
now on submit it will automatically goes to the create action. if your #category is already persisted in the database for example
#category = Category.find(1)
now if this object goes to the simple_form_for it will send the data to update action .
I'm trying to build a form_for to create a join model between two other models. I have a Book model and User model, with another called Reads that is my join. Here is how I've set up the associations:
class User < ActiveRecord::Base
has_many :reads
has_many :books, :through => :reads
end
class Book < ActiveRecord::Base
has_many :reads
has_many :users, :through => :reads
end
class Read < ActiveRecord::Base
belongs_to :book
belongs_to :user
end
I've looked at the docs for form_for and watched the railscast episode on many-to-many associations, but I can't figure out why I'm getting the error when I try to render the Book#show view where I've put the form:
First argument in form cannot contain nil or be empty
Here is my form in app/views/books/show.html.erb:
<%= form_for(#read) do |f| %>
<%= f.hidden_field :book_id, value: #book.id %>
<%= button_to 'Add to Reads', {controller: 'reads', action: 'create'}, {class: 'btn'} %>
<% end %>
I think part of the problem is that I am trying to create a 'Reads' object from the Books model, but I'm not sure what I am doing wrong. I need the 'Add to Reads' button on the Book's page so that a user can select that particular book to add to their 'reads.' I'm also adding the current_user id in the controller, rather than in the view. Here is my create action from the Reads controller if that helps...
def create
#read = Read.new(read_params)
#read.user_id = current_user.id
#read.save
if #read.save
# do this
else
# do that
end
end
And I'm using strong params...
def read_params
params.require(:read).permit(:user_id, :book_id)
end
Thanks for any help.
First argument in form cannot contain nil or be empty
This means that #read in your form is nil. Since you are in the show action of your Books controller, you have to define this variable in the books controller.
def show
#read = Read.new
...
end