NoMethodError in Ruby on Rails - ruby-on-rails

I've encountered the following error while trying to create a blogging application. Any ideas why?
NoMethodError in Articles#show
Showing app/views/articles/show.html.erb where line #1 raised:
undefined method `title' for []:Array
Extracted source (around line #1):
1: <h2><%= #article.title %></h2>
2:
3: <% if #article.category %>
4: <p class="category">
From my limited understanding it's trying to tell me that there is no 'title' field in my 'article' database table, however as you can see from the desc below there is cleary a 'title' field!
mysql> desc articles;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | YES | | NULL | |
| title | varchar(255) | YES | | NULL | |
| synopsis | text | YES | | NULL | |
| body | text | YES | | NULL | |
| published | tinyint(1) | YES | | 0 | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
| published_at | datetime | YES | | NULL | |
| category_id | int(11) | YES | | 1 | |
+--------------+--------------+------+-----+---------+----------------+
10 rows in set (0.01 sec)
Help?!
Bernard
Ps. hope the formatting of the table above holds up... doesn't seem to look very good in the preview!
Controller code for relevant call as follows.
def show
if is_logged_in? && #logged_in_user.has_role?('Editor')
#article = Article.find(params[:id])
else
#article = Article.find_all_by_published(params[:id], true)
end
respond_to do |wants|
wants.html
wants.xml { render :xml => #article.to_xml }
end
end

I think you're trying to access the title element directly from the array. You'll need to iterate through each of the objects before you can access the properties.
for #article in #articles do |a|
<h2><%= a.title %></h2>...
end
Just make sure to have in your controller...
#articles = Article.find(:all, :conditions => '...')
Update:
This part of your code returns a collection.
#article = Article.find_all_by_published(params[:id], true)

It looks like you're assigning an empty array to #article in your controller. It should instead be an instance of Article (if that's your model name). Can you paste your controller code?

Related

Rails migrations add a column and reference it to another model

mysql> desc Tab_A;
+-----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| Tab_B_id | int(11) | YES | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
With the above schema, I can do Tab_A.first.tab_b to get the respective Tab_B details.
To add Column option_A to Table Tab_A and reference that column to Model Tab_C
Migrations change updated to -->
add_reference :Tab_A, :option_a, references: :Tab_C
With that I got a column option_a_id which referenced the table Tab_C's ID.
But,
In the action -->
Tab_A.first.tab_c gives an error and I can only access Tab_A.first.tab_c_id.
I know I'm doing something wrong, big time. Not sure where.
Seems like you forget to add belongs_to association to Tab_C in Tab_A model. You should use something like this:
class TabA < ActiveRecord::Base
...
belongs_to :tab_c
...
check it.
You need to have these associations in your models:
class TabA < ActiveRecord::Base
belongs_to :tab_c
class TabC < ActiveRecord::Base
has_many :tab_a

ruby on rails - how to take only take one row

Restaurant Load (1.5ms) SELECT * FROM "restaurants" INNER JOIN
"restaurant_branches" ON "restaurant_branches"."restaurant_id" =
"restaurants"."restaurant_id"
+----------+---------+---------+---------+----------+---------+----------+---------+---------+---------+---------+---------+---------+---------+---------+-------+
| resta... | res_... | res_... | crea... | updat... | user_id | resta... | addr...
| addr... | addr... | addr... | addr... | addr... | numb... | numb... | email |
+----------+---------+---------+---------+----------+---------+----------+---------+---------+---------+---------+---------+---------+---------+---------+-------+
| 27 | DOGG... | WE S... | 2014... | 2014-... | 4 | 28 | 405 ...
| | CHICAGO | IL | 60666 | USA | | | |
| 27 | DOGG... | WE S... | 2014... | 2014-... | 4 | 29 | 111 ...
| | CHICAGO | IL | 60661 | USA | | | |
+----------+---------+---------+---------+----------+---------+----------+---------+---------+---------+---------+---------+---------+---------+---------+-------+
As you can see, I have two records for record 27. This is from a joined
table between restaurants and restaurant_branches. How would I approach
this in a view so that when I select a record on my index.html.erb file
when it gets routed to the show.html.erb file it'll only take one row of
that record and only show me one branch instead of 2?
Thank you for any help.
The two records are different, so keep them separate and list both. It's difficult to say which fields are different from the question. Based on the query, I'm assuming they are restaurant_branches fields that are different.
If it's the restaurants that you want to show/edit then use just Restaurant.all however you want to limit. If however you want to edit the restaurant_branches then introduce a RestaurantBranchesController and have a link for the branches separate.
Something like (in erb):
<%= restaurant.id %>
<% restaurant.restaurant_branches.each do |rb| %>
<%= link_to 'Some branch', rb %><br />
<% end -%>
Then the link_to(rb) should link you to RestaurantBranchesController#show action for the particular restaurant_branch.
Collection
Further to what #vee was suggesting, it seems to me that you're calling a collection of data from ActiveRecord. A collection basically means you're going to receive a multitude of objects back, rather than just one record
I'd imagine you have your models set up as follows:
#app/models/restaurant.rb
Class Restaurant < ActiveRecord::Base
has_many :branches, class_name: "RestaurantBranch"
end
#app/models/restaurant_branch.rb
Class RestaurantBranch < ActiveRecord::Base
belongs_to :restaurant
end
This will give you the ability to call the following:
#restaurant = Restaurant.find params[:id]
#restaurant.branches #-> returns the collection
--
Limit
The way to fix this will depend on what you're trying to show.
Typically, you can use something like limit to retrieve a single record, like so:
#restaurant.branches.limit(1)
Like most things in life, this will be much simpler to resolve if you define what you want to limit the results for - what's the purpose?

Mutiple selection select boxes with Ruby on Rails

I'm using 4 tables to fill a chart, similar to these:
#colors #types #fuels
----------------- --------------------- -------------------
| ID | Color | | ID | Type | | ID | Fuel |
----------------- --------------------- -------------------
| 1 | 'Red' | | 1 | 'SUV' | | 1 | 'Gasoline' |
| 2 | 'Green' | | 2 | 'Truck' | | 2 | 'Diesel' |
| 3 | 'Blue' | | 3 | 'Sports Car' | | 3 | 'Electric' |
| 4 | 'Yellow' | | 4 | 'Compact' | | 4 | 'Hybrid' |
----------------- --------------------- -------------------
The last table is an orders table, similar to this:
#orders
--------------------------------------------------------
| order_id | color_id | type_id | fueld_id | purchases |
--------------------------------------------------------
| 1 | 2 | 1 | 4 | 2 |
| 2 | 1 | 4 | 1 | 4 |
| 3 | 2 | 2 | 2 | 6 |
| 4 | 4 | 1 | 4 | 2 |
| 5 | 1 | 4 | 2 | 1 |
| 6 | 3 | 3 | 3 | 1 |
| 7 | 3 | 3 | 3 | 2 |
| 8 | 2 | 1 | 1 | 2 |
--------------------------------------------------------
I have a controller that polls data from them all to make the chart. So far so good. Now, I want to let the user pick one or more attributes from each table, to make a dynamic orders page.
My approach is to show 3 select boxes (listboxes) that could allow the user to make a selection, and based on this, the #orders table would be modified. The modifications would land on different action, say:
def newtable
...
end
I know how to do this via SQL, but I'm not too sure how to properly show these listboxes using RoR. My idea is to pick one, several, or ALL the elements of each table.
Would form_for do the trick? I was trying to use it, but I don't have a model to base the query on, and I'm not sure how to create one (or if that approach is actually viable).
Thanks.
Well I'm Not Familiar with Rails 4 yet,but i can provide an answer as per Rails 3.
First Make Necessary Associations.
And In your Orders controller define one new method like
def create
----your stuff---
end
In your Order Form page:
<%= form_tag :action => 'create' %>
<%= collection_select :order,:color_id, #colors,'id','Color',{:label=> 'Select a Color',:prompt=> 'Select'} %>
<%= collection_select :order,:type_id, #types,'id','Type',{:label=> 'Select a Type',:prompt=> 'Select'} %>
<%= collection_select :order,:fuel_id, #fuels,'id','Fuel',{:label=> 'Select a Fuel',:prompt=> 'Select'} %>
<%= submit_tag "Create" %>
Hope this works.
Note: This is just a sample code for your understanding.
<%= f.select(:color, #color.collect {|p| [ p.name, p.id ] },
{ :prompt => "Please select"},
{ :multiple => true, :size => 4 }) %>
To configure your controller to accept multiple parameters: Click Me
You can create a separate model as a ruby class to handle this if you wish, but only if you find the other models getting too cramped. Hope that helps

How to destroy a row in ruby on rails for a particular user in a conversation thread

Ok I have a messages table:
+-----+-----------+--------------+--------------+-----------+--------+--------------+--------------+
| id | sender_id | recipient_id | body | parent_id | status | created_at | updated_at |
+-----+-----------+--------------+--------------+-----------+--------+--------------+--------------+
| 220 | 4 | 1 | hi | 220 | 0 | 2012-02-1... | 2012-02-1... |
| 221 | 1 | 4 | hey | 220 | 0 | 2012-02-1... | 2012-02-1... |
| 222 | 4 | 1 | hi | 220 | 0 | 2012-02-1... | 2012-02-1... |
| 223 | 1 | 4 | hi | 220 | 0 | 2012-02-1... | 2012-02-1... |
| 232 | 1 | 4 | good | 220 | 0 | 2012-02-1... | 2012-02-1... |
+-----+-----------+--------------+--------------+-----------+--------+--------------+--------------+
I'm trying to work out a way to set a message that's deleted. I figured out that one status column wouldn't be enough because if 1 use set status to 1 which equals delete then the message would be deleted for both users.
So I decided to create a migration to add sender_status and recipient_status in my messages table and then remove the status column.
I have an update_attribute method in my destro action of my control which updates the specific column when the the delete link is clicked. I will eventually add some more code to destroy the whole message but only when both sender and recipients status's both equal 1.
My controller action:
def destroy
#message = Message.find(params[:id])
#message.update_attribute('sender_status', 1) if #message.sender_id == current_user.id
#message.update_attribute('recipient_status', 1) if #message.recipient_id == current_user.id
flash[:success] = "Message deleted"
redirect_to :back
end
This helps set either the sender or recipient status. I have this in my controller also:
def show
#current_thread = MessageThread.where('sender_id = ? OR recipient_id = ?', current_user.id, current_user.id).where(:message_id => params[:id]).first
#current_thread_messages = #current_thread.message.children.where(:sender_status => 0, :recipient_status => 0)
end
So as long as status's for message rows are 0 the message will show. Right now when I delete a message it is still not shown for both users in the conversation thread.
I can't seem to figure out a clean way to make this delete illusion work. Mayve a status column has to be permanently linked up with the user or maybe I need a separate column for status's but then things will get messy. That would be an extra table where i'd have to store more message_id's.
I would really appreciate help from some experts here.
Kind reagrds
For my reading of your code, you're almost there. As far as I can tell, it's just this line that's keeping your new system hung up:
#current_thread_messages = #current_thread.message.children.where(
:sender_status => 0,
:recipient_status => 0
)
'Cause that's forcing the message to not be deleted for both users, and that's the old behaviour. An uglier but more conditional where call should give you what you want:
#current_thread_messages = #current_thread.message.children.where([
'(sender_id = ? AND sender_status = 0) OR (recipient_id = ? AND recipient_status = 0)',
current_user.id,
current_user.id
])
Hope that helps!
PS: Apologies for any syntax bugs - I'm still on Rails 2.3, so I'm just guessing that things stayed mostly the same...

Rails Joins and include columns from joins table

I don't understand how to get the columns I want from rails. I have two models - A User and a Profile. A User :has_many Profile (because users can revert back to an earlier version of their profile):
> DESCRIBE users;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | NO | UNI | NULL | |
| password | varchar(255) | NO | | NULL | |
| last_login | datetime | YES | | NULL | |
+----------------+--------------+------+-----+---------+----------------+
> DESCRIBE profiles;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | MUL | NULL | |
| first_name | varchar(255) | NO | | NULL | |
| last_name | varchar(255) | NO | | NULL | |
| . . . . . . |
| . . . . . . |
| . . . . . . |
+----------------+--------------+------+-----+---------+----------------+
In SQL, I can run the query:
> SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1;
+----+-----------+----------+---------------------+---------+---------------+-----+
| id | username | password | last_login | user_id | first_name | ... |
+----+-----------+----------+---------------------+---------+---------------+-----+
| 1 | john | ****** | 2010-12-30 18:04:28 | 1 | John | ... |
+----+-----------+----------+---------------------+---------+---------------+-----+
See how I get all the columns for BOTH tables JOINED together? However, when I run this same query in Rails, I don't get all the columns I want - I only get those from Profile:
# in rails console
>> p = Profile.joins(:user).limit(1)
>> [#<Profile ...>]
>> p.first_name
>> NoMethodError: undefined method `first_name' for #<ActiveRecord::Relation:0x102b521d0> from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in `method_missing' from (irb):8
# I do NOT want to do this (AKA I do NOT want to use "includes")
>> p.user
>> NoMethodError: undefined method `user' for #<ActiveRecord::Relation:0x102b521d0> from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in method_missing' from (irb):9
I want to (efficiently) return an object that has all the properties of Profile and User together. I don't want to :include the user because it doesn't make sense. The user should always be part of the most recent profile as if they were fields within the Profile model. How do I accomplish this?
I think the problem has something to do with the fact that the Profile model doesn't have attributes for User...
Use select() to name the columns you want. At least this works in Rails 3.0.9.
Background: my application has a primary table named :rights. I wanted to be able to ascribe a tag and color to a given :right record so I could easily pick it out of an index listing. This doesn't cleanly fit the Rails picture of associated records; most :rights will never be tagged, and the tags are completely arbitrary (user input via tag/edit).
I could try duplicating the tag data in the :right record, but that violates normal form. Or I could try querying :tags for each :right record, but that is a painfully inefficient approach. I want to be able to join the tables.
MySQL console shows:
mysql> describe rights;
+------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
...
| Tagid | int(11) | YES | | NULL | |
+------------+---------------+------+-----+---------+----------------+
mysql> describe tags;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| TagName | varchar(255) | YES | | NULL | |
| TagColor | varchar(255) | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
I am going to use TagName and TagColor in views/rights/index.html.erb, so I want the rights controller to include those columns in the #rights object it passes to the view. Since not every :right has a :tag, I want to use an outer join:
#rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id")
But, as everyone has found, this alone doesn't work: a block reference to TagName produces a server error. However, if I add a select at the end, all is well:
#rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id").select("rights.*,tags.TagName as TagName,tags.TagColor as TagColor")
Note added 6/7/13: the select clause does not require aliases - this works too:
.select("rights.*,tags.TagName,tags.TagColor")
Now I can reference TagName and TagColor in my view:
<% #rights.each do |right| %>
<tr ALIGN=Left <%=
# color background if this is tagged
" BGCOLOR=#{right.TagColor}" if right.TagColor
%> > ...
<% end %>
I don't think that you can load users and profiles with join in Rails. I think that in earlier versions of Rails ( < 2.1) loading of associated models was done with joins, but it was not efficient. Here you have some explanation and links to other materials.
So even if you explicite say that you want to join it, Rails won't map it to associated models. So if you say Profile.whatever_here it will always be mapped to Profile object.
If you still want to do what you said in question, then you can call custom sql query and process results by yourself:
p = ActiveRecord::Base.connection.execute("SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1")
and get results row by row with:
p.fetch_row
It will already be mappet to an array.
Your errors are because you are calling first_name and user method on AciveRecord::Relation object and it stores an array of Profile objects, not a single object. So
p = Profile.joins(:user).limit(1)
p[0].first_name
shoud work.
Better way to fetch only one record is to call:
p = Profile.joins(:user).first
p.first_name
p.user
But when you call p.user it will query database. To avoid it, you can use include, but if you load only one profile object, it is useless. It will make a difference if you load many profiles at a time and want to inlcude users table.
Try using select("*").joins(:table)
In this case, you would type:
User.select("*").joins(:profile)
Hope that works for you.
After reading these tips I got the joins to all be loaded in one query by reading 3 ways to do eager loading (preloading) in Rails 3 & 4.
I'm using Rails 4 and this worked like a charm for me:
refs = Referral.joins(:job)
.joins(:referee)
.joins(:referrer)
.where("jobs.poster_id= ?", user.contact_id)
.order(created_at: :desc)
.eager_load(:job, :referee, :referrer)
Here were my other attempts.
#first attempt
#refs = Referral.joins(:job)
# .where("jobs.poster_id= ?", user.contact_id)
# .select("referrals.*, jobs.*")
# works, but each column needs to be explicitly referenced to be used later.
# also there are conflicts for columns with the same name like id
#second attempt
#refs = ActiveRecord::Base.connection.exec_query("SELECT jobs.id AS job_id, jobs.*, referrals.id as referral_id, referrals.* FROM referrals INNER JOIN jobs ON job_id = referrals.job_id WHERE (jobs.poster_id=#{user.contact_id});")
# this worked OK, but returned back a funky object, plus the column name
# conflict from the previous method remains an issue.
#third attempt using a view + rails_db_views
#refs = JobReferral.where(:poster_id => user.contact_id)
# this worked well. Unfortunately I couldn't use the SQL statement from above
# instead of jobs.* I had to explicitly alias and name each column.
# Additionally it brought back a ton of duplicate data that I was putting
# into an array when really it is nice to work with ActiveRecord objects.
#eager_load
#refs = Referral.joins(:job)
# .where("jobs.poster_id= ?", user.contact_id)
# .eager_load(:job)
# this was my base attempt that worked before I added in two more joins :)
I have got round this problem by creating a VIEW in the database which is the join, and then referencing that as if it were a normal ActiveRecord table in the code. This is fine for getting data out of the database, but if you need to update it, then you'll need to go back to the base classes that represent the 'real' tables. I have found this method to be handy when doing reports that use biggish tables - you can get the data out all in one hit. I am surprised that this doesn't seem to be built into ActiveRecord, seems an obvious thing to me!
So for you:
IN SQL:
CREATE VIEW User_Profiles
AS
SELECT P.*, U.first_name
FROM Users U
inner join Profiles P on U.id=P.user_id
IN RUBY models file:
class UserProfile < ActiveRecord::Base
self.primary_key = :id
#same dependencies as profiles
end
**HINT... I always forget to set the owner of the view (I use postgres), so it blows up straight away with much cursing and self-recrimination.

Resources