Avoiding Duplicate Data in DB (for use with Rails) - ruby-on-rails

I have five tables that I am trying to get to work nicely together but may need some help.
I have three main tables:
accounts
members
and roles.
With two join tables
account_members
and account_member_roles.
The accounts and members table are joined by account_members (fk account_id and member_id) table.
The other 2 tables are the problem (roles and account_member_roles).
A member of an account can have more than one role and I have the account_member_roles (fk account_member_id and role_id) table joining the account_members join table and the roles table.
That seems logical but can you have a relationship with a join table? What I'd like to be able to do is when creaeting an account, for instance, I would like #account.save to include the roles and update the account_member_roles table neatly ..... but through the account_members join table.
I've tried .....
accept_nested_attributes_for :members, :account_member_roles
in the account.rb but I get .....
ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection (Cannot modify association 'Account#account_member_roles' because the source reflection class 'AccountMemberRole' is associated to 'AccountMember' via :has_many.)
upon trying to save a record.
Any advice on how I should approach this?
CIA
-ants

If I am reading this correctly you have:
accounts habtm members
members habtm accounts
members has_many roles
If that is accurate you just need a join table between accounts and members and then just a regular FK between members and roles. #account.members and #account.members.roles would both give you access to the attributes you need and #account.save should save the whole mess without a fuss.
I am not sure what compels you to have that account_members_roles table. I don't if Rails can handle that. I know I sure can't. :)

Take a read here:
http://blog.hasmanythrough.com/2006/4/17/join-models-not-proxy-collections
The main difference between a simple
has_and_belongs_to_many join table and
a has_many :through join model is that
the join model can have attributes
other than the foreign keys for the
records it is joining. In fact, if you
didn't have those other attributes you
probably wouldn't use a join model and
would settle for a join table.
So it seems that you want to use has_many :through and you would be set.

Related

How to design database tables for different types of Users?

I'm on a project with Rails, Postgresql and Active Record, where I have Users, that can be either Influencers, or Creators.
The Users have common columns such as email, password, first_name and last_name, but :
influencers will have followers, eg_rate, account columns
creators will have SIRET_number and specialty columns
How can I design my database so Influencers and Creators are kind of "child" of the Users table ? I mean, is it possible to have a db where I can access a User' followers or a User's specialty with one query, or I'll always have to do multiple queries to achieve this ? I've tried to create three tables for each, with a foreign key user_id in Influencers table and Creators table. I also tried to add a user_type column to my User table where I pass the values of either "influencer" or "creator", but I'm kind of lost on how to link every tables...
Thank you everyone.
Your approach is right.
You can create a table users with the common columns and add a foreign key to influencers and creators tables.
Then when you need to retrieve the data, you can use ActiveRecord relations to easily fetch data and use ActiveRecord's includes method for relations.
For example:
class Creator < ActiveRecord::Base
# The relation must be set
has_one :user
end
# To fetch data anywhere else:
Creator.find_by(SIRET_number: 1234).includes(:user)
If you need to retrieve a creator or influencer by an attribute from related users table, you can use joins:
Creator.joins(:users).where(users: {email: "foo#bar.com"})
Make sure you have the relations set in both User and Creator models.
Check out this topic for more info.
By using includes or joins instead of using creator.user you'll avoiding the unnecessary additional query. The downside is the syntax is now longer, you can maybe create your own getters to easily retrieve data instead of writing "includes" everytime.
Also assuming you're not aware of this method, I suggest you to read about the common N+1 problem with Rails & ActiveRecord. The same methods can solve a lot of problems for you.

Joining two tables that share the same id

Let's say I have two tables: NormalClass Table and SpecialClass Table.
Each table contains classes for students.
I want to join tables so that I can access the normal classes of each student in that has a special class for students.
Both tables share the student_id key.
Trying to do this:
NormalClass.includes(:specialClasses)..
results in:
ActiveRecord::ConfigurationError: Association named 'specialClasses' was not found on NormalClass.includes; perhaps you misspelled it?
Should I be doing something else?
Gerbil,
Basically, you need to first set your associations properly.
Look at section 2.4 The has_many :through Association in Rails guides
Once you set it, you can pull up the data you want by pulling the student_id key

HMT or HABTM for Orders and Items

Fairly new to rails and trying to understand which relationships to use before going forward.
I have two models: orders and items. This is a many to many relationship, but I'm unsure of which relationship to use.
Orders might have delivery time, quantity of items, etc.
Lastly, what would you call the model joining orders and items if using HMT?
If you need to know anything else about the relationship of the item on a particular order, you need HMT.
If your items change price in the future, do you want to know how much they were sold for on orders in the past?
In this type of requirement, I've always had many "LineItem" records for an order, and the line_item instances belong_to to the item and order, and record the pricing and/or quantity for that order.
HMT vs HABTM? There are so few times that all you need is a many-to-many, that I'd almost always go with HMT for the extra ability to add more information to the association.
This seems like a classic case of HABTM, and the example given in the Rails Guides is perfect. The choice comes down to whether you need any other data or logic on the join model itself. If so, then use the HMT, where you will create a third active_record model to serve as the join table. You can name that anything you want. But it seems like HABTM will work for you, and all you need to setup is the join table with the default name (items_orders) in your migration, and rails will take care of everything else for you.
class Order < ActiveRecord::Base
has_and_belongs_to_many :items
end
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders
end

Look up table, enum or a function to return value from key in Rails

I have two models, Tutor and Student. Tutor can have multiple Topics he can cover, and Student can have multiple Topics he would like to learn. There are 10 possible topics (in string).
I am thinking of creating a Topic table, which contain topic strings. But it would create unnecessary repetition of these strings (making table heavy). So I create a Topic table which contains only topic key.
However, I am undecided about how to retrieve value:
First, I can create another lookup table, which maps a key to string value. This will result in an extra merging step.
Second, I can have a class function that belongs to Topic, that returns string from value.
Which way would be more efficient in my situation? Is there a better approach that I haven't thought of?
Thank you.
It depends. IMO "topics" sounds like something that need managing, and may change.
If that's the case, there should be a topic table, with an id, name, probably a description, etc. Both tutors and students would have_many topics :through a join table. Topics would belong_to both.
There are several implementations options, including a polymorphic association of topics.
Assuming a Tutor model could be rolled into a User model with role assignments, setup a has_and_belongs_to_many relationship between Users and Topics. This sets up a join table where the foreign keys are listed to join the heavier rows together.
class User < ActiveRecord::Base
has_and_belongs_to_many :topics
end
class Topic < ActiveRecord::Base
has_and_belongs_to_many :users
end
See the Rails Guide for additional description.
The alternative is to use just a has_many association but it lacks a join table so the Topic entries will need to be duplicated for each instance.

Confusion over the tables for has_one and has_many

In the Rails ActiveRecord Associations guide, I'm confused over why the tables for has_one and has_many are identical:
Example tables for has_many:
customers(id,name)
orders(id,customer_id,order_date)
Example tables for has_one:
these tables will, at the database level, also allow a supplier to have many accounts, but we just want one account per supplier
suppliers(id,name)
accounts(id,supplier_id,account_number) #Foreign Key to supplier here??
Shouldn't the tables for has_one be like this instead:
suppliers(id,name,account_id) #Foreign Key to account here
accounts(id,account_number)
Now because the account_id is in the suppliers table, a supplier can never have more than one account.
Is the example in the Rails Guide incorrect?
Or, does Rails use the has_many kind of approach but restricts the many part from happening?
If you think about this way -- they are all the same:
1 customer can have many orders, so each order record points back to customer.
1 supplier can have one account, and it is a special case of "has many", so it equally works with account pointing back to supplier.
and it is the same case with many-to-many, with junction table pointing back to the individual records... (if a student can take many classes, and one class can have many students, then the enrollment table points back to the student and class records).
as to why account points back to supplier vs account points to supplier, that one I am not entirely sure whether we can have it either way, or one form is better than the other.
I believe it has to do with the constraints. With has_one rails will try to enforce that there is only one account per supplier. However, with a has_many, there will be no constraint enforced, so a supplier with has_many would be allowed to exist with multiple accounts.
It does take some getting used to when thinking about the relationships and their creation in rails. If you want to enforce foreign keys on the database side (since rails doesn't do this outside of the application layer), take a look at Mathew Higgins' foreigner
If I understand your question correctly, you believe there is a 1:1 relationship bi-directionally in a has_one/belongs_to relationship. That's not exactly true. You could have:
Class Account
belongs_to :supplier
belongs_to :wholesaler
belongs_to :shipper
# ...
end
account = supplier.account # Get supplier's account
wholesaler = Wholesaler.new
wholesaler.accounts << account # Tell wholesaler this is one of their suppliers
wholesaler.save
I'm not saying your app actually behaves this way, but you can see how a table -- no, let's say a model -- that "belongs to" another model is not precluded from belonging to any number of models. Right? So the relationship is really infinity:1.
I should add that has_one is really a degenerate case of has_many and just adds syntactic sugar of singularizing the association and a few other nits. Otherwise, it's pretty much the same thing and it's pretty much why they look alike.

Resources