Why is my has_one database relationship not working in rails? - ruby-on-rails

I have a relationship model in which students and employers can enter into a relationship when applying to a project. Here are the models:
class Student < User
has_many :relationships, dependent: :destroy
has_many :employers, through: :relationships
end
class Employer < User
has_many :projects
has_many :relationships, dependent: :destroy
has_many :students, through: :relationships
end
class Relationship < ActiveRecord::Base
has_one :project
belongs_to :employer
belongs_to :student
validates_uniqueness_of :project_id, :scope => [:employer_id, :student_id]
end
class Project < ActiveRecord::Base
belongs_to :employer
end
State is a column in the projects table that is automatically set equal to 'posting'. I am trying to show all relationships that have a project with state == :posting. Here is my view code:
<% #relationships.each do |relationship| %>
<% if relationship.project.state == :posting %>
<%= relationship.project.inspect %>
<% end %>
<% end %>
Also, in the controller for this view is:
#relationships = Relationship.all
When i try to open the view, i get:
PG::UndefinedColumn: ERROR: column projects.relationship_id does not exist
What doesn't make sense to me is that I am not looking in the projects table for a relationship_id column. My code should find the project from the relationship and then find the state column. I have relationships has_one :project but i don't have :projects belongs_to relationships because a project does not need a relationship in order to exist. I'm pretty sure my database relations are right. How do I fix my view code? Or, if my db relationships are wrong, what's wrong?
Update
i forgot to mention that Employer and Student are both User models through polymorphic association. The user model has a type column that can be either Student or Employer.

When you declare that Relationship has_one :project, you are telling Rails that the Project model (and the corresponding projects database table) has a relationship_id field . When you refer to relationship.project in your view, you cause that table/field to be referenced, resulting in your error.
Given that each relationship should indeed have just one project associated with it, you should declare Project as belongs_to :relationship, create the appropriate migration and update your code accordingly to maintain the value of this field.

At first glance it seems like it should be
class Employer < User
has_many :projects, through: :relationships
for this to work. Am I missing something?

Related

Rails5: belongs_to AND has_and_belongs_to - 2 relations between the same tables

I have the following Rails Model Relation:
class Grade < ApplicationRecord
has_and_belongs_to_many students, join_table: :grade_student_mappings
has_many :students
end
class Student < ApplicationRecord
has_and_belongs_to_many :grads, join_table: :grade_student_mappings
belongs_to :grade
end
Now when I want to access the grade table and the student table using the join_table - rails will fail.
Example: Grade.find_by_id(1).student will always response me the content of the direct relation between Grade and Student. The Relation via the join_table will be ignored.
Only when I uncomment the model line has_many and belongs_to then the indirect relation via the join_table will be considered.
How I can say to RAILS, which relation between Grade and Student I want to use ?
Rails supports two kinds of many-to-many relationships. has_many through and has_and_belongs_to_many. You are mixing the two. You need to pick one and the more flexible one is has_many through. Change your model files to the following:
# app/models/grade_student_mapping.rb
belongs_to :grade
belongs_to :student
# app/models/grade.rb
has_many :grade_student_mappings, dependent: :destroy
# app/models/student.rb
has_many :grade_student_mappings, dependent: :destroy
has_many :grades, through: :grade_student_mappings
Then you can access the student's grades on their show template for example by setting the grades instance variable in the students_controller show action
# app/controllers/students_controller.rb
def show
#grades = #student.grades
end
And display the grades on the student's show page. Something like the below:
# app/views/students/show.html.erb
<% #grades.each do |grade| %>
<%= grade.semester %>
<%= grade.course %>
<%= grade.letter %>
<% end %>

Database creation/saving with a many-to-many relationship

I have two tables, item and category, and a joined table named Items_Categories. The one restriction I have is that an item HAS to have a category.
In my Item Model, I have this code:
class Item < ActiveRecord::Base
has_many :items_categories
has_many :categories, through: :items_categories
has_many :item_orders
has_many :orders, through: :item_orders
even though categories is not a column in my Items database.
Here's my ItemsCategory model:
class ItemsCategory < ActiveRecord::Base
belongs_to :item
belongs_to :category
end
Here's my Category Model:
class Category < ActiveRecord::Base
has_many :items_categories
has_many :items, through: :items_categories
end
In my test, I have this issue:
test 'it is valid' do
category = Category.create(name: "hot beverages")
item = category.items.create(title: "fjdkasf", description: "fjakdsf", price: 20901290)
assert item.valid?
end
Basically, this test fails because I cannot create an item until it item.categories is not nill. But the validation doesn't pass until I create the item in item_categories. So... what do I do? Basically I can't pass the validation when I create the item because the item doesn't yet have a category. But I want to make sure that every time I create an item, it has an associated item. So what do I do?
Validation with has_many :through relations can be tricky sometimes, there's a hint in the has_many :through documentation that you should define the inverse_of attribute on your join model:
If you are going to modify the association (rather than just read from
it), then it is a good idea to set the :inverse_of option on the
source association on the join model. This allows associated records
to be built which will automatically create the appropriate join model
records when they are saved. (See the 'Association Join Models'
section above.)
class ItemsCategory < ActiveRecord::Base
belongs_to :item, inverse_of: :items_categories
belongs_to :category, inverse_of: :items_categories
end
To be honest, I'm not 100% sure this will fix it, but give it a try and report back.

How to create multiple relations between the same models

I have a User model
class User < ActiveRecord::Base
has_many :projects
end
and I have a Project model
class Project < ActiveRecord::Base
belongs_to :user
end
Obviously right now each project is owned by a user and there can only be one user per project. I now want to make my models represent another relation between the two models. I want a User to be able to follow multiple Projects, no matter who owns the Project. I know that I am going to have to use a has_many :through and create a join, but I cant wrap my head around how to change the model to keep my current relationship and add the new relationship.
Well, in that case, in your show/index action display all the projects (Project.all) in your project table. This way all users have access to all the projects. Now, in your edit action, use user.projects.all to display projects of that particular user. That should solve your problem, I don't see the need of any further association here.
Update:
This should suffice:
class Project < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_many :projects_followed, :through => :projects
user has_many :projects_owned, :through => :projects
If you don't wish to create two more relations, create just one:class ProjectsSubscribed
belongs_to :project with three fields: project_id, is_owned, is_followed
Try following relation.
class User < ActiveRecord::Base
has_many :followers
has_many :projects, :through => :followers
end
class Follower < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :followers
has_many :users, :through => :followers
end
Note it:
You can use has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship.
Hope it is helpful.

Creating new relationship so that only certain users can edit a document

I have a model "Trip" and I want to be able to have only certain users in "Group" create "comments".
When I first created the trip model, I had it set so that only one user can edit it. Now, I want to be able to invite other users to edit it as well. The part that is tripping me up (pun intended) right now is that I have both trip belong_to :user (the user who created it) and trip has_many :users, :through => :group.
Questions:
Is this allowed per Rails convention?
Based on my model, group will have both user_id and trip_id. Is this the best way to approach this problem? That is, should there be a new record in the database for every user I invite to the group?
Thanks.
user.rb
class User < ActiveRecord::Base
has_many :trips, :through => :groups
has_many :trips, :dependent => :destroy
has_many :groups
has_many :comments, :dependent => :destroy
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
view (show.html.erb)
<% unless #user.trips.empty? %>
<% #user.trips.each do |trip| %>
<!-- Content here -->
<% end %>
<% end %>
Normal Rails convention would have been to call the Group model something like TripPerson to specify the two tables that it was joining, but these join tables are a natural byproduct of relational databases. You'll have to continue creating a new Group object for every person invited to the trip.

rails has_many through with independent through table

I have a User model, Person model and Company model.
a User has many companies through Person and vice versa.
But i would like to be able to populate People and Companies that are not tied to Users that can be tied later.
class User < ActiveRecord::Base
attr_accessible :name
has_many :people
has_many :companies, :through => :people
end
class Person < ActiveRecord::Base
attr_accessible :user_id, :company_id
belongs_to :users
belongs_to :companies
end
class Company < ActiveRecord::Base
attr_accessible :name
has_many :people
has_many :users, :through => :person
end
now in the console i want to be doing the following
User.find(1).companies
then it should find me the companies in which user(1) is a person of interest.
Have I got this wrong, is there a small change that I should be making.
Your Person model can't directly "belong_to" more than one, your belongs_to :users and belongs_to :companies associations won't work that way. Companies-to-people need to be connected through another join table that describes the relationship between them, for example Employment which points to one instance of each model:
class Person < ActiveRecord::Base
has_many :employments
has_many :companies, :through => :employments
end
class Employment < ActiveRecord::Base
belongs_to :person
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :employments
has_many :people, :through => :employments
end
You can then use the :through option to associate the many companies/people on the other side of that employment relationship in the middle.
Similarly, if a Person can be owned by more than one User then you will need a join model between those two entities as well.
Just as a followup, in a has_many :through relationship, there is nothing that says you cannot use your join table (in your case, Person) independently. By nature of the relationship, you are joining through a completely separate ActiveRecord model, which is what most notably distinguishes it from the has_and_belongs_to_many relationship.
As Brad pointed out in his comment, you need to pluralize 'person' to 'people' in your relationship. Other than that, it looks like you set it up correctly. Exposing :user_id and :company_id with attr_accessible will enable you to mass-assign these values later from a postback, but often times you want to shy away from doing so with role-based associations, as you may not want to leave them exposed to potential HTTP Post attacks.
Remember, in your controller you can always do something like this with or without attr_accessible:
#person = Person.new
#person.user = User.find(...)
#person.company = Company.find(...)
#person.save
Hope that helps.

Resources