Reputation: 5250
I'm trying to the has_one and has_many methods in one model. A taskflow can have many tasks but it also has a single default task.
I'm trying to create a column in the taskflow table that contains the id of a task. However when I try and set that default task it does not work.
class Taskflow < ActiveRecord::Base
has_many :tasks
has_one :default_task, :class_name => 'Task'
class Task < ActiveRecord::Base
belongs_to :taskflow
I seed the database then in the rails console I try to assign a task to be a taskflow default_task:
taskflow1 = Taskflow.first
task1 = Task.first
taskflow1.default_task = task1
This doesn't work with the taskflow default_task value remaining 'nil'. What the correct way to achieve the desired behaviour?
Any help would be much appreciated.
Edit
Migration files are:
class CreateTaskflows < ActiveRecord::Migration
def change
create_table :taskflows do |t|
t.string :title
t.string :description
t.references :default_task
t.timestamps null: false
end
end
end
class CreateTasks < ActiveRecord::Migration
def change
create_table :tasks do |t|
t.string :task_type
t.text :help
t.text :data
t.belongs_to :taskflow
t.timestamps null: false
end
end
end
Upvotes: 4
Views: 1929
Reputation:
I would implement it differently. I would create a boolean field default_task on the Task model. And Taskflow would have the following has_one and has_many associations:
class Taskflow < ActiveRecord::Base
has_many :tasks
has_one :default_task, class_name: 'Task', condition: proc{"tasks.default_task = true"}
Semantically, Taskflow should have many tasks and have one default task among those tasks. And on my opinion that a taskflow belongs to a default task sounds a bit artificial.
You can also add a functionality to constantly maintain a single task as a default task (with bit set to true, more details here), or create a default task at the time of creation of a Taskflow. It all depends on your requirements.
AND, if you still want to have the default_task_id column to be on your Taskflow model and use has_one association, you can do the following:
class Taskflow < ActiveRecord::Base
has_many :tasks
has_one :default_task, class_name: "Task", primary_key: "default_task_id", foreign_key: "id"
Upvotes: 5
Reputation: 288
People here are right about belongs_to or how to hack has_one. I understand that belongs_to does not feel right in this situation, but if you are using "referencing" in table you normally always want to use belongs_to. So I would assume to mark the default task on the tasks itself as default - but if they can be part of different task_flows where different tasks could be the default - then of course that's not possible.
Another option to those here already mentioned is: You can add a scope to the has_many :tasks relation. Like this:
has_many :tasks do
def default
joins(:task_flow).where('tasks.id = task_flows.default_task_id').first
end
end
Then you can request for
@task_flow.tasks.default
Upvotes: 2
Reputation: 926
has_one is used to specifie a one-to-one association with another class. This method should only be used if the other class contains the foreign key.
In your case, you should use belong_to as the default_task reference is in the TaskFlow model (in your migration).
The TaskFlow model has two relations:
class Taskflow < ActiveRecord::Base
has_many :tasks
belongs_to :default_task, :class_name => 'Task'
class Task < ActiveRecord::Base
belongs_to :taskflow
The way you had it, Rails wouldn't know which task is the default one.
Upvotes: 2
Reputation: 5250
I got this to work. Instead of has_one
, belongs_to
is needed in the model.
class Taskflow < ActiveRecord::Base
has_many :tasks
belongs_to :default_task, :class_name => 'Task'
class Task < ActiveRecord::Base
belongs_to :taskflow
I think something to do with has_one having the reverse relationship to my assumption. http://guides.rubyonrails.org/association_basics.html#the-has-one-association
Upvotes: 1