I'm implementing a multi-tenant app with Rails. My approach is not to use the postgres inbuilt multi-tenant feature and add a column to record the subdomain. That is where the question is :)
Let's get this example
class Organisation < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :organisation
end
I'm thinking about two approaches here:
Approach 1
add a subdomain column only to organisations
pros - The way how relational databases should work \0/
cons - When I have more complex queries , that will make my code slow
Approach 2
add a subdomain column to both organisations and users
pros - This will make queries faster
cons - I'm going against the relational databases
So the question is, what sort of a method I should follow between above two, or are there a different approach that I didn't think about?
We run a multi-tenant Rails app with slightly fewer than 500 table-backed classes, and if I had to guess I'd say around 400 of them relate to client data.
Client-specific attributes are held in the Client model, but we add the client_id to every client table with a not null constraint on the database. Only a minority of them are indexed, though because they are generally only accessed through a parent record.
We do not have to worry about setting the client id because the model will generally have:
class Child
after_initialize do
self.client ||= parent.client
end
end
We add the client_id to many tables because we have a lot of code that does:
#books = current_user.client.books
... so in those cases we'll have an association directly from Client to the model, and client_id is indexed.
We add the client_id to all tables, though, because we very often, for operational or unusual reasons, want to be able to find all of the relevant records for a client ...
MarketingText.where(client: Client.snowbooks).group(:type).count
... and having to go through a parent record is just inconvenient.
Also, because we made the decision to do this on all client-specific tables, we do not have to make the decision on each one.
So to get to your question, what I would do is add the subdomain to the Organisation only. However, I would add the organisation_id column to every table holding Organisation-specific data.
If you have a lot of clients but you are going to be generally familiar with their subdomain, then I would write a meta-program method on the Organisation that lets you use:
Organisation.some_subdomain
... to get the required organisation, then find the child records (in any table) with an association directly from the Organisation model ...
Organisation.some_subdomain.users
Organisation.some_subdomain.prices
Organisation.some_subdomain.whatevers
my opinion will go Approach number one, couple reasons for this
using relational database provided with activerecord + scopes will make writing software easier, also if you have more objects under organization later for example transactions, items (beside users),
I have a project with multi tenant capabilities and below is sample of design in my project
class Company < ApplicationRecord
has_many :users
# transaction
has_many :transactions
has_many :journals , :through => :transactions
# item
has_many :items
# other has_many ...
end
and in controller you can use eager loading to minimize query (includes / joins)
#company.includes(:users).scope_filter_here.search(params[:q])
approach number 1 is more user friendly compared with approach number 2 as it's more simple to user writing your url address, the less url to type is better (personal opinion).
Related
I currently have a few models, users, clients, addresses and contacts.
I used scaffold to create my client model which only contains name, leadsource and DBA. It is currently working just fine.
My address and contact was created separately as a client can have many addresses and contacts and they require a client_id to set up the relationship.
Assuming I am using your standard crud operations, is there any way to set up a single nested form that will allow me to add all 3 at once?
Sure, the standard way is fields_for.
But normally you also want to dynamically add and remove associated objects (i.e. addresses) and that's a bit more advanced.
There are gems to help you. i.e. cocoon and a great railscast: episode 403.
Unfortunately it's a pro episode, but investing the $9 is well worth it, as the railscasts really are a source of inspiration (at least for me).
I assume, you already have the associations or create them:
rails generate migration AddClientReferenceToAddress client:references
rake db:migrate
and in models:
class Client < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :client
end
I'm running a Rails 4 app and have a question about how to structure it. My application has users. These users have many fields that can be grouped into categories, such as Personal Info, Work Info, Home, etc.
My question deals with whether I should make separate models for each of these subgroups so users have many has_one associations, or instead just name the fields in the following fashion: personal_info_name, personal_info_address, work_info_address, etc.
Here are some of my thoughts for grouping into models:
Pros:
organization
readibility
Cons:
takes more database space
more models means more files/overhead
Is there a "Rails-way" to do this/what are some other pros/cons for having multiple models?
P.S.
I've read a bit about the "fat model, skinny controller" ideas, but am not sure I entirely understand them (if they pertain to the question).
You should still use proper has_one relations, but utilize ActiveRecord delegates to create shortcut methods:
class User < ActiveRecord::Base
has_one :personal_info, ...
has_one :work_info, ...
delegate :personal_info_name, to: 'personal_info.name'
delegate :personal_info_address, to: 'personal_info.address'
delegate :workd_info_address, to: 'work_info.address'
end
Of course, assuming you are using Active Record as an ORM. Otherwise, you could go the manual route:
class User
attr_accessor :personal_info, :work_info
def personal_info_name
personal_info.name unless personal_info.nil?
end
def personal_info_address
personal_info.address unless personal_info.nil?
end
def work_info_address
work_info.address unless work_info_address.nil?
end
end
OK this is just one of many ways of doing it. I usually create a Profile model, which belongs to a User. One user can either have one Profile, or, if this app is going to grow and allow one User to manage multiple properties, it could have many Profiles later on. Inside these Profiles you can use PGSQL HStore (tutorial here) to store many small preferences (tel1, tel2, address, address_work, etc.) to keep your database from cluttering. Good luck!
Is there a way in Rails to manipulate database fields and corresponding accessor methods without the „nifty generators” ?
I want users, insofar they are privileged, to be able to manipulate the database structure, that is, at least, to add or delete columns. The privileged user should have the possibility to „Add new” some columns.
Say I have an Object/Table artist and it should “dynamically” receive columns as "date of birth", "has played with", "copies sold"
Not sure if it's a dup. It takes a preliminary decision whether Rails discourages from letting the user do this to begin with or or not. (if that's the case => certainly some noSQL solution)
In pure ruby at least it is easy to dynamically add an attribute to an existing Model/Class like this
Test.class_eval do; attr_accessor "new_attribute"; end
and
Test.new.new_attribute = 2
would return => 2 as expected
In order to create or manipulate a customized input mask / model: can I not manually go the same way the generators go and manually call ActiveRecord::Migration methods like add_column as well as create getter/setter-methods for ORM ?
If yes or no, in both cases, which are they to begin with?
Thanks!
I am not aware of any elegant way to allow an Object to dynamically create new columns. This would not be a good application design and would lead massive inefficiency in your database.
You can achieve a similar type of functionality you seek using ActiveRecord associations in Rails. Here's a simple example for your Artist model using a related Attributes table.
class Artist < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :artist
end
With this association, you can allow your Artist class to create/edit/destroy Attributes. ActiveRecord will use foreign keys in the database to keep track of the relationship between the two models.
If that doesn't work for you, your next best option is to look into NoSQL databases, such as MongoDB, which allow for much more flexibility in the schema. With NoSQL, you can facilitate the insertion of data without a predefined schema.
Here is my situation. I have model called Account. An account can have one or more contracts. The problem is that i'm dealing with a legacy application and each account's contracts are stored in a different database.
Example:
Account 1's contract are in account1_db.contracts.
Account 2's contract are in account2_db.contracts.
The database name is a field stored in accounts table.
How can i make rails association work with this?
This is a legacy PHP application and i simply can't change it to store everything in one table. I need to make it work somehow.
I tried this, but it didn't worked:
has_many :contracts, :conditions => [lambda{ Contract.set_table_name(self.database + '.contracts'); return '1' }]
Any ideas?
Why isn't database migration an option?
You're approaching this the wrong way. You want the two systems in your integration to be loosely coupled. By trying to get the two associated, you're creating an array of interdependencies that will later come around to backstab you. The approach you are trying creates tight coupling and reduces cohesion.
But, to directly answer your question, see below. Once again, I don't recommend implementing what I say below, but it would technically be a solution.
The first thing is that rails associations work only with foreign key. In fact, all database associations work this way. There isn't a ActiveRecord method of association without foreign keys as it defies what it means to associate two objects.
So you're not going to get it done with a has_many association. Instead, I would just manually create a function on your Contract model that simulates a has_many association.
class Account
memoize :contracts
def contracts
# Load from other database in here
end
def contracts=
# Push to other database in here
end
end
I have the following scenario I am trying to configure in a Ruby on Rails app (warning: I'm a beginner here)...
I have SupportType model that defines different levels of customer support options (Silver, Gold etc) with details about what that support type covers (24/7, business day only etc).
I have another model, Client that contains all of the basic customer information data.
I need to associate a Client to a SupportType (Client Jones has Gold-level support) but can't understand the "proper" way to set up this relationship. Neither "has_many" or "has_one" seems to apply in this case.
How do I set this up with the goal of being able to display all client details and the support parameters in one view?
TIA!
class Client < ActiveRecord::Base
belongs_to :support_type
end
class SupportType < ActiveRecord::Base
has_many :clients
end
This way you can access client's support type with a_client.support_type and see all the clients having gold support type with gold_support.clients
You can't use has_one in the Client model because in that case Rails aspects to find a client_id in the support_types DB table and it's not the case if I understood well since many clients can have the same SupportType.
PS: Remember to add a support_type_id column to your clients DB table