I am using Rails 4.2.4. I know I have set my relations correctly but I'm getting undefined method "city" for #
support.rb:
belongs_to :user
user.rb:
has_many :supports (should the be plural?)
views/users/show.html.erb:
<%= #user.supports.city %>
In my supports table:
t.string :city
t.integer :user_id
I have a form for support in which I have filled out the city field and I can see in entry with Support.all in the rails console so Im sure the value for :city is saved in the db.
I have used rails g scaffold support for this process where a user can create many supports. Am I missed something?
has_many :supports should be plural
#user.supports returns all supports but it can return an empty array. So you have to use:
if support = #user.supports.first
# use support.city
end
or
<% #user.supports.each do |support| %>
<h1><%= support.city %></h1>
<% end %>
If you're trying to access associative data, you'll need to understand that pluralized relations (IE has_many) will return collections of data:
#app/models/support.rb
class Support < ActiveRecord::Base
belongs_to :user #-> #support.user
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :supports #-> #user.supports
end
To answer your question about the :plural, no, you don't need to call it a plural. However, as per Rails convention, it builds the entire relationship (and queries) off the back of the name:
belongs_to associations must use the singular term. If you used the
pluralized form in the above example for the customer association in
the Order model, you would be told that there was an "uninitialized
constant Order::Customers". This is because Rails automatically infers
the class name from the association name. If the association name is
wrongly pluralized, then the inferred class will be wrongly pluralized
too.
If you wanted to use singular names for your associations with has_many, you'll have to define your class etc:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :support, class_name: "Support", foreign_key: "support_id"
end
--
When you get your returned data from a has_many collection, you need to cycle through the data. Since it's a collection (as opposed to a "member" -- single record), you will need to something like the following:
<% #user.supports.each do |support| %>
<%= support.city %>
<% end %>
Related
I'm teaching myself Rails and I'm trying to setup a collaboration relationship kind of like Github adds collaborators to projects. My models look like this:
class Restaurant < ActiveRecord::Base
has_many :employees
has_many :users, through: :employees
end
class User < ActiveRecord::Base
has_many :employees
has_many :restaurants, through: :employees
end
class Employee < ActiveRecord::Base
belongs_to :restaurant
belongs_to :user
end
The employee table also has a user_type column to handle permissions within the project (restaurant). I can't figure out how to make my employee_controller set this relationship. Users primary key is :email so I'm guessing a form should be able to receive the :email parameter, check if such a user with the inputed email exists, and add the relationship to the employees table.
I'm looking to be able to do something like this:
Restaurant_A = Restaurant.create(restaurant_params)
User_A.restaurants = Restaurant_A
Restaurant_A.employees = User_B
I think my models might be wrong but essentially I'd like to be able to have users with the ability to create a restaurant as well as be added as employees of another restaurant/their own restaurants.
Your model is all right - no problem with that.
What you are trying to accomplish, you can accomplish that by following:
restaurant_a = Restaurant.create(restaurant_params)
# Remember to name it 'restaurant_a', it is convention in Ruby
user_a.restaurants << restaurant_a
<< is an operator that inserts left hand side thing into its right hand thing. So in our case, it will insert restaurant_a into the list of restaurants that are associated with user_a, and then you call save operation on your user_a like user_a.save.
Same case is on the other side:
restaurant_a.employees << user_b
# According to Ruby convention, you shouldn't start your variable
# name with an upper case letter, and you should user a convention
# called 'snake_type' naming convention. So instead of naming
# your variable like 'firstDifferentUser', name it 'first_different_user'
# instead.
restaurant_a.save # To successfully save the record in db
Edit:
For creating a form:
<%= form_for(#restaurant, #employee) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<% end %>
And you need to define #restaurant and #employee in your employee's controller new action, because you are gonna create a new employee for a particular restaurant.
I have a rails app with the following:
class Habit < ActiveRecord::Base
has_many :habit_journals
has_many :road_blocks
class RoadBlock < ActiveRecord::Base
belongs_to :habit
has_many :road_block_histories
class HabitJournal < ActiveRecord::Base
belongs_to :habit
end
I have a form that creates HabitJournals, although within this form I am trying to create RoadBlockHistories (which is just a rating on a RoadBlock over time)
I can't seem to work out how to create the form for this.
Habits have many HabitJournals and RoadBlocks. RoadBlockHistories don't relate directly to HabitJournals, but I would still like to capture these at the same form submission.
Any help would be appreciated.
Cheers
M
Sorry, I realised I need to add additional info.
This is Rails 4.1
A user will create a habit such as 'Be healthier'.
They then will create a number of road blocks to this habit such as 'Eating chocolate at night' and 'sleeping in instead of exercising'.
When the user views a habit they can add a journal entry (an update of how they are doing establishing the habit).
When adding a journal entry I'd like the user to be displayed with all their "road blocks" with a dropdown to select a rating (which is a mark out of 10 of how they are going with that road block). This rating would create a road_block_history object.
Hopefully this clarifies.
Cheers
M
Objects
I understand what you want - still trying to consider how to get it working.
Something you need to consider is the nature of Ruby on Rails (Ruby in particular). Ruby is an object orientated language, meaning everything it does is based around objects.
The problem you have is the object you're trying to create ("RoadBlockHistories") has no direct association to your "parent" object that you're trying to create ("HabitJournals").
accepts_nested_attributes_for
The "way" you'd surmount this problem typically is to use the accepts_nested_attributes_for method - basically allowing you to pass data from a parent model to a child.
The important thing to note here is that this only works if your models are directly associated, IE that it's either a has_many, belongs_to or has_many :through for the model you're trying to create.
This is not magic - it's core Ruby. If you have a model you're trying to populate, if it has associated data, you'll be able to send that through your original model. However, as the language is object orientated, you cannot just populate an unrelated object without having that association
Fix
From what you've written, I would certainly look at treating the two forms as separate data objects (IE not associating them)
Perhaps you could do this (although it will need tweaking):
#app/models/habit.rb
class Habit < ActiveRecord::Base
has_many :habit_journals
has_many :road_blocks
end
#app/models/road_block.rb
class RoadBlock < ActiveRecord::Base
belongs_to :habit
has_many :road_block_histories
end
#app/models/habit_journal.rb
class HabitJournal < ActiveRecord::Base
belongs_to :habit
has_many :road_blocks
end
Although this is not tested, nor do I think it would work, the reason I included this is to give you the idea about how you could pass an "unassociated" object through to Rails.
You can actually pass objects / data through others - so you could actually use something like the following:
#app/controllers/habit_journals_controller.rb
Class HabitJournalsController < ApplicationController
def new
#habit_journal = HabitJournal.new
#habit_journal.road_blocks.build.road_block_histories.build #-> again, not sure if will work
end
def create
#habit_journal = HabitJournal.new(habit_journal_params)
#habit_journal.save
end
private
def habit_journal_params
params.require(:habit_journal).permit(:x, :y, :z, road_blocks_attributes: [road_block_histories_attributes:[] ])
end
end
If the associations are correct, this should allow you to create the following form:
#app/views/habit_jorunals/new.html.erb
<%= form_for #habit_journal do |f| %>
<%= f.fields_for :road_blocks do |rb| %>
<%= rb.fields_for :road_blocks_histories do |h| %>
<%= h.text_field :rating %>
<% end %>
<% end %>
<% end %>
I hope to get some great help here. I am a noob in many areas of Rails 3.2 but am getting much better.
I have a collection select that has retrieved the correct records. I have multiple properties, that then has multiple users. The collection select correctly uses a UserProperty table to filter only the property users (I want the collect to display each user related to that property). So I have the user ID and not the name.
So I tried to create an object by querying the parent table (users) to pull the parents properties and pass through. Unfortunately, the second query is only passing a single record. So scrapped that.
The easiest would be to use the 1st query in the collection_select but then display the parent field based on the reference user id. Is there a syntax that allow me to display text back to the parent (USER) table within the collection select and queried object "pool"?
The goal would be to query UserProperties by the nested route(#property) - Working.
(#pool contains the correct results)
Then display the the name field from User. (User.name)
Here is my code:
Controller:
def find_property
#property = Property.find(params[:property_id])
#pool = UserProperty.find_all_by_property_id(#property)
end
View:
<%= fields_for (:assigned_to_user_id) do |pool| %>
<%= pool.collection_select(:assigned_to_user_id, #pool, :id, :id ) %>
<!-- Need to change last field from id to name field of Users Table -->
Model Design: (need name from Users)
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
class User < ActiveRecord::Base
attr_accessible :name, :email
has_many :properties, through: :user_properties
end
# Table name: user_properties
#
# id :integer not null, primary key
# user_id :integer
# property_id :integer
class UserProperty < ActiveRecord::Base
attr_accessible :property_id, :user_id
belongs_to :user
belongs_to :property
end
Issue Solved. Rails has built in filters based on nested routing.
So a friend helped me solve this issue using a simpler method and along RoyTheBoy's suggestion.
Although there was a many through relation, there was a need just for a has many statement.
Models as follows:
has_many :users, through: :user_properties
has_many :user_properties (new line)
Then remove the filter from the #pool
#pool = #property.users.all (rails automagically pulls from the nested route)
FYI - existing before_filter for loading nested properties
#property = Property.find(params[:property_id])
This allowed a simpler collection_select
<%= fields_for (:assigned_to_user_id) do |pool| %>
<%= collection_select(:assigned_to_user_id, :user_id, #pool , :id, :name, prompt: true) %>
Nested routing for the property, handles the filtering automagically! Gotta love rails, especially a newbie like me!
Thanks to Robert for the Help!
from your table definitions, in your models you need
class UserPropery < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_many :user_properties
you can then use
UserProperty.where(...).user.name
however you say
"I have multiple properties, that then has multiple users."
this implies to me that a property can have many users, In this case you should have a user_propery_id field in your User table and in you models
class UserPropery < ActiveRecord::Base
has_many :user
class User < ActiveRecord::Base
belongs_to :user_properties
you can then use something like
UserProperty.where(...).users.collect {|user| [ user.id, user.name ]}
this is a good explanation of how the associations work
http://guides.rubyonrails.org/association_basics.html
I have a polymorphic association: my user model can be of type 'student' or of type 'employer'. I am trying to set up a has_and_belongs_to_many association between the student model and another model called the 'project' model. When I try to call:
controller:
#my_projects = current_user.projects
view:
<% #my_projects.where(state: :posting).each do |project| %>
<%= project.students %><br>
<% end %>
I am told:
PG::UndefinedTable: ERROR: relation "projects_users" does not exist
I have defined a table called "projects_students" though, which i think should work. I don't want to have a "project_users" table because the table is only for students, not for employers. How do I fix this? Here are my models:
class Student < User
has_and_belongs_to_many :projects
end
-
class Project < ActiveRecord::Base
has_and_belongs_to_many :students
end
You are building #my_projects from a starting context of a User object. If you run User.first.projects and Student.first.projects from the Rails console, you will see that one tries to use projects_users as the join table and the other tries to use projects_students. So rather than force a special "join_table" name in the model as I had suggested originally you could do something like this when you look up the projects:
#my_projects = current_user.becomes(Student).projects
I am using Ruby on Rails 3.1.0. I am trying to save a nested model having an attribute that is intended to store the foreign key of the parent model. At the creation time of the parent model I would like to set that attribute value to the parent model id value.
In my model I have:
class Article < ActiveRecord::Base
has_many :article_category_relationships
has_many :categories,
:through => :article_category_relationships
# Accept nested model attributes
accepts_nested_attributes_for :articles_category_relationships
end
class Category < ActiveRecord::Base
has_many :article_category_relationships
has_many :articles,
:through => :article_category_relationships
end
# The join model:
class ArticleCategoryRelationship < ActiveRecord::Base
# Table columns are:
# - article_id
# - category_id
# - user_id
# - ...
belongs_to :category
belongs_to :article
end
In my view I have the following:
...
<% #current_user.article_categories.each do |article_category| %>
<%= check_box_tag 'article[articles_category_relationships_attributes][][category_id]', article_category.id, false %>
<% end %>
In my controller I have:
def create
#article = Article.new(params[:article])
...
end
In my case, the article_id (related to the ArticleCategoryRelationship nested model) should be set to the #article.id value after the Article creation and the problem is that the Ruby on Rails framework seems do not set that value at the creation time. In few words, considering my case, I would like to attach the foreign key automatically.
Just to know, the generated params when the form is submitted is:
"article"=>{"title"=>"Sample title", "articles_category_relationships_attributes"=>[{"category_id"=>"8"}, {"category_id"=>"9"}, {"category_id"=>"10"}] }
Is it possible to "auto"-set the foreign key (article_id) of the nested model? If so, how can I do that?
try using :
#a_particular_article.article_category_relationships.build(params[:something])
you can see here for more info, and might want to have a look at nested attributes and at validates_associated
In my opinion, you just CAN'T do that.
If you want to save articles_category_relationships, you need a article_id for each of them.
And, when you save article, rails will first validate article and all sub-objects. This means
article.valid? must be true before you can save any record.
Since article does not have an id before save to db, article_id in any of articles_category_relationships is empty.
Therefore, article.valid? will always be false as long as you need to CREATE new article and its sub-objects at the same time
To summarize, here is the steps of rails:
validate article itself, success!(NOTE: note saved)
validate articles_category_relationship for each of article.articles_category_relationships, article_id not provided, fail!
What you can do
create article first
assign params to this created article
update with params