I have been building a practice application for rails. It's an simple blog application with only articles model, and tagging system which is done using Postgresql Arrays. Everything is fine except, I couldn't get the pages for specific tags to working.
The Activerecord query to fetch the posts matching the given tags is working properly in rails console, but the same fails when tried with View. I have verified and the params are passing through, please help.. I don't receive any error messages, from what I could understand the rails passes the ActiveRecord::Relation::ActiveRecord_Relation_Article as a result of query to the view, but the view file fails to display it list by list...
Below are the relevant files,
routes.rb
get 'tags/:tag', to: 'articles#tagview', as: :tag
articles_controller.rb
def tagview
#articles = Article.where("'params[:tag]' = ANY (tags)")
end
tagview.html.erb
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.content %></td>
<td><%= article.tags.join(" ") %></td>
</tr>
<% end %>
The resulting page is just blank. whereas the above query works in Rails console.
The problem
Your original code sends a literal 'params[:tag]' string to the database, and it would have no idea what to do with it. The resulting query looks something like:
SELECT * FROM articles WHERE ('params[:tag]' = ANY (tags));
This only returns articles that has actually been tagged with the literal "params[:tag]" string.
The solution
I guess you intend for params[:tag] to be replaced with the actual tag value coming in from params. To do this, you'd need to do something like:
Article.where(['? = ANY (tags)', params[:tag]])
which is going to result in something like this being sent to the database (fx when params[:tag] is set to "foo"):
SELECT * FROM articles WHERE ('foo' = ANY (tags));
which is much more likely to be what you want.
Further reading
If you want to read more about how to construct queries with Active Record, the section about Conditions in the Active Record Query Interface guide is a good place to start.
You probably won't find anything in the official documentation that talks about using PostgreSQL arrays as that is not a general database feature, though.
Related
I am trying to get the records to order by :id in the view. I have records ordered by :id in the controller, like so
#bands = Band.where(available: true).order(:id)
Everything appears normal when the app starts, but as records are updated, the ordering behaviour goes wonky. Most often, recently edited records move to the very end, but not always. This shouldn't happen since the records are ordered by :id which shouldn't be changing
For context, in the view is something like
<% #bands.each do |b| %>
<%= b.name %>
<% end %>
Also note, in the rails console, both of these return the correctly ordered results (which makes this problem even stranger):
ActiveRecord::Base.connection.execute("SELECT * FROM bands WHERE available = 'true' ORDER BY id")
and
Band.where(available: true).order(:id)
Also note, when I load the index view, and observe the rails server, I can see that the results are not in the correct order.
I can also see the sql query that has been executed, and it ignores the order part, it simply doesn't have any mention of order in the query
Actually, I don't think it is possible.
But, try this:
#bands = Band.where(available: true).order(:id).to_a
I'm trying to find the last Econ_Result that belongs to a Econ_Report. I want to display the last record of the Econ_Result (ordered by "release_date") for each Econ_Report on the index view. In the controller I tried to take the list of all reports and find the last result using the following:
#econ_reports = EconReport.all
if #econ_reports.econ_results.size >= 1
#last_result = #econ_report.econ_results.last.release_date
end
econ_report.econ_results.size works on the index view when I place it in for each loop. When I try to call the value of the last record I run into issues with the fact that some reports don't yet have results (a temporary issue) so I threw in the if then check in the controller which is currently failing.
Thanks in advance for the rookie help.
Since #econ_reports is a collection of EconReport objects, you can't call an instance method like .econ_results on it. Instead, you can only call it on instances within the collection:
#econ_reports.each do |econ_report|
if econ_report.econ_results.any?
last_result = econ_report.econ_results.last
end
end
However, this can be terribly inefficient for a large collection of #econ_reports: both lines with econ_report.econ_results will query the database separately, meaning that you'll query the database independently for each econ_report in the collection. This is known as the N+1 query problem.
Luckily for you, as discussed in the link, Rails has a built-in solution to optimize this code so you'll only query the database once:
<% #econ_reports.includes(:econ_results).each do |econ_report| %>
<% if econ_report.econ_results.any? %>
<% last_result = econ_report.econ_results.last %>
# do something to display last_result
<% end %>
<% end %>
If you just want the release date you might try:
#last_result = #econ_report.econ_results.order('release_date DESC').limit(1).pluck(:release_date).first
It's worth noting that a Ruby if statement generally looks like:
if condition
end
The then is almost always omitted even though it is allowed.
I have a Rails 4.0 APP using PostgreSQL on Heroku. I am trying to display a table that is my XLog or transaction log, showing the last five entries in reverse order by updated_at timestamp. This works correctly on my local system. When I push it to Heroku, it sorts incorrectly.
I have checked the Heroku database definitions and the column is correctly listed as a timestamp. I have cloned the Heroku code back to my machine and verified that it is the same as what I pushed. At this point, I don't know why it doesn't work on Heroku when it works locally. And advice would be appreciated.
FWIW, the remote database and local database do not have the same data.
The code is: (Last line of log_sort was added to act as a breakpoint that would still pass the correct result.)
def self.last_objects object, count
logs = XLog.where(object: object).last(count)
log_sort = logs.sort_by{|log| log.updated_at}.reverse
log_sort
end
During execution to the breakpoint, you can see the variables passed:
This is the local result with the correct sort:
This is the Heroku result with the incorrect sort:
This is the Heroku PostgreSQL table definition for updated_at:
EDIT: View:
<% xlog = xlog_last(car.stock_number, 5) %>
...
<% xlog.each do |log| %>
<tr>
<td><%= log.associate %></td>
<td><%= log.proxy %></td>
<td><%= log.action %></td>
<td><%= log.status %></td>
<td><%= log.message %></td>
<td><%= log.value %></td>
<td><%= log.updated_at %></td>
</tr>
<% end %>
Helper:
def xlog_last(object, count)
XLog.last_objects object, count
end
EDIT:
I modified the sort_by to use an order method as follows. The results did not change at all. The same sorting error occurred and the exact same data was displayed in the exact same way:
New code:
def self.last_objects object, count
logs = XLog.where(object: object).order(updated_at: :desc).first(count)
end
I believe you need to use order to influence the actual sql query. Right now you are using sort_by which is sorting the data after you read in from the database. The order in the db is based on how it was inserted and could be different from heroku and your local system. When you export from heroku, and then import it, the ordering of the tables probably changes too.
A gem was incorrectly sorting the view. Correcting that issue fixed the problem. Still unsure why it didn't show in test except possibly for the data differences.
I'm working with a simple rails app locally for my own learning.
In my view, I have the following code;
Reviews (<%= #reviews.count %>)
<% if #reviews.any? %>
<% #reviews.each do |review| %>
This is one review <br />
<% end %>
<% end %>
Despite this seeming quite simple, the output is as follows;
Reviews (2)
This is one review
This is one review
This is one review
This seems to contradict itself on what should be a very simple pair of operations on the same array.
For reference, the array is built in the controller, using the following code;
class PlacesController < ApplicationController
def show
#place = Place.find(params[:id])
#reviews = #place.reviews
#title = #place.name
end
end
What might be the problem?
I would venture to answer: try to change code inside controller to:
#reviews = #place.reviews.uniq
And check the result.
Seems to fix it. Any idea why? – Matthew Higgins
SQL Inner joins took a place here :)
They produces a duplicate entities. You can ensure, by modifying your controller:
def show
#place = Place.find(params[:id])
sql_query = #place.reviews.to_sql
render text: sql_query
end
You'll see sql query in browser. Then execute this query in you database manager, and you'll see the duplicated results set.
I don't like answering my own question, but I think it's important to explain what I found out incase anyone else ends up in the same place.
As Vitalyp suggested, replacing #reviews = #place.reviews.uniq with #reviews = #place.reviews.uniq produced the correct number of rows, but I was struggling to work out why, when opening the table clearly showed there were only two records.
It turned out there was another model, one I had previously tried using, to create a many-to-many association, which was confusing matters. It would appear that as I hadn't fully removed it after deciding not to use it, and when I completely destroyed the model, it's tables and anything that referenced it, things went back to normal.
It would appear that review 1 was associated with the place twice, once directly and once via the old many-to-many table, so it appeared twice.
It doesn't make a huge amount of sense when I had deleted the has_many:, but I guess it is a peculiarity of how Rails works.
My way
controller pattern 1 (note: Here, it's calling all users!!)
#users = User.confirmed.joins(:profile)
view pattern 1 (note: Here, it only shows first 10 users but it show the number of all users!!)
<%= "ALL ("+ #users.count.to_s + " users)" %>
<% #users.limit(10).each do |users| %>
<%= render 'users/user', :user => users %>
<% end %>
Should it be just like this below if I'm considering page loading speed?
Or it won't be changed?
controller pattern 2 (note: I added limit(10), and #users_count to count all users)
#users = User.confirmed.joins(:profile).limit(10)
#users_count = User.confirmed.joins(:profile).count
view pattern 2 (note: I took it off limit(10) and use #users_count for count)
<%= "ALL ("+ #users_count.to_s + " users)" %>
<% #users.each do |users| %>
<%= render 'users/user', :user => users %>
<% end %>
If you have lazy loading disabled, then the second approach would be faster because Rails doesn't need to fetch all records from the database. You should really fetch only the records you need when performing queries.
If you have lazy loading enabled (by default), then it is the same, because the data is fetched when it is needed, so the effect will be the same. You can also put two variables in controller and write the same query as you did in the view and the data will be fetched only if and when it is needed.
#users = User.confirmed.joins(:profile)
#users_count = #users.count
#users = #users.limit(10)
You can check sql generated by the app in your rails console and then decide.
Also, if you are using profile in user.html.erb, consider using includes instead of join. Join can cause n+1 problem if you need associated records. If you don't, you do not want to fetch records you don't need. You can read more about it here, in 12 Eager Loading Associations.
The two options are exactly the same. Neither of them loads all the Users because you're just chaining scopes. The query is only run when you call .each in the view, at which point you've applied the .limit(10) anyway. I'd go with the first option because the code is cleaner.
#users.count does one query to get the count, it doesn't instantiate any User objects.
#users.limit(10).each ... does one query (actually two because you've used includes) with a limit, so it will instantiate 10 objects plus your includes.
you can try #users.find_in_batches
Please take a look
Find in batches
Please let me know
If you want speed loading
I can suggest you memcache Memcache