Reputation: 66
I'm pretty new to rails and lately I found that I understood activerecords has_one association contrary to how it actually works. Refering to the example from rubyonrails guide I imagined that it is the supplier that should hold its account_id, since I see no point in forcing every account to hold its supplier.
Because I don't really understand why, or simply don't agree with the object maintaining it's foreign keys in other objects, I don't know what would be the correct rails solution for my simple problem: I have two objects - document, and draft. Every document has many drafts, and one of them is marked as the current draft. I imagined the table layout to be something like this:
table document
id
current_draft_id
table draft
id
document_id
text
What I'm looking for here is something like a has_one association, but reversed, so that the document would held and maintain the current_draft_id. Using belongs_to is not an option because of its different behaviour. For example I'd like document.current_draft = new_draft to update the foreign_key correctly.
How to approach this in rails?
-- update 1
To clarify my problem, please assume that the draft being current won't have nothing to do with created_at and updated_at fields, so scopes won't do.
Adding a current field to the drafts table would be a weird move from the table design point of view. I'm also planning to add information about the published draft to the document object, and multiplying such informations in drafts tableseems to be an odd step.
I like Amesee's idea, but still I have some inner resistances, similar to adding the current column to the drafts table.
Upvotes: 0
Views: 190
Reputation: 14038
How do you enforce which draft is the "current" draft? Would it be the most recently created one? The last one edited? I would have the current draft be a logically calculated facet of the draft table and find it with scopes, rather than force a fixed ID that may not always match the logic.
class Document < ActiveRecord::Base
has_many :drafts
def current_draft
self.drafts.ordered.first
end
end
class Draft < ActiveRecord::Base
belongs_to :document
scope :all
scope :ordered, order_by(:updated_at)
end
Alternatively, add a :current, :boolean, :default => false
field to the drafts table and have there be only one child with current being true. (A good explanation of the logic for this method can be found here: Rails 3 app model how ensure only one boolean field set to true at a time)
If you really want to have a fixed child ID in the parent, then you need a :belongs_to
with a defined foreign key:
documents table:
id
current_draft_id
Model:
class Document < ActiveRecord::Base
has_many :drafts
belongs_to :current_draft, :class_name => 'Draft', :foreign_key => 'current_draft_id'
end
Controller code somewhere:
@document.current_draft = @draft
Upvotes: 0
Reputation: 4737
I would argue that Draft
is a Document
so it may make more sense to manage these classes with single table inheritance. You can tell that a draft is a "current draft" by its type.
class CreateDocuments < ActiveRecord::Migration
def change
create_table :documents do |t|
t.string :type
# ...
t.timestamps
end
end
end
And the models.
class Document < ActiveRecord::Base
# ...
end
class Draft < Document
# ...
end
class CurrentDraft < Draft
# ...
end
Later on, when a draft isn't "current" anymore, update its type by changing its type
attribute to "Draft" or "Document." I think this is a better solution than constantly checking a boolean or date attribute on the object and asking about its state everywhere in the application.
Upvotes: 1