views:

94

answers:

2

Say that I have many blogs, and each blog has_many posts. These are stored in 2 tables ("blogs" and "posts"). Is it possible to add an additional column (e.g. scoped_id) to the blog table which stores an ID, scoped by the blog.

Example

 Blog: 1
  - Posts
    - id: 1, scoped_id: 1
    - id: 2, scoped_id: 2
    - id: 3, scoped_id: 3
 Blog: 2
  - Posts
    - id: 4, scoped_id: 1
    - id: 5, scoped_id: 2
    - id: 6, scoped_id: 3

I know that counter_cache can keep track of the number of posts scoped by the parent blog. However, I don't want the scoped_id to decrement if a post is destroyed.

+1  A: 

yes you can definitely add another column, and if it's me, i would do a before_save callback to save the scoped_id if it's a new_record?

class Post < ActiveRecord::Base
  ...
  before_save :populate_scoped_id

  def populate_scoped_id
    assign_the_scoped_id_method if self.new_record?
  end
  ...
end

hope it helps =)

Staelen
Thanks, but how would you ensure that the same scoped_id is never repeated for the parent blog?
Homar
i would take the largest scoped_id and increment it by 1 (replace assign_the_scoped_id_method): self.scoped_id = Post.maximum(:scoped_id) + 1
Staelen
Don't forget to add an unique index in your database: add_index :posts, [:id, :scoped_id], :unique => true. Otherwise there could (at least in theory) be two simultaneous requests and you could end up with the same scoped_id twice.
Christoph Schiessl
yeah to prevent race conditions ;-) always the devil
Staelen
+1  A: 

Your best bet would be to keep the last used ID on the blog, and populate it via:

class Post < ActiveRecord::Base
  …
  before_create :populate_scoped_id

  def populate_scoped_id
    blog.increment!(:latest_scoped_post_id)
    self[:scoped_id] = blog.latest_scoped_post_id
  end
  …
end

or some jazz like that.

If the save fails, the counter will not be incremented, because it's all in a whopping great transaction (it's, like, totally enterprise).

cwninja
Excellent. Thanks!
Homar