I'm creating a application using ruby on rails, but currently i'm suffering a problem like db relation, below my code:
Company
has_many :posts, :foreign_key => :company_id
Post
belongs_to :companies, :foreign_key => :company_id
controller
#post = current_user.companies.all
view
<% #post.each do |p| %>
<%= p.posts.post_title %>
<% end %>
Showing error above code.
If I debug like use <%= debug p.posts %> then showing all posts, which is under my companies but when I use <%= debug p.posts.post_title %> then showing ActiveRecord_Associations_CollectionProxy:0x0000000e490b98
Thanks
I think the problem here is that you are trying to call the method :post_title on p.posts, which is an ActiveRecord::Associations::CollectionProxy object.
In your example, p is a Company object, which has a method posts, which returns to you a CollectionProxy object that acts a lot like a list of posts. That list will not have a method post_title, but each element of that list will have a method post_title
So, instead of
<% #post.each do |p| %>
<%= p.posts.post_title %>
<% end %>
You will want something like:
<% #post.each do |company| %>
<% company.posts.each do |post| %>
<%= post.post_title %>
<% end %>
<% end %>
Two additional things to note:
1) The variable #post is inaccurately named. Inaccurate variable names will lead to confusion when trying to understand what is happening. current_user.companies.all returns a list of companies, and therefore, it should read:
#companies = current_user.companies.all
not
#post = current_user.companies.all
2) The actual error that is being shown to you likely says something like
Undefined Method 'post_title' for ActiveRecord_Associations_CollectionProxy:0x0000000e490b98
Not just
ActiveRecord_Associations_CollectionProxy:0x0000000e490b98
When debugging and asking for help, it's very important to note the entire message of the exception being raised.
Because companiy has_many :posts........ posts are objects you need a loop to show all posts e.g
p.posts.each do |post|
Related
First, the example I read in the docs shows to declare the associated model as singular, :address, but if I do I get the error Association named 'address' was not found on User; If I change it to plural :addresses, then the next problem I have is the association doesn't work in views undefined method `country' for ...
Why am I declaring the association as plural and how can I make the association available in the view
User.rb:
class User < ActiveRecord::Base
searchkick word_middle: ['full_name', 'description', 'interests']
has_many :addresses
scope :search_import, -> { includes(:addresses) }
search.html.erb:
<% #users.each do |u| %>
<li>
<%= link_to "#{u.first_name} #{u.middle_name} #{u.last_name}", page_path(name: u.name) %>
<% #ua=u.addresses.where("current=?", true) %>
<% if #ua.country=="US" %>
<%= #ua.city %>, <%= #ua.state %> <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% else %>
<%= #ua.city %>, <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% end %>
</li>
<% end %>
</ul>
In controller, do this: #user = User.includes(:addresses).where(your_query) to make the association readily available in view.
And yes has_many associations are bound to be plural: "User has_one
:life" and "User has_many :passions"; does it make sense?
And finally your error: where returns an array because you queried: "bring me all records which fulfill this condition". find, on the other hand, will bring back 1 specific record as it expects a unique attribute or brings back first record that matches that attribute.
What you need to do:
You should either do this (if you are dead-sure that you will get 1
such record or you need only one of that type):
<% #ua=u.addresses.where("current=?", true).first %>
OR
If you need to go through all the resultant array then:
<% #ua=u.addresses.where("current=?", true) %>
<% #ua.each do |ua| %>
# Your code for each ua instead of #ua.
<% end %>
Happy Learning :)
I have a very concrete dilemma right now.
Given the following models:
class Message < ActiveRecord::Base
attr_accessible :body, :sent_at
belongs_to :subject
end
class Subject < ActiveRecord::Base
attr_accessible :title
has_many :messages
belongs_to :last_message, :class_name => 'Message', :foreign_key => 'last_message_id'
end
In a view I want to iterate over a list of subjects and display:
- Subject title
- sent_at for the subject's last message
like this:
<% #subjects.each do |subject| %>
<%= subject.title %>
<%= subject.last_message.sent_at %>
<% end %>
The thing is: subject.last_message may some times be nil. In which case, the above code will throw an exception.
So: What is the best solution to this? I can see 3 possibilities, but honestly don't know which are considered good or bad.
1) Let the view rescue it
<%= subject.last_message.sent_at rescue '' %>
2) Make a helper
def last_message_sent_at(subject)
return '' if subject.last_message.blank?
subject.last_message.sent_at
end
<%= last_message_sent_at(subject) %>
3) Make a sort of "proxy" on the Subject model
class Subject < ...
...
def last_message_sent_at
return '' if last_message.blank?
last_message.sent_at
end
end
<%= subject.last_message_sent_at %>
Which would you choose, and why? Or is there perhaps another way, which I haven't thought about?
/ Carsten
Use try :
<%= subject.last_message.try(:sent_at) %>
So, if subject.last_message is nil, you will get no output; else if it is not nil, it will call the method sent_at on subject.last_message.
It is like a convenient form for your #2 idea
Documentation
As additional thought, helper is a bad choice. You ideally always want a "receiver" (in some_class.perform(), some_class is the "receiver" i.e. it "receives" the message "perform"). I avoid Helpers unless I need to generate HTML. So, your #3 does have a receiver, but since Rails provides try, you do not need to roll your own.
The easiest thing, in this case, may be to simply check for the null value...
<% #subjects.each do |subject| %>
<%= subject.title %>
<%= subject.last_message.sent_at if subject.last_message %>
<% end %>
or
<% #subjects.each do |subject| %>
<%= subject.title %>
<%= subject.last_message && subject.last_message.sent_at %>
<% end %>
If you had any logic or action to perform, best practice would be to move it out of your views (into helpers, presenters).
Since your question is about "how to call a method on possibly nil instance?", then try is probably the best way to go in this case. Because, it's already there and you don't need any extra gems.
On the other note, you can improve your code by just putting render #subjects and moving your block content into the _subject parcial. Rails will do the looping for you.
Hey all, I have been away from rails for a while and have been catching up to rails 3, I think my problem might stem from me using an unsupported syntax from rails 2 that didnt make it to rails 3. Anyways, my problem might be to broad but while I have been able to fix all the other problems with this app, this one still puzzles me.
On the home page of the site there is this code:
<%- unless #requested_contacts.empty? -%>
<h2>Contact Requests</h2>
<ul class="list requests small">
<%- #requested_contacts.each do |contact| -%>
<%- conn = Connection.conn(current_user, contact) -%>
<li>
<p><%= person_link contact %></p>
<p class="meta published">
<%= time_ago_in_words(conn.created_at) %>
ago
</p>
<p>
<%= link_to "Respond", edit_connection_path(conn) %> |
<%= link_to "View profile", contact %>
</p>
</li>
<%- end -%>
</ul>
<%- end -%>
When I click respond instead of getting to the Connection edit page, I get an error:
undefined method `contact' for nil:NilClass
app/controllers/connections_controller.rb:15:in `edit'
{"id"=>"4"}
The controller code is:
def edit
#contact = #connection.contact
end
The relavent models are, Person.rb:
has_many :connections
has_many :contacts, :through => :connections, :order => 'people.created_at DESC'
has_many :requested_contacts, :through => :connections, :source => :contact
Connection.rb:
belongs_to :person
belongs_to :contact, :class_name => "Person", :foreign_key => "contact_id"
def conn(person, contact)
find_by_person_id_and_contact_id(person, contact)
end
The edit page I am trying to get to is:
<h2>Contact request</h2>
<p>You have a contact request from <%= #contact.name %></p>
<% form_for(#connection) do |f| %>
<p>
<%= f.submit "Accept", :class => "button" %>
<%= f.submit "Decline", :id => nil, :class => "button" %>
</p>
<% end %>
I think that is all the relevant code to this issue. If there is anything else you might need to see please let me know, I am very grateful for any an all help. Sorry if it is glare lying obvious, I am learning ruby and rails as a hobby (and loving it so far!).
When you see this error:
undefined method `contact' for nil:NilClass
It usually means you're trying to call a method on an unassigned variable. Since the error points to the controller, and the code you've shown us is:
def edit
#contact = #connection.contact
end
I'm going to guess that #connection is unassigned. Normally you would expect to have a finder method to retrieve the value before using it, like:
#connection = Connection.find(params[:id])
Sometimes this code is placed in a before_filter, to reduce duplication. If you do have such a filter, perhaps it is not being reached. It's hard to know without seeing the rest of the controller code.
The problem is that you seem to have mixed things up. In your controller's edit method, you reference #connection which hasn't been instantiated as far as we know, like zetetic said, so it's nil, then you try to call a method on it. It seems we need even more information.
I believe your def conn(person, contact) should be a class method: def self.conn(person, contact), based on the way you are calling it (not from an object but directly from the class).
Basically, the problem is that conn is coming up as nil, I believe it's for the reason I mentioned above, but if not, then figuring out why will find you your answer.
EDIT: If you are still getting the same error, then I recommend you try this out in the rails console.
Connection.find_by_person_id_and_contact_id(person, contact)
Try and use the same information you are using in the form which is generating the error. Most likely the problem is that there is no connection between the person and the contact. If you are sure there is but the above method says otherwise, then there is most likely an issue with how you set up your associations. Remember that not only do you have to specify the association in the class but you also have to modify the tables accordingly to reflect the association.
Assuming your associations are OK, and person references a connection.
Replace this line <%- conn = Connection.conn(current_user, contact) -%> with <%- conn = #person.conn(current_user, contact) -%>
I am trying to display the output of this find -
#test = User.joins(:plans => [:categories => [:project => :presentations]]).where(current_user.id)
Here is my output loop
<% #test.each do |p| %>
<%= p.plans %>
<% p.plans.each do |d| %>
<%= debug(d) %>
<% d.categories.each do |e| %>
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
<% end %>
<% end %>
<% end %>
The loop works until it gets to project when it throws this error
undefined method `each' for "#<Project:0x000001033d91c8>":Project
If I change it to projects in the loop it gives this error
undefined method `projects' for #<Plan:0x000001033da320>
The debug at categories level shows this
--- !ruby/object:Category
attributes:
id: 2
name: test
short_name: tst
created_at:
updated_at:
category_id: 2
plan_id: 5
My relationships look like this
User
has_many :user_plans
Plan
has_many :user_plans
has_and_belongs_to_many :categories
Category
has_one :project
has_and_belongs_to_many :plans
Project
has_many :presentations, :dependent => :delete_all
Presentation
belongs_to :project
Do I need to changed my find ?
Thanks, Alex
Category has_one :project
so it is single object not collection thus no each method.
According to your relationship definitions, Category only has_one project, so why do you want to iterate over e.project? If you just want to show debugging output, replace
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
with
<%= debug(e.project) %>
But if you want to go deeper, into presentations, do:
<%= debug(e.project) %>
<% e.project.presentations.each do |presentation| %>
<%= debug(presentation) %>
<% end %>
Your problem is that you are calling the array method .each on a single object.
category.project will give you a single Project object right? That's not an array, so you can't call each on it.
Replace this:
<% e.project.each do |r| %>
<%= debug(r) %>
<% end %>
with
debug(e.project)
While you're at it, here's some other advice: use descriptive variable names. Why does 'p' represent a test, 'd' represent a plan, 'e' represent a category, etc? Variable names should tell you what the object is. Similarly, i'd expect the variable #test to hold a Test object. In your code it seems to be an array. Use plural variable names for a variable that holds a collection of that type of object - eg #plans would be an array of Plan objects.
eg
<% #tests.each do |test| %>
<% test.plans.each do |plan| %>
<%= debug(plan) %>
<% plan.categories.each do |category| %>
<%= debug(category.project) %>
<% end %>
<% end %>
<% end %>
Isn't that more readable?
I want to edit multiple items of my model photo in one form. I am unsure of how to correctly present and POST this with a form, as well as how to gather the items in the update action in the controller.
This is what I want:
<form>
<input name="photos[1][title]" value="Photo with id 1" />
<input name="photos[2][title]" value="Photo with id 2" />
<input name="photos[3][title]" value="Custom title" />
</form>
The parameters are just an example, like I stated above: I am not sure of the best way to POST these values in this form.
In the controller I want to something like this:
#photos = Photo.find( params[photos] )
#photos.each do |photo|
photo.update_attributes!(params[:photos][photo] )
end
In Rails 4, just this
<%= form_tag photos_update_path do %>
<% #photos.each do |photo| %>
<%= fields_for "photos[]", photo do |pf| %>
<%= pf.text_field :caption %>
... other photo fields
UPDATE: This answer applies to Rails 2, or if you have special constraints that require custom logic. The easy cases are well addressed using fields_for as discussed elsewhere.
Rails isn't going to help you out a lot to do this. It goes against the standard view conventions, so you'll have to do workarounds in the view, the controller, even the routes. That's no fun.
The key resources on dealing with multi-model forms the Rails way are Stephen Chu's params-foo series, or if you're on Rails 2.3, check out Nested Object Forms
It becomes much easier if you define some kind of singular resource that you are editing, like a Photoset. A Photoset could be a real, ActiveRecord type of model or it can just be a facade that accepts data and throws errors as if it were an ActiveRecord model.
Now you can write a view form somewhat like this:
<%= form_for :photoset do |f|%>
<% f.object.photos.each do |photo| %>
<%= f.fields_for photo do |photo_form| %>
<%= photo_form.text_field :caption %>
<%= photo_form.label :caption %>
<%= photo_form.file_field :attached %>
<% end %>
<% end %>
<% end %>
Your model should validate each child Photo that comes in and aggregate their errors. You may want to check out a good article on how to include Validations in any class. It could look something like this:
class Photoset
include ActiveRecord::Validations
attr_accessor :photos
validate :all_photos_okay
def all_photos_okay
photos.each do |photo|
errors.add photo.errors unless photo.valid?
end
end
def save
photos.all?(&:save)
end
def photos=(incoming_data)
incoming_data.each do |incoming|
if incoming.respond_to? :attributes
#photos << incoming unless #photos.include? incoming
else
if incoming[:id]
target = #photos.select { |t| t.id == incoming[:id] }
end
if target
target.attributes = incoming
else
#photos << Photo.new incoming
end
end
end
end
def photos
# your photo-find logic here
#photos || Photo.find :all
end
end
By using a facade model for the Photoset, you can keep your controller and view logic simple and straightforward, reserving the most complex code for a dedicated model. This code probably won't run out of the box, but hopefully it will give you some ideas and point you in the right direction to resolve your question.
Rails does have a way to do this - I don't know when it was introduced, but it's basically described here: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers
It took a bit of fiddling to alter the configuration properly for the case where there's no parent object, but this seems to be correct (it's basically the same as gamov's answer, but cleaner and doesn't allow for "new" records mixed in with the "update" records):
<%= form_tag photos_update_path do %>
<% #photos.each do |photo| %>
<%= fields_for "photos[#{photo.id}]", photo do |pf| %>
<%= pf.text_field :caption %>
... [other fields]
<% end %>
<% end %>
<% end %>
In your controller, you'll end up with a hash in params[:photos], where the keys are photo IDs, and the values are attribute hashes.
You can use "model name[]" syntax to represent multiple objects.
In view, use "photo[]" as a model name.
<% form_for "photo[]", :url => photos_update_path do |f| %>
<% for #photo in #photos %>
<%= render :partial => "photo_form", :locals => {f => f} %>
<%= submit_tag "Save"%>
<% end %>
<% end %>
This will populate input fields just like you described.
In your controller, you can do bulk updates.
def update
Photo.update(params[:photo].keys, params[:photo].values)
...
end
Indeed, as Turadg mentioned, Rack (Rails 3.0.5) fails if you mix new & existing records in Glen's answer.
You can work around this by making fields_for work manually:
<%= form_tag photos_update_path do %>
<% #photos.each_with_index do |photo,i| %>
<%= fields_for 'photos[#{i}]', photo do |pf| %>
<%= pf.hidden_field :id %>
... [other photo fields]
<% end %>
<% end %>
This is pretty ugly if you ask me, but it's the only way I found to edit multiple records while mixing new and existing records.
The trick here is that instead of having an array of records, the params hash gets a array of hashes (numbered with i, 0,1,2, etc) AND the id in the record hash. Rails will update the existing records accordingly and create the new ones.
One more note: You still need to process the new and existing records in the controller separately (check if :id.present?)