Having three models: Datum, Author, and Book .
class Datum < ApplicationRecord
has_many :authors
end
class Book < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
belongs_to :datum
has_many :books, dependent: :destroy
end
For exercise purpose, I wanted to model it that Datum(more general), can have many authors, which can have books.
After creating a datum object and an associated author for it, I could call nameofdatum.authors, but if I added a book to that author, it could not be recognized through nameofdatum.authors.books. Am I having wrong expectations ? (Should this be done with 'through'(an explanation of it would be much appreciated)
(Schema here if needed)
create_table "authors", force: :cascade do |t|
t.string "name"
t.integer "age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "book_id"
t.integer "datum_id"
t.index ["book_id"], name: "index_authors_on_book_id"
t.index ["datum_id"], name: "index_authors_on_datum_id"
end
create_table "books", force: :cascade do |t|
t.string "name"
t.string "book_type"
t.integer "pages"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "author_id"
t.index ["author_id"], name: "index_books_on_author_id"
end
create_table "data", force: :cascade do |t|
t.string "region"
t.integer "budget"
t.date "aval"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Should this be done with 'through'?
Yes, Datum has_many books through the authors assocition:
class Datum < ApplicationRecord
has_many :authors
has_many :books, through: :authors
end
And the books can be selected via:
Datum.last.books
It's actually selects books using the following query:
SELECT "books".* FROM "books" INNER JOIN "authors" ON "authors"."id" = "books"."author_id" WHERE "authors"."datum_id" = ?
If you want to add a new book through author, you have to assign an author. So you can try:
nameofdatum.author.books.build ....
your codenameofdatum.authors.books, you can't use a plural(author) to add a new book.
Hope to help you.
Related
I want to fetch sku_code from products, wh_name from warehouses table and item_count from product_warehouses.
I tried something like
Product.all.includes(:product_warehouses)
But not working :(
Below are the schema of my tables
create_table "product_warehouses", force: :cascade do |t|
t.integer "product_id"
t.integer "warehouse_id"
t.integer "item_count"
t.integer "threshold"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_product_warehouses_on_product_id"
t.index ["warehouse_id"], name: "index_product_warehouses_on_warehouse_id"
end
create_table "products", force: :cascade do |t|
t.string "sku_code"
t.string "name"
t.decimal "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "warehouses", force: :cascade do |t|
t.string "wh_code"
t.string "wh_name"
t.string "pin"
t.integer "max_cap"
t.integer "threshold"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Below are the relationship between tables:
class Product < ApplicationRecord
has_many :product_warehouses
has_many :warehouses, through: :product_warehouses
end
class ProductWarehouse < ApplicationRecord
belongs_to :product
belongs_to :warehouse
end
class Warehouse < ApplicationRecord
has_many :product_warehouses
has_many :products, through: :product_warehouses
end
If you want to load all three records with a single query, use eager_load:
Product.all.eager_load(:product_warehouses, :warehouses)
Let's say you want to print sku_code, wh_name, and item_count in the console. First load all the products into variable:
products = Product.all.eager_load(:product_warehouses, :warehouses)
Then loop through the records and print out each of the values:
products.each do |product|
puts "sku_code: #{product.sku_code}"
product.product_warehouses.each do |product_warehouse|
puts "item_count: #{product_warehouse.item_count}"
puts "wh_code: #{product_warehouse.warehouse.wh_code}"
end
end
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
I have a problem, I want to create an hashtags system, but when I run my code, and when I want to create a travel that contain hashtags I have this error :
ActiveRecord::StatementInvalid in TravelsController#create
Could not find table 'tags_travels'
Here is my travel.rb
class Travel < ApplicationRecord
has_many :posts
belongs_to :user
has_and_belongs_to_many :tags
#after / before create
after_create do
travel = Travel.find_by(id: self.id)
sh = self.hashtags.scan(/#\w+/)
sh.uniq.map do |s|
tag = Tag.find_or_create_by(name: s.downcase.delete('#'))
travel.tags << tag
end
end
before_update do
travel = Travel.find_by(id: self.id)
travel.tags.clear
sh = self.hashtags.scan(/#\w+/)
sh.uniq.map do |s|
tag = Tag.find_or_create_by(name: s.downcase.delete('#'))
travel.tags << tag
end
end
end
my tag.rb
class Tag < ApplicationRecord
has_and_belongs_to_many :travels
end
the schema.rb file (just table concerned) :
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "topics", force: :cascade do |t|
t.string "title"
t.string "text"
t.string "end_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
create_table "travels", force: :cascade do |t|
t.string "name"
t.string "trip_type"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "hashtags"
end
create_table "travels_tags", id: false, force: :cascade do |t|
t.integer "travel_id"
t.integer "tag_id"
t.index ["tag_id"], name: "index_travels_tags_on_tag_id"
t.index
["travel_id"], name: "index_travels_tags_on_travel_id"
end
Someone has a solution ? Thank !
Rails looks for join tables in a specific syntax. Its trying to find tags_travles but uouve created it with travels_tags.
Change your model associations to specify the join table.
has_and_belongs_to_many :travels, :join_table => :travels_tags
And
has_and_belongs_to_many :tags, :join_table => :travels_tags
Heres some info from the docs to help explain the defsult behaviour for join table naming.
"By default, the name of the join table comes from the union of the first two arguments provided to create_join_table, in alphabetical order."
http://edgeguides.rubyonrails.org/active_record_migrations.html#creating-a-join-table
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.
I am using rails 4.2.0. and I am getting this error:
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :taggings in model Article
Here are my models:
class Tag < ActiveRecord::Base
has_many :taggings
has_many :articles, :through => :taggings
end
class Article < ActiveRecord::Base
has_many :comments
has_many :taggings
has_many :tags, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :article
end
Tagging is an intermediary model for the many-to-many relationship between Article and Tag.
And if it helps, my schema:
ActiveRecord::Schema.define(version: 20150224161732) do
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "comments", force: :cascade do |t|
t.string "author_name"
t.text "body"
t.integer "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "comments", ["article_id"], name: "index_comments_on_article_id"
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "taggings", ["article_id"], name: "index_taggings_on_article_id"
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id"
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
I run this code in rails console to test my associations:
a = Article.first
a.tags.create name: "cool"
And I get the above error.
I have seen similar questions where the response "if you have through: :x, you have to have has_many :x first," but I don't think that is my issue.
This might be a silly question, but have you tried creating a Tagging independent of the Article model? If you haven't, than it could be something messed up with the database not having the Tagging model. Otherwise, associations look fine and should work. The only other thing I can think of is incorrect file names for your models folder