Firstly, I'd keep this display ID (maybe "member ID") separate from users.id.
Any non-standard behaviour there is asking for trouble. There's a lot of
convention built up in activerecord around having a single artificial
integer PK, and veering from this standard isn't worth the trouble it
causes. (We used some composite PKs for a time at TC as part of a migration
strategy, and they were very inconvenient to work with.)

I'd recommend having a unique users.id column like normal, and then a
separate member_id that you use for routing, which is unique within each
tenant (enforced in the DB).

I'd add a unique index to the users table on (tenant_id, member_id):

    add_index :users, [:tenant_id, :member_id], unique: true

Then I'd add a validation in the model, for reasonable error messages (the
real "validation" is done by the DB's unique index):

    validates :member_id, presence: true, uniqueness: {scope: :tenant_id}

Then I'd auto-populate that field before the model's validation happens.

    before_validation :assign_member_id_if_required

    def assign_member_id_if_required
      self.member_id ||= begin
        max_member_id = tenant.users.order('member_id
DESC').limit(1).pluck(:member_id).first
        (max_member_id || 0) + 1
      end
    end

That will query efficiently:

    SELECT "users"."member_id" FROM "users" WHERE "users"."tenant_id" =
12345 ORDER BY member_id DESC LIMIT 1

- Ben


On 3 April 2013 20:29, Rich Buggy <r...@zoombugmedia.com> wrote:

> Hi everyone,
>
> I'm building a multi-tenant application and I'd like some of the ID's to
> be per tenant. It's using the path based multi-tenant approach. For example
> - /:account/invoices/:id
>
> I've got the routing working correctly but the ID's are being generated
> automatically so they are unique across the application, not just the
> tenant. This isn't ideal because each tenant should start from 1.
>
> I already store the next id in the tenant table so they can choose to
> start from a number other than 1. This is important as someone with
> existing data may decide to not start from 1. I think there are a number of
> ways I can deal with this.
>
> 1. If I was doing this without rails I'd make the tenant_id + id a
> composite field and set the id manually when creating the record. Is there
> are an easy way to do this with rails?
>
> 2. I could add number field and accept that number != id. This is easy but
> ugly because /demo/invoices/123456 could be invoice 1
>
> 3. I could add a number field and try to muck with the routing so it used
> number instead of id
>
> Does anyone know of a good guide or blog post on how to do this?
>
>     Rich
>
> --
> You received this message because you are subscribed to the Google Groups
> "Ruby or Rails Oceania" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to rails-oceania+unsubscr...@googlegroups.com.
> To post to this group, send email to rails-oceania@googlegroups.com.
> Visit this group at http://groups.google.com/group/rails-oceania?hl=en.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
>



-- 
Cheers
Ben

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
or Rails Oceania" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to rails-oceania+unsubscr...@googlegroups.com.
To post to this group, send email to rails-oceania@googlegroups.com.
Visit this group at http://groups.google.com/group/rails-oceania?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to