I have two models called User and Relations. Relation stores the relationships between users, and there are different types of relationships. At the moment, I am trying to implement two types: friends and friend requests.
Here are my class associations:
class Relation < ActiveRecord::Base
belongs_to :owner, class_name: "User"
validates :owner, presence: true
validates :character, presence: true
end
class User < ActiveRecord::Base
has_many :characters, dependent: :destroy
has_many :relations, foreign_key: "owner", dependent: :destroy
has_many :friends, -> { where reltype: "friend" }, through: :relations, source: "character"
has_many :reverse_relations, foreign_key: "character", class_name: "Relation", dependent: :destroy
has_many :friend_requests, -> { where reltype: "freq" }, through: :reverse_relations, source: :owner
.
.
.
end
In IRB, I am trying to use the User.friends array this is supposed to generate, and this error is thrown:
Loading development environment (Rails 4.0.0)
irb(main):001:0> user = User.find(1)
User Load (12.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
=> #<User id: 1, name: "melv", email: "melv#mx.org", password_digest: "$2a$10$g3FjyVP9ya/L.j1iWAzYH.YFOjYOyxUGp3KIt6ajic
Jf...", verified: nil, reg_ip: nil, last_ip: nil, character_limit: 3, characters: nil, created_at: "2013-10-31 19:10:36"
, updated_at: "2013-11-02 11:05:14", remember_token: "4e883d6ec84b5c142882cc084c14bc101a06350f", playchar: 0>
irb(main):002:0> user.friends
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :character in model
Relation. Try 'has_many :friends, :through => :relations, :source => <name>'. Is it one of :owner?
<...stack trace...>
irb(main):003:0>
Am I doing something wrong?
My schema, as requested:
ActiveRecord::Schema.define(version: 20131102102750) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "characters", force: true do |t|
t.integer "accountid"
t.string "username"
t.integer "roundles"
t.integer "gems"
t.integer "rank"
t.integer "tier"
t.datetime "tiertime"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "permission_ranks", force: true do |t|
t.string "label"
t.integer "badge"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "relations", force: true do |t|
t.integer "owner"
t.integer "character"
t.string "reltype"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "relations", ["character"], name: "index_relations_on_character", using: :btree
add_index "relations", ["owner", "character"], name: "index_relations_on_owner_and_character", unique: true, using: :btree
add_index "relations", ["owner"], name: "index_relations_on_owner", using: :btree
create_table "tickets", force: true do |t|
t.string "title"
t.text "desc"
t.integer "sender"
t.integer "assigned"
t.text "addinfo"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.boolean "verified"
t.string "reg_ip"
t.string "last_ip"
t.integer "character_limit"
t.integer "characters"
t.datetime "created_at"
t.datetime "updated_at"
t.string "remember_token"
t.integer "playchar"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["name"], name: "index_users_on_name", unique: true, using: :btree
add_index "users", ["remember_token"], name: "index_users_on_remember_token", using: :btree
end
OK, I think the problem is with naming. It's not a good idea naming association and column with same name. If it's possible start with renaming columns on relation:
owner => owner_id
character => character_id
Alternatively You can rename associations:
owner => user_owner
character => user_character
Another thing that I see is Your separate characters table. You seem like want to reference characters table from Your Relation model (correct me if I am wrong), but never do it.
belongs_to :user_character, foreign_key: "character"
Now to Your main question. If I understood Your correctly You want to get list of characters, using friends association defined in User. In this case the line should look like this:
has_many :friends, -> { where reltype: "friend" }, through: :relations, source: "user_character", class_name: "Character"
UPDATED
Author explained that he really wants to reference User model through friends association.
# relation.rb
belongs_to :user_character, foreign_key: "character", class_name: "User"
# user.rb
has_many :friends, -> { where reltype: "friend" }, through: :relations, source: "user_character"
UPDATE2
There was a problem with association condition, in particular case You need to specify table explicitly:
has_many :friends, -> { where("relations.reltype" => "friend") }, through: :relations, source: "user_character"
Related
I have the following models
class RecruitmentPhase < ApplicationRecord
belongs_to :cohort, optional: true
belongs_to :parent, class_name: 'Cohort', required: false
has_many :patient_phases, dependent: :destroy, class_name: 'PatientPhase', foreign_key: :phase_id, primary_key: :phase_id
has_many :patients, through: :patient_phases, class_name: 'Patient', foreign_key: :patient_id, primary_key: :patient_id
def phase_name_ret
phase_name
end
end
class PatientPhase < ApplicationRecord
belongs_to :patients, foreign_key: 'patient_id', class_name: 'Patient', primary_key: 'patient_id'
belongs_to :recruitment_phase, foreign_key: :phase_id
self.primary_key = :phase_id
end
class Patient < ApplicationRecord
has_many :patient_phases, foreign_key: 'patient_id', primary_key: :patient_id
belongs_to :patient_phase
has_one :recruitment_phase, through: :patient_phase
end
and my schema is as follows
create_table "recruitment_phases", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.integer "phase_id"
t.string "phase_token"
t.string "phase_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "source_id"
t.bigint "cohort_id"
t.index ["cohort_id"], name: "index_recruitment_phases_on_cohort_id"
t.index ["source_id"], name: "index_recruitment_phases_on_source_id"
end
create_table "patients", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.integer "patient_id"
t.integer "recruitment_code_id"
t.string "disease_code_id"
t.string "sex"
t.integer "yob"
t.date "date_imported"
t.integer "is_exported"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "patient_phase_id"
t.index ["patient_phase_id"], name: "index_patients_on_patient_phase_id"
end
create_table "patient_phases", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.integer "patient_id"
t.integer "phase_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "recruitment_phase_id"
t.index ["recruitment_phase_id"], name: "index_patient_phases_on_recruitment_phase_id"
end
I am trying to perform joins on the tables as follows in rails console
RecruitmentPhase.joins(:patient_phases, :patients).where(:patients => {disease_code_id: 'KIN'}).select('distinct patients.disease_code_id', 'recruitment_phases.phase_name', 'recruitment_phases.phase_id')
And I am getting the translated query to SQL as
SELECT distinct disease_code_id FROM `recruitment_phases`
INNER JOIN `patient_phases` ON `patient_phases`.`phase_id` = `recruitment_phases`.`phase_id`
INNER JOIN `patient_phases` `patient_phases_recruitment_phases_join` ON `patient_phases_recruitment_phases_join`.`phase_id` = `recruitment_phases`.`phase_id`
INNER JOIN `patients` ON `patients`.`patient_id` = `patient_phases_recruitment_phases_join`.`patient_id`
WHERE `patients`.`disease_code_id` = 'KIN' LIMIT 11
But I need the query as follows
SELECT distinct patients.disease_code_id,
recruitment_phases.phase_name, recruitment_phases.phase_id FROM `patients`
join patient_phases on patient_phases.patient_id = patients.patient_id
join recruitment_phases on recruitment_phases.phase_id = patient_phases.phase_id
WHERE (patients.disease_code_id = 'KIN') LIMIT 11
I know some how my associations are not correct, as I am a trying to learn more on such concepts, can you give me a hint on how I can approach the above problem?.
I resolved my question. It was just using the following
RecruitmentPhase.joins(patients: :patient_phases).where(:patients => {disease_code_id: 'KIN'}).select('distinct patients.disease_code_id', 'recruitment_phases.phase_name', 'recruitment_phases.phase_id')
Server log screencap
Hi everyone!
Was writing a rating system for an airBnb style project so i made the objects Host and Guest as reference to the User object.
But something is wrong in my code:
SQLite3::SQLException: no such table: main.hosts
In fact the method looks for host table i dont have cause it should be associated to the Users one.
migration
class CreateReviews < ActiveRecord::Migration[6.0]
def change
create_table :reviews do |t|
t.text :comment
t.integer :star, default: 1
t.references :car, foreign_key: true
t.references :reservation, foreign_key: true
t.references :guest, foreign_key: true
t.references :host, foreign_key: true
t.string :type
t.timestamps
end
end
end
Schema
create_table "reviews", force: :cascade do |t|
t.text "comment"
t.integer "star", default: 1
t.integer "car_id"
t.integer "reservation_id"
t.integer "guest_id"
t.integer "host_id"
t.string "type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["car_id"], name: "index_reviews_on_car_id"
t.index ["guest_id"], name: "index_reviews_on_guest_id"
t.index ["host_id"], name: "index_reviews_on_host_id"
t.index ["reservation_id"], name: "index_reviews_on_reservation_id"
Models:
class HostReview < Review
belongs_to :host, class_name: "User"
end
class User < ApplicationRecord
....
has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id"
I think you can do something like this:
t.references :host, references: :users, foreign_key: true
or alternatively
t.integer :host_id
and then
add_foreign_key :reviews, :users, column: :host_id
I'm creating a rails 5 application (a sort of job finder that connects recruiters with applicants).
Here is a part of my model configuration:
class User < ActiveRecord::Base
has_and_belongs_to_many :tag
end
class Applicant < User
has_many :experience
has_many :match
end
class Recruiter < User
has_one :company
has_many :offer
end
class Experience < ActiveRecord::Base
belongs_to :applicant, :foreign_key => "user_id"
has_one :company
end
And these are extracts from my schema file:
create_table "users", force: :cascade do |t|
t.string "type", null: false
t.string "login", limit: 40, null: false
t.string "password", limit: 500, null: false
t.bigint "company_id"
t.index ["company_id"], name: "index_users_on_company_id"
t.index ["login"], name: "index_users_on_login", unique: true
end
create_table "experiences", force: :cascade do |t|
t.string "job_name", limit: 100, null: false
t.bigint "company_id"
t.bigint "user_id", null: false
t.text "description"
t.index ["company_id"], name: "index_experiences_on_company_id"
t.index ["job_name"], name: "index_experiences_on_job_name"
t.index ["user_id"], name: "index_experiences_on_user_id"
end
add_foreign_key "users", "companies"
add_foreign_key "experiences", "companies"
add_foreign_key "experiences", "users"
An Experience is attached to the model Applicant through the table user (which contain a type field for the STI), this is why I specified "foreign_key => 'user_id'" in Experience model.
My problem is when I try to access at the first experience of an applicant, I get this error:
PG::UndefinedColumn: ERROR: column experiences.applicant_id does not exist LINE 1: SELECT "experiences".* FROM "experiences" WHERE "experiences...
I hope you can help me.
Thanks!
As stated in the docs:
By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix _id added.
Try doing:
class Applicant < User
has_many :experiences, foreign_key: :user_id
has_many :matches
end
Note that it is conventional to use the plural with the has_many association.
BTW, it's no obvious to me why you're using STI, but I'm sure there are good reasons.
In my online shop I have tables Product and Size, also I think I need to add a table Restocking
Instead of updating a product, I guess It's better to have a Restocking table then I could track the dates where I added any new sizes, quantity, and why not the new prices (buying and selling)... and create stats...
Do you this it is correct?
Once a Restocking is created, the corresponding Product is updated with new quantity and price?
Well,
So it started this way:
#Product
has_many :sizes
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
#Size
belongs_to :product
The Restocking table needs to have sizes attributes (like product)
I believe that I have to use polymorphic associations, but how I am supposed to update my schema , what should I add, remove?
So since I added the Restocking model, my models look like this:
#Product
has_many :sizes, inverse_of: :product, dependent: :destroy, as: :sizeable
has_many :restockings
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
#Restocking
has_many :sizes, as: :sizeable
belongs_to :product
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
#Size
belongs_to :product
belongs_to :restocking
belongs_to :sizeable, polymorphic: true, class_name: "Size"
schema.rb
create_table "sizes", force: :cascade do |t|
t.string "size_name"
t.integer "quantity"
t.bigint "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quantity_stock"
t.index ["product_id"], name: "index_sizes_on_product_id"
end
create_table "restockings", force: :cascade do |t|
t.bigint "product_id"
t.bigint "sizeable_id"
t.decimal "price", precision: 10, scale: 2
t.decimal "buying_price", precision: 10, scale: 2
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_restockings_on_product_id"
t.index ["sizeable_id"], name: "index_restockings_on_sizeable_id"
end
create_table "products", force: :cascade do |t|
t.string "title", limit: 150, null: false
t.text "description"
t.bigint "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "color"
t.integer "user_id"
t.json "attachments"
t.string "brand"
t.string "ref"
t.decimal "price"
t.decimal "buying_price", precision: 10, scale: 2
t.index ["category_id"], name: "index_products_on_category_id"
end
At this point I have several errors, like
in ProductsController
def new
#product = Product.new
#product.sizes.build
end
error:
ActiveModel::UnknownAttributeError at /admin/products/new
unknown attribute 'sizeable_id' for Size.
Can you light me on the migrations I have to change?
Suggestions are welcome
You're almost there, to use polymorphic inside your Size model, you have to change the size resource, and add two attributes to the resource: sizeable_id and sizeable_type.
The sizeable_type is a string, indicates the class of the parent element, in your case, can be Product or Restocking, and sizeable_id indicates the element_id to find the parent element, your relations are correct, but you must add this elements to your Size, see the following:
One exemple of a migration to your case:
class AddSizeableToSize < ActiveRecord::Migration
def change
add_reference :sizes, :sizeable, polymorphic: true, index: true
end
end
On your Size model:
# app/models/size.rb
class Size < ActiveRecord::Base
belongs_to :sizeable, polymorphic: true
end
In your Product or Restocking model:
has_many :sizes, as: :sizeable
This is just a simple way to make your case works! If you want to know more about rails associations and polymorphism, can take a look in this link.
I added a couple of foreign keys to my models and to my tables and it has since broken my use of Active Admin. I'm wondering if anyone knows a work around or a fix to this issue.
schmea.rb
create_table "students", primary_key: "student_id", id: :string, force:
:cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "last_name"
t.string "first_name"
t.string "home_address"
t.string "home_city"
t.string "home_state"
t.string "home_zip"
t.string "school_year_address"
t.string "school_year_city"
t.string "school_year_zip"
t.string "room_number"
t.string "home_phone"
t.string "cell_phone"
t.boolean "new_student"
t.boolean "returning_student"
t.string "athletic_team"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "fk_rails_148c9e88f4"
end
add_foreign_key "emergency_contacts", "students", primary_key: "student_id"
add_foreign_key "students", "users"
add_foreign_key "vehicles", "students", primary_key: "student_id"
student.rb Students Model
class Student < ApplicationRecord
self.primary_key = :student_id
belongs_to :user
has_one :emergency_contact
has_one :vehicle
end
I'm getting the error, has anyone found a fix for this?
undefined method `emergency_contact_id_eq' for Ransack::Search<class: Student, base: Grouping <combinator: and>>:Ransack::Search
You have defined the assocation wrong.
class Student < ApplicationRecord
self.primary_key = :student_id
belongs_to :user
belongs_to :emergency_contact, class_name: 'User'
has_one :vehicle
end
belongs_to places the foreign key on this table and is exactly what you want. When joining you want to have the id on this table instead of having to look for records where student_id matches this record.
You also need to make sure to add a foreign key column and the correct foreign key constraint:
class AddEmergencyContactIdToStudents < ActiveRecord::Migration[5.0]
def change
add_reference :students, :emergency_contact, foreign_key: false
add_foreign_key :students, :users, column: :emergency_contact_id,
end
end
I would also strongly advise against using non standard primary keys. Prefixing the PK with student_ gives you nothing but headaches and will confuse other developers.