I have the following tables
keyword
keyword_id - PK
description
status_id - FK
keyword_status
status_id - PK
description
Trying to model them in AR and when attempting to save in my test it's not saving the status ID in keyword. They are mapped as such:
class Keyword < ActiveRecord::Base
self.table_name = :keyword
self.primary_key = :KEYWORD_ID
attr_writer :description
attr_writer :keyword_status
has_one :keyword_status, foreign_key: :STATUS_ID
end
class KeywordStatus < ActiveRecord::Base
self.table_name = :keyword_status
self.primary_key = :STATUS_ID
end
and the code where it breaks (keyword status is populated by a fixture)
keyword = Keyword.new
keyword.description = "keyword#{n}"
keyword.keyword_status = KeywordStatus.first
keyword.save
When keyword.save is called i get cannot insert NULL into 'STATUS_ID' on table Keyword
NOTE: I cannot change any of the DDL
As dstarh said, the foreign key association is backwards. In your models you should only need:
class Keyword < ActiveRecord::Base
has_one :keyword_status
end
class KeywordStatus < ActiveRecord::Base
belongs_to :keyword, :foreign_key => "status_id"
end
For the association to work. Also, setting the KeywordStatus object up with a fixture before creating the associated object is a bit odd but will work if that is the behavior you need. Also, why not just use the foreign key keyword_id and let rails handle it for you?
Update if you want to use keyword_id instead of status_id as the foreign key:
the tables will be as such:
keyword
id - PK
description
keyword_status
id - PK
keyword_id - FK
description
And your models:
class Keyword < ActiveRecord::Base
has_one :keyword_status
end
class KeywordStatus < ActiveRecord::Base
belongs_to :keyword
end
Hope this helps!
Update 2: Given the tables cannot change the association has to be a bit backwards. I would recommend:
class Keyword < ActiveRecord::Base
belongs_to :keyword_status
end
class KeywordStatus < ActiveRecord::Base
has_many :keywords, :foreign_key => "status_id"
end
I used a has many as the association since from what has been said it looks like a keyword can share a status that another keyword has. This also means that you will have to do something like:
KeywordStatus.first.keywords = KeywordStatus.first.keywords.push(keyword)
KeywordStatus.save
instead of:
keyword.keyword_status = KeywordStatus.first
Which operates in the opposite direction than you want. As you can see this can get pretty confusing so if at all possible i would suggest writing a migration to change your tables (this can be done for tables that have existing data if that is the problem). Again I hope this helps!
You got the direction wrong. It should be keyword belongs_to status. The general rule is that the model that has the foreign key column belongs to the other model.
Related
City model:
class City < ActiveRecord::Base
has_many :angels
def angel_count
angels.size
end
end
Angel model:
class Angel < ActiveRecord::Base
belongs_to :city, :counter_cache => true
attr_accessible :city_id
end
I able to access the angel_count method through the rails_admin list action, however it won't allow me to change the order direction like the other columns.
Looks like you are using counter_cache wrong way. Do you have angels_count column in cities table? If not, add the column and run migration. You should be able to sort by this column.
I have 3 models: Question, Option, Rule
Question has_many options;
Option needs a foreign key for question_id
Rule table consists of 3 foreign_keys:
2 columns/references to question_ids -> foreign keys named as 'assumption_question_id' and 'consequent_question_id'
1 column/reference to option_id -> foreign key named as option_id or condition_id
Associations for Rule:
Question has_many rules; and
Option has_one rule
I want to understand how to write up migrations for this, and how that associates to the 'has_many'/'belongs_to' statements I write up in my model, and the ':foreign_key' option I can include in my model.
I had this for my Option migration, but I'm not sure how the "add_index" statement works in terms of foreign keys, and how I can use it for my Rule migration: (my Question and Options models have appropriate has_many and belongs_to statements - and work fine)
class CreateOptions < ActiveRecord::Migration
def change
create_table :options do |t|
t.integer :question_id
t.string :name
t.integer :order
t.timestamps
end
add_index :options, :question_id
end
end
Thank you for the help!
Note: I have found this way to solve the problem.Kindness from China.
If you have RailsAdmin with you,you may notice that you can see all rules of one question as long as one field of both question fields(assumption_question_id,consequent_question_id) equals to id of the question.
I have done detailed test on this and found out that Rails always generates a condition "question_id = [current_id]" which make to_sql outputs
SELECT `rules`.* FROM `rules` WHERE `rules`.`question_id` = 170
And the reason that the following model
class Question < ActiveRecord::Base
has_many :options
# Notice ↓
has_many :rules, ->(question) { where("assumption_question_id = ? OR consequent_question_id = ?", question.id, question.id) }, class_name: 'Rule'
# Notice ↑
end
makes Question.take.rules.to_sql be like this
SELECT `rules`.* FROM `rules` WHERE `rules`.`question_id` = 170 AND (assumption_question_id = 170 OR consequent_question_id = 170)
Is that we have not yet get ride of the annoy question_id so no matter how we describe or condition properly, our condition follows that "AND".
Then,we need to get ride of it.How?
Click here and you will know how,Find sector 8.1,and you can see
Article.where(id: 10, trashed: false).unscope(where: :id)
# SELECT "articles".* FROM "articles" WHERE trashed = 0
Then lets do it:
class Question < ActiveRecord::Base
has_many :options
# Notice ↓
has_many :rules, ->(question) { unscope(where: :question_id).where("assumption_question_id = ? OR consequent_question_id = ?", question.id, question.id) }, class_name: 'Rule'
# Notice ↑
end
class Rule < ActiveRecord::Base
belongs_to :option
belongs_to :assumption_question, class_name: "Question", foreign_key: :assumption_question_id, inverse_of: :assumption_rules
belongs_to :consequent_question, class_name: "Question", foreign_key: :consequent_question_id, inverse_of: :consequent_rules
end
class Option < ActiveRecord::Base
belongs_to :question
has_one :rule
end
All done.
Finally
This is my first answer here at stackoverflow,and this method is never found anywhere else.
Thanks for reading.
add_index adds an index to column specified, nothing more.
Rails does not provide native support in migrations for managing foreign keys. Such functionality is included in gems like foreigner. Read the documentation that gem to learn how it's used.
As for the associations, just add the columns you mentioned in your Question to each table (the migration you provided looks fine; maybe it's missing a :rule_id?)
Then specify the associations in your models. To get you started
class Question < ActiveRecord::Base
has_many :options
has_many :assumption_rules, class_name: "Rule"
has_many :consequent_rules, class_name: "Rule"
end
class Rule < ActiveRecord::Base
belongs_to :option
belongs_to :assumption_question, class_name: "Question", foreign_key: :assumption_question_id, inverse_of: :assumption_rules
belongs_to :consequent_question, class_name: "Question", foreign_key: :consequent_question_id, inverse_of: :consequent_rules
end
class Option < ActiveRecord::Base
belongs_to :question
has_one :rule
end
Note This is just a (untested) start; options may be missing.
I strongly recommend you read
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://guides.rubyonrails.org/association_basics.html
Edit: To answer the question in your comment
class Option < ActiveRecord::Base
belongs_to :question
# ...
The belongs_to tells rails that the question_id column in your options table stores an id value for a record in your questions table. Rails guesses the name of the column is question_id based on the :question symbol. You could instruct rails to look at a different column in the options table by specifying an option like foreign_key: :question_reference_identifier if that was the name of the column. (Note your Rule class in my code above uses the foreign_key option in this way).
Your migrations are nothing more than instructions which Rails will read and perform commands on your database based from. Your models' associations (has_many, belongs_to, etc...) inform Rails as to how you would like Active Record to work with your data, providing you with a clear and simple way to interact with your data. Models and migrations never interact with one another; they both independently interact with your database.
You can set a foreign key in your model like this:
class Leaf < ActiveRecord::Base
belongs_to :tree, :foreign_key => "leaf_code"
end
You do not need to specify this in a migration, rails will pull the foreign key from the model class definition.
I am using Ruby 1.8.7 and Rails 2.3.8 and I have the following root resource:
class PointOfInterest < ActiveRecord::Base
set_primary_key "Id"
set_table_name "POI"
has_many :attributes, :foreign_key => 'POIId'
end
The point of interest can have several attributes:
class Attribute < ActiveRecord::Base
set_primary_key "Id"
set_table_name "Attribute"
belongs_to :point_of_interest, :foreign_key => 'POIId'
has_one :multimedia, :foreign_key => 'Id', :primary_key => 'Value'
end
The attribute class may have media associated with it:
class Multimedia < ActiveRecord::Base
set_primary_key "Id"
set_table_name "Multimedia"
end
I am trying to insert a point of interest in my database like so:
poi = PointOfInterest.new
attr = poi.attributes.new
attr.SomeAttribute = 1
attr.build_multimedia(:content => 'test')
poi.save
This is properly persisting both the root (PointOfInterest) and the Multimedia record. The Attribute, however, is not being properly persisted. While the foreign key to the point of interest is properly set (POIId), the foreign key to the Multimedia record remains null.
Any clue as to why this is very much appreciated!
Thanks!
Your relationship / foreign key are set at cross purposes.
If Attribute has_one Multimedia, then you need the Multimedia model to declare belongs_to :attribute, and the Multimedia table to contain a foreign key for Attribute.
I'm guessing that you can't change your database schema (otherwise, I'd have to ask why you're using non-Rails-standard table and key names); in which case you want to switch the sense of the relation. Make Attribute belongs_to :multimedia, and Multimedia has_one :attribute. Then the Multimedia FK in the Attribute table is pointing in the right direction.
I am a newbie in the Rails framework. I've created tables:
DealerGroups Dealer
------------ ------------
Id:integer(primary key) Id:integer(primary key)
name:string dealer_group_id:integer(foreign key)
But when I try to set Dealer.dealer_group_id = value (this value exists in the DealerGroups table) I get an "UninitializedConstant Dealer::DealerGroup" exception.
In models I have :
class Dealer < ActiveRecord::Base
belongs_to :dealer_buying_group, :foreign_key => "dealer_buying_group"
end
class DealersGroup < ActiveRecord::Base
has_many :dealer
end
If I delete the has_many and belongs_to relations, it all works fine.
Why won't it work with the relations?
Be careful with the "s" (why is your "Dealer" table is not "Dealers"?)
You do not need to manually set a foreign key in Rails, all you need to define the Model_ID field to it as you generate your scaffold/model/controller, then belongs_to and has_many in the model will do the relation for you
Database:
DealerGroups Dealers
------------ ------------
Id:integer(primary key) Id:integer(primary key)
name:string dealergroup_id:integer
Models :
class Dealer < ActiveRecord::Base
belongs_to :dealergroup
end
class DealersGroup < ActiveRecord::Base
has_many :dealers
end
To access dealdergroup's name from dealers, just use
controller:
#dealer = Dealer.find_by_id(myInt)
view:
<%= #dealer.dealergroup.name %>
You have class DealersGroup whereas you are looking for dealer_group_id.
I am using an existing database with a rails app.
I can't change the table or column names.
Lets say table one is "invoices" and table 2 is "orders"
they both have a primary key that is called the same thing, lets say "order_id"
invoices can find its order by looking at the primary key "order_id" in the orders table.
Vice versa for an order finding its invoice.
Invoices can have but not always have one order (you might be invoiced for something besides an order, like a "work_order" which would be found by looking for the "order_id" in the primary key position of the "work_orders" table. So invoices might have a work_order or an order.
orders always has an invoice
work_orders always has an invoice
Im trying to figure out the classes in the models.
Do you set the primary and foreign keys to the same thing? What about belongs_to? The way this DB is set up, nothing really belongs to anything, they just reference each other by this same value "order_id". Would it be like this?
class Invoice < ActiveRecord::Base
set_primary_key(:order_id)
set_foreign_key(:order_id)
end
-- snip --
class Order < ActiveRecord::Base
set_primary_key(:order_id)
set_foreign_key(:order_id)
end
... And the same for a work order.
class WorkOrder < ActiveRecord::Base
set_primary_key(:order_id)
set_foreign_key(:order_id)
end
Is this correct? It seems to trashy of a way to do it but this DB is terrible.
What about all the belongs_to stuff?
Let me know if I have left anything out.
Thanks!
I believe the answer could be:
class Order < ActiveRecord::Base
set_primary_key(:order_id)
belongs_to :invoice, :foreign_key => :order_id
end
class WorkOrder < ActiveRecord::Base
set_primary_key(:order_id)
belongs_to :invoice, :foreign_key => :order_id
end
class Invoice < ActiveRecord::Base
set_primary_key(:order_id)
has_one :work_order
has_one :order
end
Although I'm not really sure your primary key can also be a foreign key (I'm new to Rails too)