I've ran into a bit of a problem. I watched Railscasts 370 and discovered a cool new gem called ransack
My problem is it's not doing what I want it to do, which is to filter a table based on a search
Here is my controller
def index
#search = Component.ransack(params[:q])
#components = #search.result
end
And my view
<%= search_form_for #search do |f| %>
<%= f.label :name_cont %>
<%= f.search_field :name_cont %>
<%= f.submit "Search" %>
<% end %>
When I typed in "Gigabyte" and clicked search, I saw the following in the URL
http://localhost:3000/components?utf8=%E2%9C%93&q%5Bname_cont%5D=Gigabyte&commit=Search
I know that this is URL syntax for a query. And it refreshes the page as well. Problem is, I see the following three results
Intel Core i5-4460 3.2GHz Quad-Core
Gigabyte GA-Z97X-SLI ATX LGA1150
Team Elite Plus 8GB (2 x 4GB) DDR3-1600 Memory
These were the names of the 3 items in my table. I expected only one result to return from this query.
If it helps, I've added part of my Development Logs
Processing by ComponentsController#index as HTML
Paramaters: {"utf8=>"checkmark", "q" => {"name_cont"=>"Gigabyte"}, "commit"=>"Search"
It shows up in the parameters too, so I'm completely mystified as to why my code's not working. Any help will be greatly appreciated.
EDIT
I think I found the problem but I'm not sure
I have this inside <tbody>
<tbody>
<% Component.all.each do |c| %>
<tr>
<td> <strong><%= c.part %></strong> </td>
<td> <a id="links" href="<%=c.link%>"> <%= c.name %> </a></td>
<td> $<%= c.price %> </td>
</tr>
<% end %>
</tbody>
EDIT
Looks like that was indeed my mistake. Changing Component.all to #components.each fixed it.
Have you tried to change this
#search = Component.ransack(params[:q])
for this?:
#search = Component.search(params[:q])
I've been using the gem recently and probably the gem changed a little with regard the railscast
Related
Hi in my apps I have a scaffold, were users can select different type of paper and add a paper weight for each type. Then I calculate the running total of paper_weight for each user in the ApplicationController.
I want to be more specific and show the users the average weight of each paper_type they are using. I´m a bit lost since I ´m rather new to rails, can anyone here help me??
thanks in advance
below are my views.
in paper/new.html.erb
<div class="control-group">
<%= f.label :paper_type, class: 'control-label' %><br>
<div class="controls">
<%= f.select(:paper_type, options_for_select([['Skrifstofupappír', 'Skrifstofupappír'], ['Dagblaðapappír', 'Dagblaðapappír'], ['Glans_&_tímaritapappír', 'Glans_&_tímaritapappír'], ['Annað', 'Annað']])) %>
</div>
</div>
<div class="control-group">
<%= f.label :paper_weight, class: 'control-label' %><br>
<div class="controls">
<%= f.number_field :paper_weight %>
</div>
</div>
in ApplicationController.rb
#paper_weight_per_capita = current_user.papers.sum(:paper_weight) / (current_user.profile.staff)
First a tweak.
In your Paper class is where you want to put the array of acceptable values for the paper type. That way you can validate input from perhaps more than just this form in the future. It also allows you to test that code plus putting an array right in the view like that is just not the best practice.
class Paper
PAPER_TYPES = ['Skrifstofupappír', 'Dagblaðapappír', 'Glans_&_tímaritapappír', 'Annað']
...
Then in your view, change to this.
<%= f.select(:paper_type, options_for_select(Paper::PAPER_TYPES)) %>
That doesn't answer your question, just a tweak to make your code cleaner and testable.
Then in your User model
def average_paper_types
self.papers.group(:paper_type).sum(:paper_weight)
end
And in your partial:
<table>
<thead>
<tr>
<% current_user.average_paper_types.each do |pair| %>
<th><%= pair[0] %></th>
<% end %>
</tr>
</thead>
<tbody>
<tr>
<% current_user.average_paper_types.each do |pair| %>
<td><%= pair[1] %></td>
<% end %>
</tr>
</tbody>
</table>
Not positive I understand correctly, but I think what you're trying to do (based on comments) is display the averages of each individual paper type on the index page.
As you're iterating through the different types of paper that belong to the user, you could call a method that performs that calculation. Inside that method you might do something like this:
def paper_weight_by_type(user_id, paper_type)
u = User.find(user_id)
weight_by_type = Paper.where(user_id: user_id, paper_type: paper_type).pluck(:paper_weight)
# then return this
weight_by_type.inject(:+)
end
this is a crude version. This could be an instance method on either user or paper in which case you wouldn't need both arguments. (ie current_user.paper_weight_by_type(paper_type) ...could also make it a class method and just pass both arguments by adding self. in front of def) Also several ways you could probably cut down on the number of queries being run...but this would get the job done. You'd want to call it from the paper loop on the index page however you ended up doing it. I would get it working one way and then improve it from there. Hope this helps.
This is how my CategoriesController looks like:
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def show
#category = Category.find(params[:id])
end
end
inside my show.html.erb I wrote this to display the category name:
<h2><%= #category.name %></h2>
I also have a PagesController which I made relations with the Category
I have few Pages assigned to Category (for example category_id: 1)
When I click on the category link from my homepage:
<%= link_to "category", category_path(cat) %>
It goes to the show page which is great
How can I display on the show.html.erb all the Pages that belongs to this category that I've clicked on?
You should call the pages assosication with the category model as,
<%= #category.name %>
<h1> PAGES </h1>
<table>
<tr>
<% #category.pages.each do |page| %>
<td> <%= page.title %> </td>
<td> <%= page.content %> </td>
<% end %>
</tr>
</table>
This will do for you.
You should use includes for enhanced performance of your application. It will return all expected records from pages also and that's without firing database query again. #category.pages in ERB will not make database query. Hence, you will have efficient code.
In your show action:
def show
#category = Category.find(params[:id]).includes(:pages)
end
In show.html.erb:
<%= "Category: #{#category.name}" %>
<h1> POSTS </h1>
<table>
<tr>
<% #category.pages.each do |page| %>
<td> <%= page.field1 %> </td>
<td> <%= page.field2 %> </td>
<% end %>
</tr>
</table>
Rich Peck edit
Normally, ActiveRecord does something called lazy loading, which means that it will only execute a DB query when it needs to. This is normally highly efficient; unfortunately causes a problem when you call associative data.
When calling #category.pages, you get the n+1 issue, which means that each time you call the Category.find query, an extra query will be used to load the pages:
The above code will execute just 2 queries, as opposed to 11 queries in the previous case:
SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
So using .includes essentially appends all the pages to your #category, making it much more efficient than letting it load lazily.
Well mostly it depends on how you made your relationships between category and page. With what i can see from your code, you can do something like this.
#pages = Page.where(:category_id => 1)
Update
As per to your relationships
#pages = #category.pages
Looking for ways to improve the particularly bad performance I'm getting from my rails application. Here's the code from the page in question:
notifications_controller.rb
class NotificationsController < ApplicationController
def index
#questions = Question.all.order(:updated_at => :desc)
#users = User.all
#answers = Answer.all.order(:updated_at => :desc)
end
end
and here's the corresponding view. I know it's ugly but it's working.
<div>
<% if current_user %>
<div class="notifications-added col-md-8">
<h4 class="col-md-offset-2">Approvals & Answers</h4>
<span class="text-center">
<% current_user.questions.order(id: :desc).each do |question| %>
<% if question.approved == true %>
Your question, <%= link_to "#{question.title}", question_path(question) %>, has been <span class="notifications">approved.</span><br>
<% end %>
<% question.answers.each do |answer| %>
<%= answer.user.name %> <span class="notifications">added an answer</span> to your question, <%= link_to "#{question.title}", question_path(question) %>.<br>
<% end %>
<% end %>
</span>
</div>
<div class="notifications-voted col-md-4">
<h4 class="text-center">Votes</h4><span></span>
<% current_user.answers.order(updated_at: :desc).each do |answer| %>
<% #users.each do |user| %>
<% if user.voted_up_on? answer %>
<%= user.name %> <span class="notifications">upvoted</span> your answer to <%= link_to "#{Question.find(answer.question_id).title}", question_path(answer.question_id) %>.<br>
<% elsif user.voted_down_on? answer %>
<%= user.name %> <span class="notifications">downvoted</span> your answer to <%= link_to "#{Question.find(answer.question_id).title}", question_path(answer.question_id) %>. <br>
<% end %>
<% end %>
<% end %>
</div>
<% end %>
</div>
I think I'm just sorting too much. The page is taking a long time to load. What's the low hanging fruit for improving my performance? Any thoughts? Thanks in advance.
The first , you should move logic code in your view into model.
The second , use pluck method instead array active record objects. Array string are lightweight than array of active record. Right ?
The third , use slim template engine instead erb template engine.
The fourth , cache db.
The fiveth, use google PageSpeed plugin for google chrome to analytic what's slow.
Peter answer is correct, and I will add two things :
Eager loading
Doing this
current_user.questions.each do |question|
question.answers.each do |answer|
...
end
end
will generate a query for each question. Rails will load all the questions, then for each question load its associated answers (1 query + 1 query for any question).
If you replace the first line by
current_user.questions.include(:answer).each do |question|
Rails will load all the questions, then all the associated answers (2 queries).
Look at the log
Every information for any bad performance should be visible on logs in development mode. For example, if voted_up need to load any other models than answer, your query number will be too big.
I have this form:
<tr>
<% item.inventory_items.each do |product| %>
<td>
<%= form_tag("/list_items", method: "post") do %>
<%= hidden_field_tag(:item_id, item.id) %>
<%= hidden_field_tag(:inventory_item_id, product.id) %>
<%= hidden_field_tag(:shopping_list_id, ShoppingList.first.id) %>
<%= submit_tag("#{product.price}", class: "btn btn-primary") %>
<% end %>
</td>
<% end %>
</tr>
Currently the hidden_field for shopping_list_id is being set, as you can see, by ShoppingList.first.id. That was really just a placeholder to make sure my form was working. I want the :user to be able to select which of their lists to submit this list_item to. I'm unsure of the best way to do that. Ideally I'd like to be able to have them hover over the product price and have a drop down of their lists to select from, whereby the form would get the shopping_list_id from. How can I best accomplish something like this? I'm using Twitter Bootstrap. Thanks in advance.
Something like this should get you started:
<%= select_tag(#user, :shopping_list_id, options_for_select(#user.lists)) %>
I'm working with rails and I have gotten a bunch of posts set up. However, I am trying to put a linebreak or some whitespace in between each post. I've got all the articles to display using <%= render #articles %> onto the index page but I am unsure how to proceed to add whitespace. Any help would be greatly appreciated! Thanks!
You should loop trough the articles and add some custom HTML CSS to them.
<% #articles.each do |article| %>
<%= article %>
<%= link_to article.number, article, {:class => "artikel"} %>
<br />
<% end %>
Or add them to a proper html tag like p
<% #articles.each do |article| %>
<p><%= article %></p>
<% end %>
FYI you can also use a second partial that will be rendered by Rails between each instance of your main partial, this can be interesting depending of the complexity of your layout, but probably overkill for just a <br /> ;-)
<%= render #articles, :spacer_template => "article_spacer" %>