Migration for Users Following Posts - ruby-on-rails

In my application, I have models for Users and Projects.
I want users to have the ability to follow many projects. So users has_many projects, and projects belongs_to users that not only created them but users that follow them too.
So I generated a migration called ProjectRelationship and tried to make it flow below, but it doesn't seem to work. Can somebody help me fix my associations?
Thanks for the help!
project_relationship.rb
class ProjectRelationship < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
project.rb
belongs_to :user
has_many :project_relationships
has_many :followers, through: :project_relationships, source: :user
user.rb
has_many :projects
has_many :project_relationships
has_many :projects_followed, through: :project_relationships, source: :project
schema.rb
create_table "project_relationships", :force => true do |t|
t.integer "follower_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "projectuser_id"
end
add_index "project_relationships", ["follower_id"], :name => "index_project_relationships_on_follower_id", :unique => true
add_index "project_relationships", ["projectuser_id"], :name => "index_project_relationships_on_projectuser_id"
projects/show.html.erb
<%= #project.followers.count %>

You need to specify the foreign keys. The ProjectRelationship model will be expecting the corresponding table to have a "user_id" and "project_id" columns. However, you used different names. So either specify the foreign keys:
class ProjectRelationship < ActiveRecord::Base
belongs_to :user, foreign_key: "follower_id"
belongs_to :project, foreign_key: "projectuser_id"
end
or change the column names in your migration:
create_table :project_relationships do |t|
t.integer :user_id
t.integer :project_id
...
end
You will also need to specify the foreign key in your other models:
class Project < ActiveRecord::Base
belongs_to :user
has_many :project_relationships, foreign_key: "projectuser_id"
has_many :followers, through: :project_relationships, source: :user
end
class User < ActiveRecord::Base
has_many :projects
has_many :project_relationships, foreign_key: "follower_id"
has_many :projects_followed, through: :project_relationships, source: :project
end

#roma149 - Thanks for your response. I updated the controllers, routes, and what you said. No errors generate, but when I click the button follow in _follow.html.erb, it does not seem to follow the project or update the count "#project.followers.count"
Moved details to here: Why doesn't my user follow/unfollow button work?

Related

Rails 4 HABTM has_many :through

Team, looking for some help for a very specific (newbie) situation on a Rails 4 association.
We have 3 models:
class Brand < ActiveRecord::Base
has_many :lines, dependent: :destroy
has_many :products, through: :lines, dependent: :destroy
end
class Line < ActiveRecord::Base
belongs_to :brand
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :lines
has_many :brands, through: :lines
end
This configuration works well when trying to check for Products under specific Brand (or Line) and viceversa: different Brands (or Lines) available for a specific Product. However, when it comes to delete/destroy there is an issue. We are getting this Rspec error:
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection:
Cannot modify association 'Brand#products' because the source reflection
class 'Product' is associated to 'Line' via :has_and_belongs_to_many.
We have made research on this exception, checked for Rails API, with no luck, examples found are showing a different model configuration. What's missing on this approach?
Appreciate your help guys!
In my opinion, it should be something like this:
class Brand < ActiveRecord::Base
has_many :lines, dependent: :destroy
has_many :products, through: :lines, dependent: :destroy
end
class Line < ActiveRecord::Base
belongs_to :brand
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
belongs_to :brand, through: :line
has_and_belongs_to_many :lines
end
And in migrations:
create_table :brands , force: true do |t|
t.string :name
...
t.timestamps null: false
end
create_table :lines , force: true do |t|
t.string :name
t.belongs_to :brand
...
t.timestamps null: false
end
create_table :products , force: true do |t|
t.string :name
...
t.timestamps null: false
end
create_table :line_products, force: true, id: false do |t|
t.belongs_to :line, index: true
t.belongs_to :product, index: true
end
I hope it will help.

Rails Joining multiple models on a single table

New to rails (using 4.1), making a small project management tool as a learning project. I have run into a slightly more complex model association, and I want to make sure I am on the right track here, and ask how to extend it a bit.
Consider this situation:
Models:
class Website < ActiveRecord::Base
has_many :website_user
has_many :users, through: :website_user
has_many :tasks, through: :website_user
end
class User < ActiveRecord::Base
has_many :websites, through: :website_user
has_many :website_user
end
class WebsiteUser < ActiveRecord::Base
belongs_to :website
belongs_to :user
belongs_to :role
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :website_user
has_one :website, through: :website_user
end
class Role < ActiveRecord::Base
has_many :website_user
end
DB:
create_table "roles", force: true do |t|
t.string "name"
end
create_table "tasks", force: true do |t|
t.text "description"
t.string "title"
t.integer "website_user_id"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.string "password"
t.string "password_hash"
t.string "password_salt"
end
create_table "website_users", force: true do |t|
t.integer "website_id"
t.integer "user_id"
t.integer "role_id"
end
create_table "websites", force: true do |t|
t.string "name"
t.string "url"
end
What I have going on here is basically Websites get users (team members working on sites) associated though the website_user table. That table belongs to roles, so that a team member would have a specific job on this website, and finally, tasks belong to the website_user association, so that you could swap out a user, but the task would stay associated with the role and website.
I am looking into extending it more, so that the task would be associated on website_user twice, once for the assigner, once for the assigned user of the task. However, at this point, it feels like I will have an awful lot of things attached to a big join table in the middle, and without a ton of experience under my belt, it is starting to smell like there might be a better way.
If this all looks good, how would you join the tasks to the website_user twice, once for assigner, once for assigned? Or alternatively, how would rearrange the model association?
A simple solution that first comes to head is to keep assigner and assignee ids in Task model.
Migration AddAssigneeAssignerToTask
class AddAssigneeAssignerToTask < ActiveRecord::Migration
change do
add_reference :task, :assignee, index: true
add_reference :task, :assigner, index: true
end
end
Adding belongs_to into Task model
class Task < ActiveRecord::Base
belongs_to :assignee, class: 'WebsiteUser'
belongs_to :assigner, class: 'WebsiteUser'
has_one :website, through: :assignee
end
Modifying WebsiteUser
class WebsiteUser < ActiveRecord::Base
belongs_to :website
belongs_to :user
belongs_to :role
has_many :assigned_tasks, class_name: 'Task', foreign_key: 'assigner_id'
has_many :received_tasks, class_name: 'Task', foreign_key: 'assignee_id'
end
So afterwards you can use it like this
#website_user.assigned_tasks # => []
#website_user.received_tasks # => [Task1, Task2]
BUT
If you think to add some different functionality to either assigner or assignee, you should consider to use STI or MTI
class Task < ActiveRecord::Base
belongs_to :assignee, class_name: WebsiteUser, foreign_key:website_user_id
belongs_to :assigner, class_name: WebsiteUser, foreign_key:assigner_user_id
end
class WebsiteUser < ActiveRecord::Base
has_many :assigned_tasks, class_name: Task, inverse_of: :assignee, dependent: :destroy, foreign_key: :website_user_id
has_many :tasks_assigned, class_name: Task, inverse_of: assigner, dependent: :destroy, foreign_key: :assigned_user_id
end
You will have to add another foreign key in your tasks table..
just a starting point but this should get you going..
I can not advice you in the database design, but you can assign users twice using an option called class_name. You can read more here: http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference
But you will have to add additional foreign_key to your Tasks model as well.
And I also advice you to read following chapter of M. Hartle book, as it have really good explanation between relationships of models: https://www.railstutorial.org/book/following_users#cha-following_users

Rails associations in one table (belongs_to :region, has_many :regions)

I'm trying to create simple geo-model with tree-structure with Rails4. Every region has one parent region and can have many children regions.
class Region < ActiveRecord::Base
has_many :regions, belongs_to :region, dependent: :destroy
end
Schema:
create_table "regions", force: true do |t|
t.string "name"
t.string "description"
t.integer "region_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Unfortunatelly, such code is not working. What should i do?
I think, you are looking for a self join relationship. Try this :
class Region < ActiveRecord::Base
has_many :child_regions, class_name "Region", foreign_key: "parent_id" dependent: :destroy
belongs_to :parent, class_name: "Region"
end
You should have a parent_id in your schema as well. Thanks
I assume that Rails4 works just as Rails3 in this case:
class Region < ActiveRecord::Base
has_many :regions, dependent: :destroy
belongs_to :region
end
has_many and belongs_to are class/singleton methods of Region. Aa such you cannot use one of them as a parameter to the other method.
class Region < ActiveRecord::Base
has_many :regions, dependent: :destroy
belongs_to :region
end
Of course you also need region_id integer column in your regions table.

References in Rails using custom Name?

So I am developing a rails app that will have two kinds of Users, student/tutor. but I only have one User model (using cancan for auth), so when I try to set up the meeting model (which has one tutor and one student) how do I do this? This is the model:
class Meeting < ActiveRecord::Base
belongs_to :student
belongs_to :tutor
attr_accessible :price, :subject, :time
end
and here's the relevant part of the schema:
create_table "meetings", :force => true do |t|
t.string "subject"
t.integer "student_id"
t.integer "tutor_id"
t.datetime "time"
t.integer "price"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "meetings", ["student_id"], :name => "index_meetings_on_student_id"
add_index "meetings", ["tutor_id"], :name => "index_meetings_on_tutor_id"
Without having to have two extra models containing student and tutor can I use those labels?
one way to do it..
class Meeting < ActiveRecord::Base
belongs to :student, class_name: 'User'
belongs to :tutor, class_name: 'User'
class User < ActiveRecord::Base
has_many :meet_with_students, class_name: 'Meeting', foreign_key: :tutor_id
has_many :students, through: :meet_with_students, source: :student
has_many :meet_with_tutors, class_name: 'Meeting', foreign_key: :student_id
has_many :tutors, through: :meet_with_tutors:, source: :tutor
I think you're looking for class_name:
class Meeting < ActiveRecord::Base
belongs_to :student, class_name: "User"
belongs_to :tutor, class_name: "User"
end

Many to many relationship in Rails

I'm trying to create a many to many relationship between two models in Rails 3.2.11.
A User can be associated with many Incidents and vice versa.
class User < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :incident_participants, foreign_key: "participant_id"
has_many :participated_incidents, through: :incident_participants
end
class Incident < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :incident_participants, foreign_key: "participated_incident_id"
has_many :participants, through: :incident_participants
end
The join table:
class IncidentParticipant < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
t.belongs_to :participant, class_name: "User"
t.belongs_to :participated_incident, class_name: "Incident"
end
Table for IncidentParticipants
create_table "incident_participants", :force => true do |t|
t.integer "participant_id"
t.integer "participated_incident_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
So, why doesn't rails get this relationship? When I try to do #incident.participants in my view I get this error:
"Could not find the source association(s) :participant or
:participants in model IncidentParticipant. Try 'has_many
:participants, :through => :incident_participants, :source => '.
Is it one of ?"
Any ideas?
Try taking out the t.belongs_to and replace with belongs_to.
To create a many to many association you should consider creating an association table. That is to say you will have two 1-M relationships that point to a sort interim table. For instance:
In your first model:
class Example < ActiveRecord::Base
has_and_belongs_to_many :example2
end
In your second model:
class Example2 < ActiveRecord::Base
has_and_belongs_to_many :example
end
Then you need to write a migration to link the two tables together:
class CreateTableExamplesExamples2 < ActiveRecord::Migration
create_table :examples_examples2 do |t|
t.integer :example_id
t.integer :example2_id
end
end
Then just let rails magic work. Check out the guides for more information.

Resources