I have a many-to-many association throught RoomsUsers model and in this model i have a role field, association works well but i can't access this field.
My schema looks like:
create_table "messages", force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "room_id"
end
create_table "rooms", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "rooms_user_id"
end
create_table "rooms_users", force: :cascade do |t|
t.integer "user_id"
t.integer "room_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "role"
t.integer "last_checked"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "mail"
t.integer "rooms_user_id"
end
User model:
class User < ApplicationRecord
has_secure_password(validations: false)
has_many :messages
has_many :rooms_users
has_many :rooms, through: :rooms_users
accepts_nested_attributes_for :rooms_users
attr_accessor :register, :mail_confirmation, :login
end
Room model:
class Room < ApplicationRecord
has_many :rooms_users
has_many :users, through: :rooms_users
accepts_nested_attributes_for :rooms_users
has_many :message
end
RoomsUsers model:
class RoomsUsers < ApplicationRecord
belongs_to :user
belongs_to :room
end
And i am trying to get role field from first user's room.
User.first.rooms.first.role
It give's me NoMethodError (undefined method `role' for #). What's wrong?
You're trying to access role field in the rooms table, but it is in rooms_users table. Should be:
User.first.rooms_users.first.role
And remove rooms_user_id from rooms and users table, you don't need it
If you want to access "role" field through Rooms model, you will need to change the place of your "role" field from rooms_users table to rooms table. Doing it you can access "role" using User.first.rooms.first.role.
However if you want to keep "role" field in rooms_users table, so you will need to use User.first.rooms_users.first.role as Vasilisa has already mentioned.
t.integer "rooms_user_id" are not necessary in rooms and users tables. The has_many used in rooms and users are already linking rooms_users with them.
Related
I don't know if anyone can help me as this is a little odd.
I have a moderately complicated set of relations in a database, which roughly has a structure something like this:
Delivery Director has Account Directors has Pods has Account Managers has Companies.
Therefore, Delivery Directors should have Companies.
This whole structure is working, all the way down to Companies, and then suddenly stops. The Delivery Director returns [] on companies.
class DeliveryDirector < User
has_many :account_directors
has_many :pods, through: :account_directors
has_many :account_managers, through: :pods
has_many :companies, through: :account_managers
end
And the company class looks like:
class Company < ApplicationRecord
belongs_to :account_manager
has_one :pod, through: :account_manager
has_one :account_director, through: :pod
has_one :delivery_director, through: :account_manager
end
Like I say, everything is working. The Company even has a Delivery Director! It's just the DeliveryDirector.all.first.companies returns [].
If anyone could even just point me in the right direction, that would be great. There is no error message, and nothing seems to be going wrong at all.
Oh, in case it helps, here is the SQL generated by the query:
Company Load (0.7ms) SELECT "companies".* FROM "companies" INNER JOIN "users" ON "companies"."account_manager_id" = "users"."id" INNER JOIN "pods" ON "users"."pod_id" = "pods"."id" INNER JOIN "users" "account_directors_companies" ON "pods"."account_director_id" = "account_directors_companies"."id" WHERE "users"."type" IN ('AccountDirector') AND "account_directors_companies"."delivery_director_id" = $1 [["delivery_director_id", 2]]
Thanks!
Edit: Request for other models, schema
Pod:
class Pod < ApplicationRecord
belongs_to :account_director
has_many :account_managers
has_many :companies, through: :account_managers
end
Account Manager:
class AccountManager < User
belongs_to :pod
has_one :account_director, through: :pod
has_one :delivery_director, through: :account_director
has_many :companies
end
Schema:
ActiveRecord::Schema.define(version: 2018_10_19_141416) do
enable_extension "plpgsql"
create_table "companies", force: :cascade do |t|
t.string "name"
t.string "officelocation"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "campaign_link"
t.string "company_logo"
t.string "website"
t.integer "account_manager_id"
end
create_table "images", force: :cascade do |t|
t.string "name"
t.string "location"
t.bigint "company_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["company_id"], name: "index_images_on_company_id"
end
create_table "jwt_blacklist", id: :serial, force: :cascade do |t|
t.string "jti", null: false
t.index ["jti"], name: "index_jwt_blacklist_on_jti"
end
create_table "markets", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "markets_users", id: false, force: :cascade do |t|
t.bigint "market_id", null: false
t.bigint "talent_manager_id", null: false
end
create_table "pods", force: :cascade do |t|
t.string "name"
t.integer "account_director_id"
t.integer "delivery_director_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "table_campaigns", force: :cascade do |t|
t.bigint "user_id"
t.bigint "company_id"
t.string "name"
t.integer "iterations"
t.integer "interviews"
t.index ["company_id"], name:
"index_table_campaigns_on_company_id"
t.index ["user_id"], name: "index_table_campaigns_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "jobtitle"
t.string "linkedin"
t.string "office"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "type"
t.integer "team_lead_id"
t.integer "delivery_director_id"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.bigint "pod_id"
t.string "user_photo"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["pod_id"], name: "index_users_on_pod_id"
t.index ["reset_password_token"], name:
"index_users_on_reset_password_token", unique: true
end
add_foreign_key "table_campaigns", "companies"
add_foreign_key "table_campaigns", "users"
end
And now adding Account Director:
class AccountDirector < User
belongs_to :delivery_director
has_one :pod
has_many :account_managers, through: :pod
has_many :companies, through: :account_managers
end
You use Single Table Inheritance. 3 of your models: DeliveryDirector, AccountDirector and AccountManager are descendants of User model. When doing shallow request it works fine, but when you construct requests which involve all 3 models Rails cannot build the right query. If you try to project how to find all companies of a delivery director in terms of database, you will come to the chain of tables:
companies -> users (account managers) -> pods -> users (account directors) -> users (delivery directors)
The SQL query for your request may look like:
SELECT companies.* FROM companies
INNER JOIN users AS account_managers ON companies.account_manager_id = account_managers.id
INNER JOIN pods ON account_managers.pod_id = pods.id
INNER JOIN users AS account_directors ON pods.account_director_id = account_directors.id
INNER JOIN users AS delivery_directors ON account_directors.delivery_director_id = delivery_directors.id
WHERE delivery_directors.id = 2;
but obviously, Rails does not add AS clause to the query to distinguish user roles and uses users table name instead. To filter results it uses condition "users"."type" IN ('AccountDirector') which is not enough in your case, because in your query there should be also AccountManager (as a link between pods and companies).
Another sign that Rails is confused: despite correct association in your models Rails tries to use table account_directors_companies which you obviously do not have.
I would recommend to review your database schema and extract user roles and relationship between them into separate substances.
UPDATE:
For example, user authentication/registration data can be left in users table as it is now. All info about user roles and their relations can be moved to extra tables, backed up by models:
class DeliveryDirector < ApplicationRecord
belongs_to :user
has_many :account_directors
has_many :pods, through: :account_directors
has_many :account_managers, through: :pods
has_many :companies, through: :account_managers
end
class AccountDirector < ApplicationRecord
belongs_to :user
has_one :pod
has_many :account_managers, through: :pod
has_many :companies, through: :account_managers
end
class AccountManager < ApplicationRecord
belongs_to :user
has_many :companies
end
Each of these models has their own table in the database.
Thus, to fetch companies of delivery director you could call:
DeliveryDirector.find_by(user_id: user_id).companies
create_table "addresses", force: :cascade do |t|
t.integer "user_id"
t.string "code_postal"
t.string "street_name"
t.string "street_number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["code_postal"], name: "index_addresses_on_code_postal"
t.index ["user_id"], name: "index_addresses_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name_first"
t.string "name_last"
t.date "date_birth"
t.string "address_email"
t.integer "address_primary_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
A user can have many addresses, but a user can only have one primary address.
How do I add a foreign key constraint on address_primary_id?
I'm assuming your associations look like this:
class User < ApplicationRecord
belongs_to :address_primary, class_name: Address
has_many :addresses
end
class Address < ApplicationRecord
belongs_to :user
has_one :user_as_primary, class_name: User, foreign_key: :address_primary_id
end
You can create a foreign key you want in a migration with this line:
add_foreign_key :users, :addresses, column: :address_primary_id
Here are the docs on foreign keys in migrations.
I am making a Band application where a Venue has many Events and Bands through Events.
I realized that in my form for creating an event can only hold one band_id
but I want to have many bands because it only makes sense to do so.
This is my Schema
ActiveRecord::Schema.define(version: 20170817180728) do
create_table "bands", force: :cascade do |t|
t.string "name"
t.string "genre"
t.string "image"
t.boolean "explicit_lyrics"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "events", force: :cascade do |t|
t.string "name"
t.text "date"
t.boolean "alcohol_served"
t.string "image"
t.integer "venue_id"
t.integer "band_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "venues", force: :cascade do |t|
t.string "name"
t.string "city"
t.string "state"
t.boolean "family_friendly"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
These are my models
class Venue < ApplicationRecord
has_many :events
has_many :bands, through: :events
end
class Event < ApplicationRecord
belongs_to :venue
belongs_to :band
end
class Band < ApplicationRecord
has_many :events
end
I am fairly new to rails this is a practice web app. I want to be able to be able to show multiple band_ids to my event.
Would I just keep repeating t.band_id in my form??
You'll want to specify a foreign key relationship in your migration that reflects the Active Record associations you've set up in your models using belongs_to instead of a data type. This way, you'll get a references from one table to another, or in your case, from one table to two others, which is how one table with two one-to-many relationships is set up.
class CreateEvents < ActiveRecord::Migration
def change
create_table :venues do |t|
t.string :name
t.string :city
t.string :state
t.boolean :family_friendly
t.string :image
t.timestamps
end
create_table :bands do |t|
t.string :name
t.string :genre
t.string :image
t.boolean :explicit_lyrics
t.timestamps
end
create_table :events do |t|
t.belongs_to :venue, index: true # Look here!
t.belongs_to :band, index: true # and here!
t.string :name
t.text :date
t.boolean :alcohol_served
t.string :image
t.timestamps
end
end
end
In my app User can have many Companies and vice versa. In Accounts table id of User and id of its Company is stored.
I want to find all Users who belong to Companies, which belong to current_user. Let's assume that the current_user is like master User (not Admin, as that would be system Admin) of those companies.
How do I do this? My guess is to do it with Arel, but then how should it look in Model, Controller, View? Many thanks for any help. I'm on Rails 5.
models/user.rb
class User < ApplicationRecord
has_many :accounts, dependent: :destroy
has_many :companies, through: :accounts
models/account.rb
class Account < ApplicationRecord
belongs_to :company
belongs_to :user
accepts_nested_attributes_for :company, :user
models/company.rb
class Company < ApplicationRecord
has_many :accounts, dependent: :destroy
has_many :users, through: :accounts
accepts_nested_attributes_for :accounts, :users
My schema.rb looks like this:
create_table "accounts", force: :cascade do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["company_id"], name: "index_accounts_on_company_id"
t.index ["user_id"], name: "index_accounts_on_user_id"
end
create_table "companies", force: :cascade do |t|
t.string "name"
t.string "legal_name"
t.string "reg_number"
t.string "address"
t.string "bank_acc"
t.string "description"
t.string "website"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "role", default: 0
t.integer "currency", default: 0
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.index ["email"], name: "index_users_on_email", unique: true
end
You can find current user's companies, and eager load users who belong to those companies
#companies = current_user.companies.includes(:users)
To list all users(may be in a view), loop through #companies and all its users
#companies.each do |company|
company.users.each do |user|
puts user.name
end
end
Or use map/collect to assign them to a variable.
#users = #companies.map(&:users).flatten
Hello I do have this two models and I would like to check that my model associations are working the way it should trough rails console.
I am not able to do the association work. The relationship is the following:
One Event has one rule and one rule belongs to one event. It could not be a rule without an event and it could not be a event without a rule.
Any idea how to test this with rails console?
MODEL 1:
class Event < ActiveRecord::Base
has_and_belongs_to_many :users
has_one :rule
has_many :grand_prixes
belongs_to :eventable, polymorphic: :true
end
MODEL 2
class Rule < ActiveRecord::Base
belongs_to :events
end
Rules' Schema:
create_table "rules", force: :cascade do |t|
t.boolean "abs"
t.boolean "tc"
t.boolean "allow_auto_clutch"
t.boolean "allow_sc"
t.boolean "allow_throttle_blip"
t.boolean "dynamic_track"
t.integer "damage_mult"
t.integer "fuel_rate"
t.integer "tyre_wear_rate"
t.integer "quali_percentage"
t.integer "min_valid_laps"
t.integer "event_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "rules", ["event_id"], name: "index_rules_on_event_id"
Events' Schema:
create_table "events", force: :cascade do |t|
t.string "event_type"
t.string "name", null: false
t.datetime "starting_date"
t.datetime "ending_date"
t.integer "eventable_id"
t.string "eventable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "events", ["eventable_type", "eventable_id"], name: "index_events_on_eventable_type_and_eventable_id"
Thanks in advance.
I think your belongs_to :events should be singular to follow the rails convention :
class Rule < ActiveRecord::Base
belongs_to :event
end
The conventional name of a relation is always singular for belongs_to and has_one, and always plural for has_many.
Related documentation : http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference
EDIT : There much left to say
You wrote :
ev = Event.create(:name "test1").save
rule = Rule.create.save
create is already a new followed by a save. No need to save afterwards.
the syntax key: value is something very common in ruby, and should be well understood : you're actually writing a hash, equivalent to {:key => value}, but the syntax allows you to write key: value ONLY IF your key is a Symbol.
the columns eventable_type and eventable_id should be in the table rules, who is hosting the polymorphic relation with eventable things. Event should not have these columns, and event_id should not exist at all in rules.
Here's an example of what you can write in your console to create an Event and a Rule :
ev = Event.create(name: "test1")
rule = Rule.create(abs: true, event: ev)
Change your code:
class Rule < ActiveRecord::Base
belongs_to :event
end
With belongs_to you should use singular like event not events.
In console you can check association like:
Event.first.rule if Event.first.present?
For more details you should go through http://guides.rubyonrails.org/association_basics.html documentation.
Current code:
class Rule < ActiveRecord::Base
belongs_to :event
end
class Event < ActiveRecord::Base
has_and_belongs_to_many :users
has_one :rule
has_many :grand_prixes
belongs_to :eventable, polymorphic: :true
end
SCHEMA:
create_table "rules", force: :cascade do |t|
t.boolean "abs"
t.boolean "tc"
t.boolean "allow_auto_clutch"
t.boolean "allow_sc"
t.boolean "allow_throttle_blip"
t.boolean "dynamic_track"
t.integer "damage_mult"
t.integer "fuel_rate"
t.integer "tyre_wear_rate"
t.integer "quali_percentage"
t.integer "min_valid_laps"
t.integer "event_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "rules", ["event_id"], name: "index_rules_on_event_id", unique: true
create_table "events", force: :cascade do |t|
t.string "event_type"
t.string "name", null: false
t.datetime "starting_date"
t.datetime "ending_date"
t.integer "eventable_id"
t.string "eventable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "events", ["eventable_type", "eventable_id"], name: "index_events_on_eventable_type_and_eventable_id"
Tested on console:
ev = Event.create(:name "test1").save
rule = Rule.create.save
No idea how to link it both through console.