I want to set an attribute on a child model when its parent is changed
Here is an example:
create_table "children", :force => true do |t|
t.integer "parent_id"
t.string "parent_type"
t.integer "foo_id"
end
create_table "fathers", :force => true do |t|
t.integer "foo_id"
end
create_table "mothers", :force => true do |t|
t.integer "foo_id"
end
create_table "foos", :force => true do |t|
end
class Foo < ActiveRecord::Base
end
class Child < ActiveRecord::Base
belongs_to :parent, :polymorphic => true
belongs_to :foo
end
class Father < ActiveRecord::Base
belongs_to :foo
end
class Mother < ActiveRecord::Base
belongs_to :foo
end
Now, when I execute the following, I want child.foo_id to be set from parent:
foo = Foo.new {|foo| foo.id = 1}
parent = Father.new {|father| father.foo = foo}
child = Child.new
child.parent = parent
I need foo_id to be set right away, not in a before_validation callback or anything like that.
This is a simplified example, in the real case I have many more polymorphic types. I know this can be accomplished with an after_add callback on a has_many association on Father and Mother, but I'd rather not have to add a has_many association if possible since that requires me to add code in many more places. Is there a way to do this?
I don't clearly understand what you want to achieve.
May be this
parent = Parent.new(foo_id=>123456)
child = Child.new(:parent=>parent,:foo_id=>parent.foo_id)
if parent.save
child.save
end
or
parent = Parent.new(foo_id=>123456)
if parent.save
Child.create(:parent=>parent,:foo_id=>parent.foo_id)
end
not sure if this would work but maybe you could overwrite the setter for parent in the Child model
def parent=(p)
self.foo_id = p.foo_id
super(p)
end
Related
I would like to know how we can scope a Post with his association table ?
I explain what I want to do it will be more clear. I actually have a scaffold :ranch, and a scaffold :staff. And when I create a new Staff, I can choose to associated it with many ranches. So I have an other table to reference this association :ranch_staff.
So if I want to scope the staff to display only which are associated with the actual #ranch, how can I do that ?
Something like this should do the trick:
migration:
class Init < ActiveRecord::Migration
def change
create_table :ranches do |t|
t.string "name"
end
create_table :staffs do |t|
t.string "name"
end
create_table :ranches_staffs, id: false do |t|
t.belongs_to :ranch
t.belongs_to :staff
end
end
end
app/models/staff.rb:
class Staff < ActiveRecord::Base
has_and_belongs_to_many :ranches
end
app/models/ranch.rb:
class Ranch < ActiveRecord::Base
has_and_belongs_to_many :staffs
end
run in rails console:
staff1 = Staff.create(name: "staff1")
staff2 = Staff.create(name: "staff2")
staff3 = Staff.create(name: "staff3")
staff4 = Staff.create(name: "staff4")
ranch1 = Ranch.create(name: "ranch1")
ranch2 = Ranch.create(name: "ranch1")
staff1.ranches << ranch1
staff2.ranches << ranch1
staff3.ranches << ranch2
staff4.ranches << ranch2
Now you can access ranch1's staff by
ranch1.staffs
=> #<ActiveRecord::Associations::CollectionProxy [#<Staff id: 1, name: "staff1">, #<Staff id: 2, name: "staff2">]>
See association.
My models are:
class CarBrand < ActiveRecord::Base
has_many :car_models
end
class CarModel < ActiveRecord::Base
belongs_to :car_brand
end
and my migrations are
class CreateCarBrands < ActiveRecord::Migration
def up
create_table :car_brands do |t|
t.string "brand", :limit => 20
t.timestamps null: false
end
end
def down
drop_table :car_brands
end
end
class CreateCarModels < ActiveRecord::Migration
def up
create_table :car_models do |t|
t.references :car_brand
t.string "model", :limit => 20
t.timestamps null: false
end
add_index :car_models, :car_brand_id
end
def down
drop_table :car_models
end
end
and i want to get car models according to specific car brand, in database i have both records, but when i type in console it gives error
somecar = CarBrand.where(:brand => 'Toyota')
somecar.car_models
so it doesn't returns me models of toyota, but i have them in database!!!
somecar = CarBrand.where(:brand => 'Toyota') returns an active record relation #<ActiveRecord::Relation [#<..... the main point is that it is a collection. You have to iterate over each item in the collection.
some_cars = CarBrand.where(:brand => 'Toyota')
some_cars.each do |car| puts car.car_model end
or on the first item some_cars.first.car_model
Notice I changed some_car to some_cars, the name of the variable does matter but it is easier to see that it is a collection. Notice I called .car_model (and NOT car_models) on each item, that really is important.
Try like that:-
somecar = CarBrand.where(:brand => 'Toyota')
somecar.first.car_models
As CarBrand.where(:brand => 'Toyota') returns an array.
OR
Try like that:-
somecar = CarBrand.find_by brand: 'Toyota'
somecar.car_models
CarBrand.find_by brand: 'Toyota' will fetch first matching record.
In one model, I have this:
class Game < ActiveRecord::Base
has_one :turn
attr_accessor :turn
attr_accessible :turn
default_scope :include => :turn
def Game.new_game
turn = Turn.create count: 1, phase: 'upkeep', player: 1
game = Game.create turn: turn
game
end
end
class Turn < ActiveRecord::Base
belongs_to :game
end
Later, in a controller, I have this:
respond_with Game.find(params[:id])
But for some reason, the returned game has a turn_id that is nil and no associated turn object.
Why isn't the association being saved properly, or not returning properly with find()?
In my migration, I think I've setup the association correctly:
create_table :games do |t|
t.timestamps
end
def change
create_table :turns do |t|
t.string :phase
t.integer :count
t.references :game
t.timestamps
end
end
You seemed to have got messed up on associations
This is what i Think as per the understanding of scenario.
The associations should be like
class Game < ActiveRecord::Base
has_one :turn
#.....
end
class Turn < ActiveRecord::Base
belongs_to :game
#.....
end
and migrations like
create_table :games do |t|
#add some columns
t.timestamps
end
create_table :turns do |t|
t.references :game
#add some columns
t.timestamps
end
now to add new game and turn
game = Game.create
turn = game.turn.create count: 1, phase: 'upkeep', player: 1
game.tun.create will automatically create a turn record with game_id = game.id and other supplied args.
problem with your migration is game is referring turn which instead should be opposite .
Find more on associations here
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://guides.rubyonrails.org/association_basics.html
Something is inverted.
Since Turn has the belongs_to statement, Turn should contain game_id, and not the other way around.
So you can't access a Game turn_id because the field does not exists. It will be always nil and if ou remove the has_one statement it will raise an exception.
Okay so what I have is a table that keeps track of history on a "person", this logs the person(User), the handler(User), the status before(JobApplicationStatus), the status after(JobApplicationStatus).
Now in my head this translates down to a table of such:
**JobApplicationHistory**
id (int)
status_before_id (int)
status_after_id (int)
user_id (int)
handler_id (int)
I tried to make a migation, that sorta worked, but it's not working right.
As I would like to use something like:
user = User.find(1)
handler = User.find(1)
status_before = JobApplicationStatus.find(1)
status_after = JobApplicationStatus.find(2)
history = JobApplicationHistory.new()
history.user = user
history.handler = handler
history.status_before = status_before
history.status_after = status_after
history.save
Here is my migration
class CreateUserApplicationHistories < ActiveRecord::Migration
def self.up
create_table :user_application_histories do |t|
t.integer :user_id # goes to User
t.references :job # goes to Job
t.integer :handler_id # goes to User
t.integer :status_from_id # goes to JobApplicationStatus
t.integer :status_to_id # goes to JobApplicationStatus
t.timestamps
end
add_index("user_application_histories", "job_id")
add_index("user_application_histories", "handler_id")
add_index("user_application_histories", "user_id")
add_index("user_application_histories", "status_from_id")
add_index("user_application_histories", "status_to_id")
end
def self.down
drop_table :user_application_histories
end
end
And my model, which i think makes it fail
class UserApplicationHistory < ActiveRecord::Base
belongs_to :status_from_id, :class_name => "JobApplicationStatus"
belongs_to :status_to_id, :class_name => "JobApplicationStatus"
belongs_to :user_id, :class_name => "User"
belongs_to :handler_id, :class_name => "User"
end
You right, your model should look like this for user and handler:
belongs_to :user
belongs_to :handler, :class_name => "User"
To help you with JobApplicationStatus I would need to know how this table looks like
How to do that with ActiveRecord? My code:
p = Product.create
l = Label.create
p.labels.add << l
But I get l.parent == nil
create_table "labels", :force => true do |t|
t.integer "product_id"
end
Some code from you would be appropriate, since what you're asking should be working automatically.
class Parent < ActiveRecord::Base
has_many :children
end
class Child < ActiveRecord::Base
belongs_to :parent
end
If your code doesn't resemble that, then please post a more specific question.
Edit:
Does your Product model have any validations in it? They aren't going to pass your code above, and you aren't checking the return value from create, so you'll never know.
You could use acts_as_tree for this: http://github.com/rails/acts_as_tree