I have a rails app. Homepage searches the movies database and displays the results in the results page for the search query.
Now, in the results page there are a list of movies (say 5) and the results page displays the attributes of the movies in the left side bar.
For example,
=================Movies results page================
Left-side-bar
Genres
Comedy(link_to)
Horror(link_to)
Languages
English(link_to)
German(link_to)
Results set
1 Anger management
2 American pie
3 Evil dead
4 Grudge
5 Du riescht so gut
=====================================================
Now, after the first search, my search controller has a #movieid array which has those 5 movieids(mentioned above). And the left-side-bar has the filters to filter this further.
Using the #movieid array i listed the movies in the results set.
If i click on the link "comedy", I want the results page to show only "1. Anger management" and "2. american pie".
If i click on the link "german". It should only display "5. Du riescht so gut". Thats it.
Right now,
Im confused of the following options,
1. Im thinking of passing the genre_id and #movieid array through the link_to as parameters.
2. Use cookie[:store] to save the #movieid array
3. Store the #movieids in database and retrieve.
I dont know how to handle this. Remember my #movieid array can contain 100000 ids as well.
Help me with the best practice and performance.
Thanks!
Edit:
Can i use Mysql Views to store the result set? problem is what happens if a 100000 users are searching at the same time, that many views will be created?, is it ok?. What is the good practice?
I think, I did great!
Answering to my question:
From first main search page, if I run a search query, on the movies table and get 300 records as a result from 5000 movies in the table then I dynamically create a view in the mysql database from rails method, AR::Base.connection.execute() which holds the movie_ids of the 300 searched movies.
Now moving on, in the second page if the user filters 300 results based on language or genre, I narrow down my search, doing a search on the created view and not the MOVIES table again. This is done by getting the language_id and genre_id from the url after passing it to a link_to helper.
I get a session_id from every browser and append an alphabet in front of that id which will be used as my view name.
If the user visits the home page or gets past the search page and finds the exact product, I will drop my view and i will keep a timeout to drop my view say 1 hour and every day I will drop the views created the day before.
I heard from Ryan that persisting an User search is a good practice. I was interested in that as well. Now I think I have implemented it. Please hit me with all the negatives in my approach, Im trying to run a company here.
Thanks!
Edit:
Forgot my gratitude to Ashitaka.
Of course I have added will_paginate gem. Its working alright.
TIP: In will_paginate, if you are listing out 83 movies with :per_page => 10, then you wont be able to display the total number of movies in the first page of 9 pages(10+10+....+3). If you wish to do it like Movies(83). Then, #movies.total_entries.to_s helps.
Thanks to you too!
Since you can have 100000 different movies, you need to implement pagination in your rails app. This is made easy with the will_paginate gem. Here's an example:
# movies_controller.rb
def index
#movies = Movie.search(params[:q]).order('name').paginate(:page => params[:page], :per_page => 15)
respond_to do |format|
format.html
end
end
And then in your view:
# index.html.erb
<%= form_tag movies_path, :method => 'get' do %>
<div>
<%= text_field_tag :q, params[:q] %>
<%= submit_tag "Search", :name => nil %>
</div>
<% end %>
<table>
<tr>
<th>Movies</th>
</tr>
<% #movies.each do |movie| %>
<tr>
<td><%= link_to movie.name, movie %></td>
</tr>
<% end %>
</table>
<%= will_paginate #movies %>
Searching and filtering is definitely a thorny issue. It's really hard to explain in detail such a complicated thing, so I will simply point out to the incredible Railscast that explains how to do what you want.
http://railscasts.com/episodes/240-search-sort-paginate-with-ajax
Related
i have two tables in Ruby on Rails, Movies and Directors. Movies has belongs_to association to director, and director has has_many association to movies. I can create both just fine, but when i try to edit a movie to include its director via a dropdown(using form.collection_select) and click update i get this message:
1 error prohibited this movie from being saved:
Director must exist
This is the code for the dropdown (its labeled in my native language, sorry about that)
"
<%= form.label :director_id, "Režisér", style: "display: block" %>
<%= form.collection_select(:director_id, Director.all, :id, :first_name, {:prompt => 'Vyberte režiséra'}, :selected => #movie.director_id ) %>
"
I'm a newbie to Ruby on Rails and my search on this issue has so far been unsuccessful and i have no idea how to ask Mr Google correctly
solved thanks to max's comment below, i simply needed to add the director_id reference parameter to my movies_controller.rb like so:
def movie_params
params.require(:movie).permit(:name, :release_date, :description, :director_id)
end
I have two models in Ruby on Rails, movies, and directors. A director has_many movies, and a movie belongs_to a director. I'm creating a mini-project, where if I view a Director on a page, I would like to have all movies associated with this particular author shown, along with links to these movies. I was able to make it with a movie successfully showing its Director with a link to his page, but I've been unable to accomplish this when viewing a director. All my research so far has led me to is
<%= director.movies.uniq.pluck(:name) %>
but that only shows the :name strings within an array format, which is not optimal. I would like to know if there is a way, in which I can access the id values stored in has_many. Because it has to work like an array, which saves the ids, so the program knows, which Movies belong to which Director. Please correct me if I'm wrong in this assumption, I'm still learning and will appreciate any input on this matter.
To accomplish this you will need to iterate over the array of Movie instances that belong to an instance of a Director. You can access this array (which is actually a Collection Proxy) with director.movies (assuming that director is an instance of Director). You will then be able to access the values of each instance.
Your code may look something like this:
<ul>
<% director.movies.each do |movie| %>
<li><%= link_to movie.name, movie_path(movie) %></li>
<% end %>
</ul>
I am struggling with a college project course, and I have been stuck with this error for weeks now, despite the suggestions provided by colleagues and tutors.
In my create method for a given table. I am trying to have the page containing the forms for new record entries redirect back to the index page after saving. Instead, I get redirected to this error instead, highlighting #courier=Courier.new(courier_new_path) with the error stating that it is not a hash, and would not redirect me back into index. However, when manually searching the index, I see that the data string would indeed get updated.
I have tried renaming the path label, but Rubymine prompt suggestions appear limited, and any further deviation would cause a different error
The following is the create method in the controller page (courier_controller.rb):
def create
#courier=Courier.new(params.require(:courier).permit(:courier_name,:courier_email))
#courier.save
redirect_to courier_path(#courier)
#courier=Courier.new(courier_new_path)
if #courier.save
redirect_to(:controller=>'courier' ,:action=>'index')
else
render('new')
end
end
Here is the code for the form page (courier/new/html.erb):
<h1>Courier#new</h1>
<p>Find me in app/views/courier/new.html.erb</p>
<%= form_with scope: :courier, :url => {:action => 'create'}, local: true do |f| %>
<p>
<%= f.label :courier_name %><br/>
<%= f.text_field :courier_name %>
</p>
<p>
<%= f.label :courier_email %><br/>
<%= f.text_field :courier_email %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
I have tried renaming #courier as Courier.new(courier_create_path) or Courier.nww(courier_path), I have tried looking for arguments using a hash form, but none seemed equivocal nor translatable as a solution to my problem.
Any suggestions would help. This is part of a college project, and as a multimedia student not as savvy in programming compared to fellow peers, I would highly appreciate suggestions that I can try out.
Many thanks in advance.
Has anyone tried to explain what is actually going on in your create method? I've added comments after each line to say what that line is doing.
def create
#courier=Courier.new(params.require(:courier).permit(:courier_name,:courier_email))
# use the params to build a new courier record (the permit part is usually in a separate method that other methods can access but it works this way)
#courier.save
# Save the new courier record to the database
redirect_to courier_path(#courier)
# Send the user back to the "show" page of the courier record (not index!)
#courier=Courier.new(courier_new_path)
# this makes no sense, you are trying to create a new courier object using a path method
# Basically you are saying: #courier = Courier.new('/courier/new')
if #courier.save
#you are trying to save this record that will fail because you can't create a courier by passing it a url
redirect_to(:controller=>'courier' ,:action=>'index')
#send the user to the index page of the courier views.
else
render('new')
#something went wrong so go back to the new courier form.
end
end
When your program gets to line 4 redirect_to courier_path(#courier) it is going to exit the create method and send the user to http://my_app:3000/couriers/1 where the number 1 would be the ID in the database of the record you just created. This would relate to the file in your app in app/views/couriers/show.html.erb. It sounds like you want to get to http://my_app:3000/couriers which presents the user with the file app/views/couriers/index.html.erb Not sure why you are doing anything after that line.
Also it is unclear what error you are getting. You need to look at your webserver console, the place where you run "rails s" that shows all the communications between the browser and your app. find the stack trace that starts with the actual error, and add that to your question above (don't paste it in a comment, it will be too long and impossible to read).
I think you just need:
def create
#courier=Courier.new(params.require(:courier).permit(:courier_name,:courier_email))
if #courier.save
redirect_to #courier #if you want to redirect to their 'show" page
else
render('new')
#something went wrong so go back to the new courier form.
end
end
What might be confusing to a new programmer is that Rails is doing so much "magic" behind the scenes it gets very confusing if you don't already know the underlying concepts happening behind the scenes. You are saying redirect_to(:controller=>'courier' ,:action=>'index') which specifically says "go to the index page of the couriers" but you can say redirect_to #courier and it will assume that you want to go to the page that shows the record of the courier you just created. If you really want to go the table that shows all of the couriers you would replace that with redirect_to :couriers The symbol :couriers tells it to go to the index method of the couriers controller.
I've been struggling for a while on this (been reading a lot of the ruby on rail guides to try and understand this), but I'm not sure how user inputs work.
I am trying to search for a restaurant in my database with a list of fields the user specifies (cuisine, zipcode, review score). I have created a html.erb page that has the options for all of these.
Here is my controller.
class WelcomeController < ApplicationController
def home
#my_search = Restaurant.joins(:inspection).where(cuisine: c, zipcode: z, totalscore: 1..h)
end
My models for restaurant and inspection also have relations between them (the foreign keys).
How would you go about letting the user give inputs for c (cuisine), z (zipcode) and 1..h (score range)?
I know that people have answered this question in the past, but I think I need a concrete example to actually understand how to do this. As in, what would you put in the html.erb code so that when an option is selected, that value is passed to the method?
Thank you
First you need to create a form in the view. The simplest way to do this is with form_tag:
<%= form_tag(home_path) do %>
<%= text_field_tag 'cuisine' %>
...other inputs
<% end %>
Next, make sure you have a route defined for your controller action in config/routes.rb
post 'home' => 'welcome#home'
Most likely your routes will look different but this is the bare minimum you need.
And in your controller you can access the submitted data using the params object
class WelcomeController < ApplicationController
def home
#restaurants = Restaurant.joins(:inspection).where(
cuisine: params[:cuisine],
# ...other params
)
end
end
Environment: Rails 3.0.6 and MySQL
I have the following two models:
class Employee < ActiveRecord::Base
belongs_to :user
belongs_to :manager
end
class Manager < ActiveRecord::Base
has_many :employees
end
I have a page which shows, for a given user, all the employees under him and all their direct managers. This page typically shows tens of records but for executive level users can potentially show up to 1000 records. (For various reasons we do not want to use pagination.)
The controller method which renders the page is as follows (shown in entirety)
def manage_reports
#tab = "home"
#sub = "manage"
add_breadcrumb I18n.t("home_menu.My Reports"), :manage_reports_path
#my_reports = current_user.employees.includes(:manager).order('manager.name)
respond_to do |format|
format.html
format.pdf { render :layout => false } if params[:format] == 'pdf'
prawnto :filename => "employee_list.pdf", :prawn => { }
end
end
The view is a simple table showing columns of information for the employee and a two columns of information for the manager (name and a tag field). Neither of the two models have more than ten columns. Both are indexed.
The load time for the page ranges from << 1 second to over 15 seconds in the case of ~600 reports. After many hours of playing around with indexes, joins etc. we took another look at the New Relic performance breakdown and discovered that the time spent in the controller represents 80% of the time, whereas the DB queries actually seem to execute fairly quickly.
I have a couple of questions:
1) What is actually happening in the controller that takes 80% of the time? Is it the loading of the reports into memory? Should we not expect to be able to load that many records on to a page any quicker?
2) The manager table has a 'bio' field with a paragraph of text. How does one exclude that column when including the manager association? The Rails 3.0.x documentation seems particularly patchy on this topic. Is it even necessary to exclude that column? If the bio column is not accessed in the view, is it ever loaded into memory? I've become confused as to whether including associations in Rails 3 are now eager- or lazy-loaded. Looking at the query in the console, there is only one SQL query loading all the columns for both employee and manager.
Update:
This is the breakdown of the transaction in NewRelic. (Apologies for the formatting)
Time Avg calls Avg time (ms)
* manage_employees 79 1 1,676
* Manager#find 11 81 227
* Tag#find 9 82 185
* Employee#find 1 2 19
* Database SQL - SELECT .3 1 5
* User#find .1 1 3
The view code is a simple table. Each row is as follows:
<td align="left"> <%= link_to emp.mgr.name, show_mgr_path(emp.mgr) %></td>
<td align="center"><%= emp.mgr.lang %></td>
<td align="center"><%= emp.num %></td>
<td align="center"><%= emp.status %></td>
<td align="center"><%= emp.test_score %></td>
<td align="center"><%= emp.test_date %></td>
<td align="center"><%= emp.serviceyrs %></td>
<td align="left"> <%= emp.tag_list.to_s %></td>
Reading your view, I think I've spotted the potential issue:
<td align="left"> <%= emp.tag_list.to_s %></td>
It seems that your partial is executing another query to fetch the tag list. Check your logs—if that's the cae, with 600 records it's a classic N+1 problem that explains your 15 second response time.