This is not a duplicate of 'How do I explicitly specify a Model's table-name mapping in Rails?', as I am looking for a way to not explicitly set the table names. Instead I want to automatically set the table names accordingly to the inheritance of a model.
I've got the following model structure:
models
First (inheritance)
Second (inheritance)
Third (model)
User (model)
Second (model)
I have the following tables in the database:
first_second_thirds
users
user_seconds
When I execute the following code:
First::Second::Third.create(id: 1)
User.create(id: 1)
User::Second.create(id: 1)
I get that error:
ActiveRecord::StatementInvalid: Could not find table 'thirds'
How do I tell the model the appropriate database table?
EDIT: Adding Code Examples
db/migrate/20170305200503_create_first_second_third.rb
class CreateFirstSecondThirds < ActiveRecord::Migration[5.0]
def change
create_table :first_second_thirds do |t|
t.timestamps
end
end
end
db/migrate/20161114164114_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.timestamps
end
end
end
db/migrate/20170305145804_create_user_stories.rb
class CreateUserSeconds < ActiveRecord::Migration[5.0]
def change
create_table :user_seconds do |t|
t.timestamps
end
end
end
app/models/first/second/third.rb
class User < ApplicationRecord
end
app/models/user.rb
class User < ApplicationRecord
end
app/models/user/second.rb
class User < ApplicationRecord
end
Related
I've had some experience with some self projects in Rails and I'm having some trouble creating the data model for this specific scenario. Basically, there are many users, each of whom can play many instruments, and for each user/instrument pairing, there is a certain skill level associated with it.
For example, Joe can play saxophone with skill level 5, clarinet with skill level 2, and trumpet with skill level 3. Bob can play trombone with skill level 1, saxophone with skill level 4, and clarinet with skill level 5.
I understand how to make this with traditional SQL, but I really want to be able to take advantage of the Rails ActiveRecord features (so, in theory, I could do something like this:
#users = User.all
#users.each do |user|
user.instruments do |ins|
puts ins.level #The current user's skill level on a particular instrument
end
end
How do I create the migrations/models to achieve this? Thanks!
the models are basically:
class User < ActiveRecord::Base
has_many :instrument_skills
has_many :instruments, through: :instrument_skills
end
class InstrumentSkill < ActiveRecord::Base
belongs_to :instrument
end
class Instrument < ActiveRecord::Base
end
that said you could create the models like::
rails g model User name:string
rails g model InstrumentSkill instrument_id:integer user_id:integer level:integer
rails g model Instrument name:string
then your generated migration probably look like:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
class CreateInstruments < ActiveRecord::Migration
def change
create_table :instruments do |t|
t.string :name
t.timestamps
end
end
end
class CreateInstrumentSkills < ActiveRecord::Migration
def change
create_table :instrument_skills do |t|
t.integer :user_id
t.integer :instrument_id
t.integer :level
t.timestamps
end
end
end
I'm trying to add product attachment functionality to a Spree store. E.g. a product has many attached documents: brochures, instruction manuals, etc. I can't get the relationship between documents and products to work.
I can use the Paperclip gem for the attachment functionality since Spree already uses it for images.
I have the "document" model: models/spree/document.rb:
class Spree::Document < ActiveRecord::Base
belongs_to :products, class_name: "Spree::Product"
has_attached_file :pdf
end
Then I try to relate the document model to the Spree::Product model in models/spree/product_decorator.rb:
Spree::Product.class_eval do
has_many :documents, dependent: :destroy
end
Then I add migrations:
class CreateDocuments < ActiveRecord::Migration
def change
create_table :spree_documents do |t|
t.timestamps
end
end
end
class AddPdfToDocuments < ActiveRecord::Migration
def self.up
add_attachment :spree_documents, :pdf
end
def self.down
remove_attachment :spree_documents, :pdf
end
end
Now I go into the rails console to see if it worked:
#=> prod = Spree::Product.first
#=> prod.document
#=> PG::UndefinedColumn: ERROR: column spree_documents.product_id does not exist
#=> LINE 1: ..."spree_documents".* FROM "spree_documents" WHERE "spree_doc...
^
#=> : SELECT "spree_documents".* FROM "spree_documents" WHERE "spree_documents"."product_id" = $1
Seems like I'm not defining the relationship between documents and products correctly, but I'm not sure what the issue is.
It looks like you never added a product_id column to your Spree::Documents table. When you define a model belongs_to another model, it tells ActiveRecord that the first one will be a [relation]_id column in its table.
You just need to make sure to add t.references :product in your migration, so it'd look like:
class CreateDocuments < ActiveRecord::Migration
def change
create_table :spree_documents do |t|
t.references :product
t.timestamps
end
end
end
I have 3 tables: proposals, items/proposals (items is nested inside proposals) and invoices.
I want to create invoices for those items in the proposals that got approved. How would the associations for these look like? Also, how would I set up the invoices form to choose only those items that got approved by the client?
Consider creating two different line items models for Proposal and Invoice.
class Proposal < ActiveRecord::Base
has_many :proposal_line_items
end
class ProposalLineItem < ActiveRecord::Base
belongs_to :proposal
end
class Invoice < ActiveRecord::Base
has_many :invoice_line_items
end
class InvoiceLineItem < ActiveRecord::Base
belongs_to :invoice
end
You can consider having an "approved" attribute in proposal line items. In the invoice form, you can show proposal line items approved by the client.
The suggestion of having separate line items for Proposal and Invoice is based on ERP data modeling principles to maintain the integrity of Invoice.
Update
For example here are the sample migrations for the models suggested
class CreateProposalLineItems < ActiveRecord::Migration
def change
create_table :proposal_line_items do |t|
t.references :proposal, index: true, foreign_key: true
t.string :name
t.integer :approved
t.timestamps null: false
end
end
end
class CreateProposals < ActiveRecord::Migration
def change
create_table :proposals do |t|
t.string :name
t.timestamps null: false
end
end
end
class InvoicesController < ActionController
def new
#approved_items = Proposal.find(params[:proposal_id]).proposal_line_items.where(:approved => 1)
end
end
You can iterate over the #approved_items in your view and display it to users.
V
I've been racking my head around a rails issue for a while and wanted to verify my findings. I was trying to get the Has_and_belongs_to_many relationship working, but couldn't connect my two classes, auctionItem and category. First of all, here was my migration file and the two classes before solving the issue:
Migration file:
class AuctionItemsCategories < ActiveRecord::Migration
def up
create_table 'auction_items_categories', :id=>false do |t|
t.reference :auctionItem_id
t.references :category_id
end
end
def down
drop_table 'auction_items_categories'
end
end
Category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :auction_items
end
auction_item.rb
class AuctionItem < ActiveRecord::Base
has_and_belongs_to_many :categories
end
After creating an instance of AuctionItem, I tried
auction_item = AuctionItem.last
auction_item.categories
...and got the following error:
NoMethodError: undefined method `categories' for #<AuctionItem:0x0000010521b870>
After some research, I found adding the specific class to the has_and_belongs_to_many helped:
Category Model
has_and_belongs_to_many :auction_items , :class_name => 'AuctionItem'
auction_item Model
has_and_belongs_to_many :categories , :class_name => 'Category'
This solved that issue and I was able to access the categories table. I went on to try to append a category to the auction item:
auction_item.categories << category
I then got received the following error:
SELECT "auction_items".* FROM "auction_items" INNER JOIN "auction_items_categories" ON "auction_items"."id" = "auction_items_categories"."auction_item_id" WHERE "auction_items_categories"."category_id" = 2
SQLite3::SQLException: no such column: auction_items_categories.auction_item_id: SELECT "auction_items".* FROM "auction_items" INNER JOIN "auction_items_categories" ON "auction_items"."id" = "auction_items_categories"."auction_item_id" WHERE "auction_items_categories"."category_id" = 2
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: auction_items_categories.auction_item_id: SELECT "auction_items".* FROM "auction_items" INNER JOIN "auction_items_categories" ON "auction_items"."id" = "auction_items_categories"."auction_item_id" WHERE "auction_items_categories"."category_id" = 2
If you notice, the query is trying to get auction_item.id rather than AuctionItem.id. To get the connection to work, I had to change my migration file to the following:
class AuctionItemsCategories < ActiveRecord::Migration
def up
create_table 'auction_items_categories', :id=>false do |t|
t.integer :auction_item_id
t.integer :category_id
end
end
def down
drop_table 'auction_items_categories'
end
end
So long story short/TL DR version: For me, it seems that when naming your class with multiple words and using camel case, rails does not singularize your pluralized class name back to it's original state if it has an underscore. So for example, my class name was AuctionItem which became auction_items for the model. Rather than search for auctionitem.id, the sql call that was looked for was auction_item.id, which is the singularize version of auction_items. Why didn't it search for auctionitem.id? In the future, when I am making association tables with multi word classes, do I use the singular underscore id version of the model name?
Your original migration was incorrect. It should have been as follows:
class AuctionItemsCategories < ActiveRecord::Migration
def up
create_table 'auction_items_categories', :id => false do |t|
t.references :auction_item
t.references :category
end
end
def down
drop_table 'auction_items_categories'
end
end
You should specify the symbolized model name when using references.
I am in the process of building a multiclient system in ROR. (I am looking at http://guides.rubyonrails.org/association_basics.html#polymorphic-associations)
The structure is that a client has a contract, so when he logs in with his username, password and contract, he will have access to the system.
We have the contract id as a “master key”, which has to be in every table in the system.
class CreateContracts < ActiveRecord::Migration
def change
create_table :contracts do |t|
t.integer :contract_id
end
end
end
(chart of accounts)
class CreateCoas < ActiveRecord::Migration
def change
create_table :coas do |t|
t.integer :account_id
t.string :account_name
end
end
end
class CreateCustGroups < ActiveRecord::Migration
def change
create_table :custgroups do |t|
t.integer :account_id1
t.integer :account_id2
t.integer :account_id3
end
end
end
Q1: How do I define the contract with belongs_to? There has to be a relation in every table in the system to the contract table. Do I have to have a relation to all tables? (I think so)
class Contracts < ActiveRecord::Base
has_and_belongs_to_many :Coas
has_many:xxx
belongs:to
end
Q2: How do I define the association on the custgroup? Here we have a record where I have 3 or more fields that link to the same table (COA).
As Jesper said, it's quite hard to follow what you're trying to achieve, but I'll try to reply to your questions :
Q1 : If you want all your tables to reference a contract, you'll need to add to all those tables a foreign_key such as contract_id
so each create_table call will have the contract_id key
create_table :new_models do |t|
t.belongs_to :contract # this will create a contract_id field
end
you can also add an index on the column
add_index :new_models, :contract_id
then in all you models you'll add the belongs_to association :
class NewModel
...
belongs_to :contract
...
end
so if your Coas & CustGroups needs to reference the contract table, you'll have to change both migrations to include the contract_id key and then the models to add the belongs_to association
If a contract needs to have access to all Coas that references it, then you need to use the has_many association
class Contracts < ActiveRecord::Base
...
has_many :coas
...
end
It doesn't look like you need a has_and_belongs_to_many here, but i might be wrong about that.
if a contract also needs to access to CustGroups, you'll add :
has_many :cust_groups in the Contract model.
Q2 : I really didn't get understand what you want to do. Please explain what is the relation between Coas and Custgroups and I'll try to help you