I'm trying to get a join table working with a custom id in the schema. I want to be able to get all the relevant posts for a user through the join table using the custom_id on the join table as recipient_id
Here is my current setup:
class User > ActiveRecord::Base
has_many :user_posts
has_many :received_posts, through: :user_posts, source: :recipient
end
class UserPost < ActiveRecord::Base
belongs_to :recipient, class_name: "User"
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :user_posts
has_many :recipients, through: :user_posts, source: :recipient
end
Here's the schema of my join table:
UserPost
recipient_id: integer
post_id: integer
The error I get is:
PG::UndefinedColumn: ERROR: column user_posts.user_id does not exist
I've tried adding the source like above but to no avail. It makes me think that it's looking for the user_id and not recipient_id on the join table. I'm not sure how to correctly specify the custom id as the field to use. I've tried foreign keys and other answers on here but to no luck. Could be that I'm implementing foreign keys or source wrong.
Any help is much appreciated, Thanks.
In User model, add foreigh_key to :user_posts
class User > ActiveRecord::Base
has_many :user_posts, foreign_key: :recipient_id
has_many :received_posts, through: :user_posts, source: :recipient
end
Related
All,
I'm still working my way around learning Rails and I'm not having much luck finding the relevant answers I need; that said, I suspect it's something that's been done before since I've done this by hand many times in the past myself.
I have a table called tab_accounts with account_id(int) as PK. I also have a lookup table called mtom_account_relations. This table has two int columns (account_subject, account_associate), both of which are FK to tab_accounts.account_id. The purpose of this layout is to allow for a many-to-many relationship between tab_account entries. The goal is to create an endpoint that returns an account's details along with a list of its associates, also accounts.
At this point I have the following:
models/account_relation.rb:
class AccountRelation < ApplicationRecord
self.table_name = "mtom_account_relations"
belongs_to :subject, foreign_key: "account_id", class_name: "Account"
belongs_to :associate, foreign_key: "account_id", class_name: "Account"
end
models/account.rb
class Account < ApplicationRecord
self.table_name = "tab_accounts"
self.primary_key = "account_id"
...
has_many :account_relations
has_many :associates, :through => :account_relations
has_many :subjects, :through => :account_relations
end
controllers/account_controller.rb
class AccountsController < ApplicationController
...
def associates
_account_id = params[:account_id]
#rs_account = Account
.select("tab_accounts.account_id, tab_accounts.screen_name, tab_accounts.friends, tab_accounts.followers")
.where(:tab_accounts => {account_id: _account_id})
.as_json[0]
#rs_account['associates'] = Account.select("tab_accounts.account_id, tab_accounts.screen_name")
.joins(:subjects)
.where(:tab_accounts => {account_id: _account_id})
.as_json
render json: #rs_account
end
end
config/routes.rb:
Rails.application.routes.draw do
...
get 'accounts/associates/:account_id', :to => "accounts#associates"
end
When I run the method I get the following error:
PG::UndefinedColumn: ERROR: column mtom_account_relations.account_id does not exist LINE 1: ..._accounts" INNER JOIN "mtom_account_relations" ON "mtom_acco... ^ : SELECT tab_accounts.account_id, tab_accounts.screen_name FROM "tab_accounts" INNER JOIN "mtom_account_relations" ON "mtom_account_relations"."account_id" = "tab_accounts"."account_id" INNER JOIN "tab_accounts" "subjects_tab_accounts" ON "subjects_tab_accounts"."account_id" = "mtom_account_relations"."account_id" WHERE "tab_accounts"."account_id" = $1
I suspect the call to the non-existent table "subjects_tab_accounts" is being created from my .joins(:subjects) clause in the controller.
It thinks there's a "mtom_account_relations"."account_id" column.
I'd be grateful for any actionable assistance. Thank you for your attention.
Joe
Consider this example of a family tree. Since Person can be either in the parent_id or child_id column setting up has_many :relationships relation won't work since we don't know which column to use in the join.
Instead we need to setup separate relationships depending on which foreign key on Relationship we joining and then query through this relationship
class Person
has_many :relationships_as_child,
class_name: 'Relationship'
foreign_key: 'child_id'
has_many :relationships_as_parent,
class_name: 'Relationship'
foreign_key: 'parent_id'
has_many :parents,
through: :relationships_as_child,
source: :parent
has_many :children,
through: :relationships_as_child,
source: :child
end
class Relationship
belongs_to :parent, class_name: 'Person'
belongs_to :child, class_name: 'Person'
end
class AccountRelation < ApplicationRecord
self.table_name = "mtom_account_relations"
belongs_to :subject,
foreign_key: "account_id",
class_name: "Account"
belongs_to :associate, foreign_key: "account_id",
class_name: "Account"
end
class Account < ApplicationRecord
self.table_name = "tab_accounts"
self.primary_key = "account_id"
...
has_many :account_relations_as_subject,
class_name: 'AccountRelation',
foreign_key: 'subject_id'
has_many :account_relations_as_associate,
class_name: 'AccountRelation',
foreign_key: 'associate_id'
has_many :associates,
through: :account_relations_as_subject,
source: :associate
has_many :subjects,
through: :account_relations_as_associate,
source: :subject
end
When learning Rails I would really encourage you to learn to love the conventions when it comes to naming tables, primary keys and columns in general. Don't make it harder on yourself.
When it comes to the controller I would set it up like so:
#routes.rb
resources :accounts do
resources :associates, only: [:index]
end
class AssociatesController
# GET /accounts/:account_id/associates
def index
#account = Account.joins(:associates).find(params[:account_id])
#associates = #account.associates
end
end
This models associates as a RESTful resource which is nested under an account and gives you straight forward and conventional way to add more CRUD actions.
I apologize if this is a big question. I used to be good at rails but it has been a long time.
I am working a rails project and am having trouble with the has_many relation.
I have the following tables:
User
SchoolClass
Question
UserClassQuestion
Inside the models i have:
user.rb
has_many :questions, :through => :user_class_questions
has_many :user_class_questions
school_class.rb
has_many :questions, :through => :user_class_questions
has_many :user_class_questions
question.rb
belongs_to :schoolclass
belongs_to :user
user_class_question.rb
belongs_to :question
So, what I want is to on a users home page, display let them view the questions they have asked with current_user.questions. This works.
But on the SchoolClass show page, if i say #school_class.questions, i get the following error:
!!<ActiveRecord::StatementInvalid: SQLite3::SQLException: no such colum:
user_class_questions.school_class_id: SELECT "questions".* FROM "questions"
INNER JOIN "user_class_questions" ON "questions"."id" =
"user_class_questions"."question_id" WHERE
"user_class_questions"."school_class_id" = ?>
#school_class is an object set by the params
def set_school_class
#school_class = SchoolClass.find(params[:id])
end
The columns in UserClassQuestion table are:
{ user_id: , schoolclass_id: , question_id }
So, do I have something set up wrong?
The error you're experiencing is most likely due to the variation in your column name. In Rails, school_class_id is not the same as schoolclass_id. I think a quick way to work around, is to specify the primary_key/foreign_key in your belongs_to option.
A more lasting solution is to run a migration to rename your column to school_class_id rather than schoolclass_id.
Let me know if I was able to help.
UPDATE
if the belongs_to :school_class is defined on question, then you have access to question.school_class and your school_class model should just have has_many :questions, no need of running to a join table to do that as that would confuse rails a bit. If however you don't want it this way you could comment out the belongs_to :school_class assoication and instead have a has_many :user_class_questions on question
class User
has_many :questions, through: :user_class_questions
has_many :user_class_questions
end
class SchoolClass
has_many :questions, through: :user_class_questions
has_many :user_class_questions
end
class Question
#belongs_to :school_class
belongs_to :user
has_many :user_class_questions
end
class UserClassQuestion
belongs_to :question
belongs_to :school_class
belongs_to :user
end
Your Data model needs a bit of a change, your UserClassQuestion is a join model and it should have data about the User, SchoolClass, and Question. So the relationship should be something like
UserClassQuestion
rails g model UserClassQuestion user:references school_class:references question:references
class UserClassQuestion < ActiveRecord::Base
belongs_to :user
belongs_to :school_class
belongs_to :question
end
school_class.rb
class SchoolClass < ActiveRecord::Base
has_many :user_class_questions
has_many :questions, through: :user_class_questions
end
user.rb
class User < ActiveRecord::Base
has_many :user_class_questions
has_many :questions, through: :user_class_questions
end
And then you can do user.questions and school_class.questions
Also try not to use has_many_and_belongs_to association, I feel it is a bad idea, it creates a table without a primary id, and later on if you want to do something else with this join table, then this will come and bite you. has_many, through is a bit extra step but it is worth it.
I have a permissions model that is based on an individual Store. A store has a member that can view data has_and_belongs_to_many_users, edit data has_and_belongs_to_many_editors and own the Store has_and_belongs_to_many_owners.
If I were to add more permissions in the future, I would have to add more joins. I'm not sure I like this model.
Store has_and_belongs_to_many_users
Store has_and_belongs_to_many_owners -> [Users]
Store has_and_belongs_to_many_editors -> [Users]
An alternative is this, I factor out the role, and create a second join table called "authorized users".
Here is what that could look like:
Store has_and_belongs_to_many_authorized_users
authorized_users: User_id Role_id
How can I use a second join table, that is called "authorized_users" with rails? Looking at my model, I do not have a model called authorized_users, and I am not sure how to build a model that relates to a join table.
essentially my question is, with Rails, How can I join a join table with a join table?
Just answering your question as it is stated, I would prefer to put the roles in the join table myself
class User < ActiveRecord::Base
has_many :store_users, inverse_of: :user, dependent: :destroy
has_many :stores, through: :store_users
end
class Store < ActiveRecord::Base
has_many :store_users, inverse_of: :store, dependent: :destroy
has_many :users, through: :store_users
end
class StoreUser < ActiveRecord::Base
belongs_to :store
belongs_to :user
has_many :permissions, dependent: :destroy, inverse_of: :store_user
validates_presence_of :store, :user
end
class Permission < ActiveRecord::Base
belongs_to :store_user
validates_presence_of :role
end
If you need a join table, that can or should be a model, you want to use has_many :trough association. You can read more about it here: http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
I have model relationships like so:
class User
include Mongoid.Document
has_many :favorite_shows
end
class FavoriteShow
include Mongoid.Document
belongs_to :user
belongs_to :show
end
class Show
include Mongoid.Document
has_many :favorite_shows
end
FavoriteShow is a join table between users and shows with both the user_id and show_id as foreign keys. I keep getting the following error despite the fact that these foreign keys already exist:
Problem:
When adding a(n) Show to User#favorite_shows, Mongoid could not determine the inverse foreign key to set. The attempted key was 'user_id'.
Summary:
When adding a document to a relation, Mongoid attempts to link the newly added document to the base of the relation in memory, as well as set the foreign key to link them on the database side. In this case Mongoid could not determine what the inverse foreign key was.
Resolution:
If an inverse is not required, like a belongs_to or has_and_belongs_to_many, ensure that :inverse_of => nil is set on the relation. If the inverse is needed, most likely the inverse cannot be figured out from the names of the relations and you will need to explicitly tell Mongoid on the relation what the inverse is.
Example:
class Lush
include Mongoid::Document
has_one :whiskey, class_name: "Drink", inverse_of: :alcoholic
end
class Drink
include Mongoid::Document
belongs_to :alcoholic, class_name: "Lush", inverse_of: :whiskey
end
Now I've tried adding both inverse_of: nil to the associations, as well as the following with no luck:
class User
include Mongoid.Document
has_many :favorite_shows, class_name: "FavoriteShow", inverse_of: :user
end
class FavoriteShow
include Mongoid.Document
belongs_to :user, class_name: "User", inverse_of: :favorite_shows
belongs_to :show, class_name: "Show", inverse_of: :favorite_shows
end
class Show
include Mongoid.Document
has_many :favorite_shows, class_name: "FavoriteShow", inverse_of: :favorite_shows
end
I have these relationships working perfectly in ActiveRecord, but when switching over to Mongoid, I'm still unclear how the exact relationship is supposed to be translated. Any help would be really appreciated!
When using a document based database such as MongoDB you don't have a need for join tables like you would with a relational database. I suggest a structure like the one below:
class User
include Mongoid::Document
include Mongoid::Timestamps
has_and_belongs_to_many :favorite_shows, class_name: "Show", inverse_of: :users
end
class Show
include Mongoid::Document
include Mongoid::Timestamps
has_and_belongs_to_many :users, class_name: "User", inverse_of: :favorite_shows
end
I have a model called article with columns : id, title, content, user_id, i want to use association with the same table to say that article can be reshared by other users, so when a user reshare an article i think this is not necessary to copy the whole row of the origianl article, but just copy a reference to it , but how to do this using active record ?
remove the user_id from table articles, add a new association table call user_articles,
class User < ActiveRecord::Base
has_many :user_articles
has_many :articles, :through => :user_articles
end
class Article < ActiveRecord::Base
has_many :user_articles
has_many :user, :through => :user_articles
end
class User < ActiveRecord::Base
belongs_to :user
belongs_to :article
end