Reputation: 79
I tried to normalize my database model. But I'm clueless how to do it in this case. Giving the following model:
I want to display all Systems of a Customer. That would be: @customer.systemobjects.each
. That is already working.
Then I could add a System to a Cluster (which I mentioned is a "HABTM" association). In my Customer view I want to show only systems, that are not related to a cluster (also working with Cluster.includes(:systems).where(systems: { id: sysid }).present?
).
Now my question: I want to display all Clusters (and Systems of that Cluster) of a specific customer, too. But, right now, I only to have the connection to customer through systems. For me, it would be easier, to add a reference to customer also in the cluster object (even though I would have this information already in the system).
Should I add this reference? Does it have something to do with normalization anyway? How would you assess this situation from a best practice point of view for a databases in general and for Ruby On Rails in specific? How would also be the best way, to go through each cluster of a customer, when I have it only through systems (how I could do it in rails?)?
Upvotes: 1
Views: 55
Reputation: 1399
I think you'd prefer something like this:
class Customer
has_many :systems
has_many :clusters, through :systems # expect `clusters_id` in System, which is typical
# ...other code
end
class System
belongs_to :customer
belongs_to :cluster
# ...other code
end
class Cluster
has_many :systems
has_many :customers, through :systems # expect `customer_id` in System, which is typical
# ...other code
end
This results in three tables, as your model already implies, but uses the systems table as a "hinge" lookup table for the other two without implication that clusters belong to systems (which doesn't make sense IRL as I understand your problem statement).
I often find has_many :through
is often an easier and better choice than HABTM unless you truly have a mutual belonging relationship. You can go to the Rails Guides here and here (guide: "Active Record Associations") for more information on use :through
. Definitely worth getting to know that guide for the kind of questions you have (though to be fair, it can take a bit of experience to fully appreciate the various options and how they're helpful).
Now when you want to refer to clusters that a customer has systems within, you merely need to write something like this:
my_customer = Customer.find(some_id)
customer_clusters = customer.clusters
customer_systems = customer.systems
To find all the customers for a cluster (and so through
clusters' systems), you'd write something like this:
target_cluster = Cluster.find(some_id)
cluster_customers = target_cluster.customers
If you want a want to produce a hierarchy of those (say, systems of a customer grouped under the clusters they belong to), it'd be something like this:
my_customer = Customer.find(some_id)
customers_systems = customer.systems.includes(:cluster) # this brings in the associated cluster records without additional database operations
then either iterate or use group_by
from the resulting data in customer_systems
depending on how you intend to display or return the data.
Upvotes: 2