Which relationship is good to use STI or Polymorphism? - ruby-on-rails

I have read about them but still not clear to me which one I suppose to use and how.
I have User model, Message model and Place model
Message model:
class Message < ActiveRecord::Base
belongs_to :user
end
Messages Table:
create_table "messages", force: true do |t|
t.string "title"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
User model:
class User < ActiveRecord::Base
has_many :messages
end
Users Table:
create_table "users", force: true do |t|
t.string "email"
t.string "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
t.string "username"
end
Now, what I want to do is:
"USER" says "MESSAGES" from "PLACES"
eg. "AHMED" says "HELLO" from "EARTH"
For me both Models (Message and Place) have same data (data type) and same behaviours. So places table should be:
create_table "places", force: true do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Now may be I'm confused or making big deal than it should be.
What kind of relation should Message and Place have? should it be STI or Polymorphism?
How should I decide?
I'd appreciate the thinking process of how and why I decide specific association.

This example, despite Messages and Places having the same data, doesn't seems a STI/Polymorphism scenario and they should have two different tables.
This could work as a solution:
create_table "users" do |t|
t.string "username"
end
create_table "messages" do |t|
t.string "text"
t.integer "user_id"
t.integer "place_id"
end
create_table "places" do |t|
t.string "name"
end
class User < ActiveRecord::Base
has_many :messages
has_many :places, through: :messages
end
class Place < ActiveRecord::Base
end
class Message < ActiveRecord::Base
belongs_to :user
belongs_to :place
def to_s
"#{user.username} says #{title} from #{place.name}"
end
end
ahmed = User.new(username: "AHMED")
earth = Place.new(name: "EARTH")
message = Message.new(text: "HELLO", user: ahmed, place: earth)
puts message
# => "AHMED says HELLO from EARTH"

Related

has_one association not adding methods to belongs_to class

I have two tables User and UserToken. User has_one: token and UserToken belongs_to :user. I was under the impression this would add UserToken#User method to the UserToken class. However, I am getting:
undefined method 'user' for '#' with the
following 'UserToken.where(user_id: 1).user
Do I not understand the association correctly or have I not set it up right?
UsersController:
def get
user = UserToken.where(user_id: 1).user
render json: user.to_json
end
User Model:
class User < ApplicationRecord
has_one :user_token
end
UserToken Model:
class UserToken < ApplicationRecord
belongs_to :user
end
Migration:
def change
create_table :users do |t|
# This id comes from Auth0
t.datetime :date
t.timestamp :updated_at
t.timestamp :created_at
end
create_table :user_tokens do |t|
t.belongs_to :user
# This comes from plaid when a user signs in
t.string :token
t.timestamp :updated_at
t.timestamp :created_at
end
end
Schema:
ActiveRecord::Schema.define(version: 2019_09_19_004350) do
create_table "user_tokens", force: :cascade do |t|
t.bigint "user_id"
t.string "token"
t.datetime "updated_at"
t.datetime "created_at"
t.index ["user_id"], name: "index_user_tokens_on_user_id"
end
create_table "users", force: :cascade do |t|
t.datetime "date"
t.datetime "updated_at"
t.datetime "created_at"
end
end
What you want is:
UserToken.where(user_id: 1).first.user
or better yet:
UserToken.find_by(user_id: 1).user
You're getting the "undefined method" error because #where returns an ActiveRecord::Relation and an ActiveRecord Relation has no #user method.
UserToken.where(user_id: 1).class.name
#=> "ActiveRecord::Relation"
UserToken.where(user_id: 1).first.class.name
#=> "UserToken"

Rails one to many through a relationship table

I have a Company that has many Users through a join table company_user. Each user should work for only one Company. This is a 1 to many relationship.
I have looked around for this and found the solution in https://stackoverflow.com/a/7080017/883102
But I get the error
PG::UndefinedTable: ERROR: relation "companies" does not exist
LINE 5: WHERE a.attrelid = '"companies"'::regclass
When I try to create a Company. How can I solve this?
My models are
Company
class Company < ActiveRecord::Base
has_many :employments
has_many :users, :through => :employments
end
Users
class User < ActiveRecord::Base
...
end
Employment
class Employment < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
The migration for my join table is
create_table :employment do |t|
t.belongs_to :company
t.belongs_to :user
t.timestamps
end
My schema.rb
create_table "company", force: true do |t|
t.integer "rating"
t.integer "phone"
t.string "name"
t.string "address"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "employment", id: false, force: true do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_digest"
t.string "remember_token"
t.string "role"
end
Hi I found the answer here
https://stackoverflow.com/a/24318236/883102
The problem was that my table names were in the singular form, I changed these in the migration and then re-created the database. It all seems to be working fine now.
My User class ended up as
class User < ActiveRecord::Base
has_one :employment
has_one :company, :through => :employment
end
This was to allow bi-directional associations

Error in rails saving attribute to object

I have 2 models: User and UserLvl.
class User < ActiveRecord::Base
has_one :user_lvl, primary_key: 'user_lvl_id', foreign_key: 'id'
end
class UserLvl < ActiveRecord::Base
belongs_to :user
end
Controller action
def change_lvl
#user.user_lvl = UserLvl.first
#user.save
end
UserLvl.first is returned fine,with id and all but it failes at the first line with : "Column 'id' cannot be null"
why is this happening?
EDIT:
schema.rb
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.integer "user_lvl_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "user_lvls", force: true do |t|
[omited some information]
t.datetime "created_at"
t.datetime "updated_at"
end
You probably want to use a belongs_to association rather than a has_one, like this:
class User < ActiveRecord::Base
belongs_to :user_lvl
end
Your foreign key is placed in the users table, which makes User the associated model, that belongs to the UserLvl.

Validating the presence of a column on a join model

I think this is a fairly common issue but I couldn't find a solution. My domain has two models, Company and ExternalLink (a representation of a social media provider like Facebook or Twitter). The join model, CompaniesExternalLink, has the direct URL to a social media account (ie http://www.facebook.com/goldmansachs). I need to validate the presence of that URL, but the join is created on the association, which breaks and returns ActiveRecord::RecordInvalid
Any advice would be great. Sample code below:
class Company < ActiveRecord::Base
has_many :companies_external_links
has_many :external_links, through: :companies_external_links, dependent: :destroy
end
class ExternalLink < ActiveRecord::Base
has_many :companies_external_links
has_many :companies, through: :companies_external_links, dependent: :destroy
end
class CompaniesExternalLink < ActiveRecord::Base
validates :url, presence: true
belongs_to :company
belongs_to :external_link
validates :external_link, uniqueness: {scope: :company_id}
end
The relevant parts of the schema.rb
create_table "companies", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.string "state"
t.string "logo"
t.string "slug"
t.text "description"
end
create_table "companies_external_links", force: true do |t|
t.integer "company_id"
t.integer "external_link_id"
t.string "url"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "external_links", force: true do |t|
t.string "source"
t.datetime "created_at"
t.datetime "updated_at"
t.string "icon"
end

TypeError Conversion on has_many relationship

I have a couple of objects in a Rails app ("Ticket", and "Comment")
class Ticket < ActiveRecord::Base
has_many :attributes
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :ticket
belongs_to :user
end
with the following schema:
create_table "comments", :force => true do |t|
t.integer "ticket_id"
t.integer "user_id"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "tickets", :force => true do |t|
t.integer "site_id"
t.integer "status"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
However, for some reason - whenever I do a #lead.comments I get a crash:
can't convert String into Integer
Any ideas? This is driving me nuts!
I think the line that's causing you pronlems is:
has_many :attributes
"attributes" is a special word in an Active Record. It refers to the values of the columns in the db.
If you try and override this with an association, then you will have problems.
My suggestion is that you should not have a model called an "attribute" - call it something else, eg "properties", and the problems will go away.

Resources