HABTM not working with polymorphic association in rails - ruby-on-rails

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

Related

Rails has one association

I am working on building a portfolio section in my website right now. I have it set where a portfolio has one category that is associated with it.
class Portfolio < ApplicationRecord
has_one :category
end
I am trying to access the category attribute name in the show view for the portfolio , but I am getting this error.
SQLite3::SQLException: no such column: categories.portfolio_id: SELECT "categories".* FROM "categories" WHERE "categories"."portfolio_id" = ? LIMIT ?
This is what is in the view:
<li><i class="icon_link"></i><span>Category: </span><%= #portfolio.category.name %></li>
I remembering using this syntax before and not having any problems with it. Any help would be great. I tried to find this question on here =, but could not make of them work.
Rails conventional naming for has_one/belongs_to associations is - if you have category_id column in Portfolio then "a portfolio belongs_to a category" and "a category has one portfolio". You need to rewrite your models as:
class Portfolio < ApplicationRecord
belongs_to :category
class Category < ApplicationRecord
has_one :portfolio
After that #portfolio.category.name should work fine.
Update: Possibly "a category has many portfolios", when the models will be:
class Portfolio < ApplicationRecord
belongs_to :category
class Category < ApplicationRecord
has_many :portfolio
Whenever you have an association in rails, there has to be at least two entites (or models) involved in that association. In your particular case, you've associated a Portfolio with a Category via the has_one association. However, you need to specify the association on the Category end as well. So in your Category.rb model class, you need to write:
class Category < ActiveRecord::Base
belongs_to :portfolio
# other stuff
end
There's one more step to ensuring that the association is working properly. You need to make sure that the categories table in your database has a field called portfolio_id. If it does, then you should be good to go! If it doesn't then do the following:
Type rails generate migration addPortfolioIdToCategory in your terminal
Open up the migration file and ensure it looks like this:
def change
add_column :categories, :portfolio_id, :integer
end
Now run rake db:migrate from your terminal
If you reload your server, your problem should be solved!
NOTE
The model that specifies the belongs_to association must have the primary key attribute in the corresponding database table. In your case, if the Category model has the belongs_to association, then the categories database table must have the field titled portfolio_id.
It's complaining portfolio_id doesn't exist on your categories table. Verify your migration file for categories. Given the foreign key is set on categories it should work. Rails has one association

Rails: Polymorphic Associations - Create Child In Form And Choose Parent

To illustrate my question, i'll use the following association as listed on ruby guides for polymorphic associations:
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end
Now in our :pictures table, we will have 2 different columns, namely imageable_id and imageable_type.
The first thing that I am unclear of is, what exactly goes into imageable_type ? Is that something that the "rails magic" would automatically fill up when imageable_id is declared?
Moving on to my next point (which will probably indirectly answer my uncertainty above), how do I assign either a Product or Employee to my Picture.new form?
#create new picture form
#assume pre_created_array holds the :id for both Employee & Product
<%= form_for #picture do |f| %>
#relevant fields ontop
#choosing parent field is the one im unsure of
#this is what i assumed might be correct(?)
<%= f.select :imageable_id, options_for_select(pre_created_array) %>
<% end %>
Now does this form actually work? Is the building of the association something that has to be handled in the controller action instead? I am actually not very sure because usually in a regular association the parent can be declared before .save, such as doing #post.employee = #find an employee. So are we suppose to read the :imageable_id?
#pictures controller
def create
#picture = Picture.new(picture_params)
#manipulate :imageable_id here???
if #picture.save
#blahblah other code
end
So i am actually quite unsure about this, whether it is supposed to be the form or controller handling the building of association. Which is why i'm bringing up this 2 "uncertainties".
A polymorphic association is cool because it allows a single table to belong_to multiple, as you know. So given a picture, we don't have to know whether it belongs to an employee or a product, we can just call picture.imageable and get the parent.
So if we don't have to know where the parent is, that must means Rails has to know. How does it know? imageable_type! imageable_type is the name of the class which it belongs to. In this case, either 'Employee' or 'Product'. That way, given the imageable_id, it knows what table in which to search.
image.imageable actually calls image.imageable_type.constantize.find(image.imageable_id)
Rails will do this "magic" for you if you simply assign objects instead of IDs. image.update(imageable: Product.first) will assign both for you.
So in your form, you should be able work with a collection of objects and let Rails do the rest for you.

Active Relations: undefined method `city'

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 %>

Setting a relationship between Users to access a resource

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.

How can I access Active Record associations in a view?

If I have a Model like the following example
class Person < ActiveRecord::Base
has_many :moods
end
class Mood <ActiveRecord::Base
end
how do I change the new.html.erb allow me to choose a mood when entering a new Person? Do I need to add the foreign keys to mysql manually?
If you want a select menu, you're looking for the collection_select helper.
Check out Formtastic, it can generate a nice select menu for your has_many for you.
Rails migrations does not create foreign keys automatically. Either the constraint has to added during migrations with a command similar to the following:
execute "alter table moods add constraint fk_moods_persons foreign key (person_id) references persons(id)"
There are plugins which does this job automatically and makes the job simpler. Few of the popular ones are:
Foreigner
Redhill On Rails Core [www.railslodge.com/plugins/389-redhill-on-rails-core]
And for displaying in views, collection_select helper can be used.
I guess habtm will be a better association (if more than one person can have the same mood.) Or has_many :through is even better.
From the few details you gave, it is a little hard to guess, what you are trying to do. But I'll try anyway:
When you want a person to have exactly one mood at a time, I would model it like this:
class Person < ActiveRecord::Base
belongs_to :mood
end
class Mood < ActiveRecord::Base
has_many :people
end
The migration to create the people table in the database should then include the following statement to create a foreign key:
def self.up
create_table :people do |t|
...
t.references :mood
...
end
end
If this is what you want, you can use the collection_select command as flyfishr64 pointed out.
Inside the form_for tag in new.html.erb you would write something like this:
<% form_for #person do |f| %>
...
<%= f.collection_select :mood_id, Mood.all, :id, :mood_name, :include_blank => "--- Choose your mood ---" %>
...
<% end %>
Hope that helps!
However, when you really want your person to have multiple moods simultaneously, this would be a little more complicated, and I probably would suggest to use the has_and_belongs_to_many association in both models.
If that's the case, I would recommend watching this Railscast episode: HABTM Checkboxes. (Sorry, you have to look for the link yourself, as I am not allowed to post more than one line. Go to railscast.com and look for episode 17.)

Resources