How could I best structure these similar models? - ruby-on-rails

I have n models that represent orders, e.g. EbayOrder and AmazonOrder. Each order model has slightly different fields but they all represent the same basic thing, an order.
The problem is that I frequently need to query all my orders to display them in views, add up fees and sales totals, and perform other analyses on them. It's tricky to do this when the models are separate entities.
Here are some options I've considered:
STI
The STI table would likely be dozens of columns wide.
Keeping separate models but generating a replica Order model with normalized data
This would be a slave 'generic' model that would be a one-to-one duplicate of each type of order, but have more normalized cleaner data. I don't like this option because of the data duplication required.
Normalize all order types into a single Order model
Would be ideal if the data were similar enough, but they're pretty different among order types.

Another option would be to use Polymorphic associations:
class Order < ApplicationRecord
belongs_to :specific, polymorphic: true, dependent: :destroy
end
class EbayOrder < ApplicationRecord
has_one :order, as: :specific
end
class AmazonOrder < ApplicationRecord
has_one :order, as: :specific
end
In Order model you can keep shared attributes, but for custom attributes you can use separate model. Anyway this approach makes querying a database more complex.
Also if you use Postgres then you can review another approach: keep all orders in one table but create JSON column for custom attributes.

Related

Many-to-Many: Simple vs Rich in Rails

I have a question about these two relationships as I think I'm overthinking it and have confused myself quite a bit.
So they both involve a new table that sits in between the two tables you want to join.
A simple M2M gets the foreign keys from the other two tables (so example would be "blog_posts_categories" table would get the blog_post_id and the category_id foreign keys). Then for the associations, the blog_post and category models would have has_and_belongs_to_many associations with each other but the joined table gets no model.
for a rich M2M, a third joined table is created. This table gets the foreign key from the other two tables, and those two tables get the foreign key of the joined table. Then for the rails association, the joined DOES get a model, and it belongs_to the other two corresponding models. And those two models has_many of the joined table model
Am I anywhere near close to being right? I think my issue is that I keep conflating the table with the model or at least for the simple many-to-many since I keep expecting there should be a model to go along with the table.
Thanks for any direction that can be given.
Your conceptual understanding of the two are almost there. Rather than thinking of them as simple vs. rich, I prefer to think of them as implicit vs. explicit.
Take for example, two models, Book and Author.
With a has_and_belongs_to_many, Rails will implicitly create a join table between your two models. I.e. books_authors.
You can also explicitly create the join table, say, Authorship, that belongs_to both. (Book and Author will then have has_many :authorships.)
In both cases, your domain model will look the same:
Book (1)--(n) Authorship (n)--(1) Author
Now, here is the opninionated part. I prefer to use the explicit approach, as this is easier to grasp, conceptually, and makes it easier to add additional fields to your join table. Say that for example you want the Authorship to be ordered, you can easily add an order column to your Authorship model and use that accordingly.
A third relationship table is suitable for many-to-many relations. You can do something like this:
class BlogPost < ActiveRecord::Base
has_many :blog_post_categories
has_many :categories, through: :blog_post_categories
end
class Category < ActiveRecord::Base
has_many :blog_post_categories
has_many :blog_posts, through: :blog_post_categories
end
class BlogPostCategory < ActiveRecord::Base
belongs_to :blog_post
belongs_to :category
end
The third model is very simple and its overhead is basically negligible. Also it is flexible and extensive if you want to attach more information to that relationship (for example, priority or timestamp). I personally prefer having a standalone table for relationships rather than a few more columns in the blog_posts table.
Here is a relevant blog post: Why You Don’t Need Has_and_belongs_to_many Relationships.
Perhaps you can explain the tradeoffs you're considering here?

How do I add items to join tables in Rails? Do they get their own models?

I have two models: Reader and Magazine. I obviously want to have a join table, readers_magazines, to represent which magazines each reader is subscribed to.
So I create my Reader model (with fields like name, address and age) and my Magazine model (with fields like Title and Active?). In each model I write has_and_belongs_to_many of the other.
Then I write a migration, CreateReadersMagazinesJoin, and write:
create_join_table :readers, :magazines do |t|
t.index 'reader_id'
t.index 'magazine_id'
end
And migrate the database. All good.
My question is... what now? Do I create a model for the join table? That seems wrong, and yet I do need some model validations (I don't want to represent the same User-Subscription combo twice). So do I write a model for it and manually specify the database table to use?
What is the correct procedure in this situation?
From the rails guides:
A has_and_belongs_to_many association creates a direct many-to-many
connection with another model, with no intervening model.
That means that using a has_and_belongs_to_many association might be easier for you to setup (because there is no middle model) but you don't have any kind of control over the join table, validations, etc. HABTM are normally considered harmful and you would want to use a has_many :through relationship instead, that is the same but having a model to represent the join table, so you have control over everything.
More information here.
Do I create a model for the join table? That seems wrong, and yet I do need some model validations.
If you don't want to add an additional fields to the join table, you don't really need to create the model for it.
And in most cases the validation that are required by yours, can be applied to one or both the tables.
I don't want to represent the same User-Subscription combo twice.
What do you mean twice, I havent seen the cases for that, however since the User-Subscription is something external the for the join table, you can explain more crear.
Where you have the choice between a habtm relationship or separate model, I would consider whether you need a rich amount of data in the join. In the present case, I think you do need extra data in the join. For example, a subscription would have a start date and an end date. It may also have a billing date and billing amount. You would not normally populate this information in a join table but rather in a stand alone model.
I would call the join table subscriptions and thus create a model Subscription. Then you can do it like this:
class Reader < ActiveRecord::Base
has_many :subscriptions
has_many :magazines, through: :subscriptions
end
class Subscription < ActiveRecord::Base
beongs_to :reader
belongs_to :magazine
end
class Magazine < ActiveRecord::Base
has_many :subscriptions
has_many :readers, through: :subscriptions
end

How do I define many to many associations and the related properties in Rails (Active record)?

I am wondering how do I add additional properties to a many to many relationship.
I have two models that share a many to many relationship, Company and Profession.
Many professionals could belong to a Company
So my Company model looks like below
class Company < ActiveRecord::Base
has_and_belongs_to_many :professions
end
The same people in the same profession could belong to multiple companies as well
so
class Profession < ActiveRecord::Base
has_and_belongs_to_many :companies
end
Now I need to associate an hourly rate which could be different for each of the companies for the same profession. I am not very sure where to introduce the hourly rate property? Even if I were to add that to the joining table, how do I access that rate using active record?
This is a typical scenario where you select has_many through over habtm. As a rule, if you only need to associate two models, no other info needed to be stored in the association, use habtm. For most cases, you have to use has_many through. You case falls under this scenario.
You want to save the hourly rate in the table that associates a Profession and a Company. If you have existing data that you want to migrate, you may want to look at this post How to migrate has_and_belongs_to_many to has_many through?. If you can drop the joins table you use for the habtm association, just drop it and create a new table.

Problems With or Alternative To Unidirectional has_and_belongs_to_many association

I have two components to my application, for the most part, data entry/tracking and data reporting.
I have a basic data tracking object, let's call it Meter:
class Meter < ActiveRecord::Base
has_many :activity_records
end
I have a collection of different reporting objects, like tabular reports, charts, widgets, etc. All of these objects, per user configuration, refer to a collection of Meters as data sources.
I model this relationship as has_and_belongs_to_many, through a join table: a Widget has many Meters; Meters can be referenced by many Widgets.
class Widget < ActiveRecord::Base
has_and_belongs_to_many :meters
end
Now, I don't think Meters actually need to know which reporting objects reference them. A Meter should be able to exist blithely, collecting data and (through a Service object) responding to data requests. I should never, ever, refer to #meter.widgets.
So, do I need, in the Meter model, to declare half a dozen different habtm relationships for all the different reporting objects?
Concrete Questions:
1. Are there unintended consequences from omitting the habtm declaration on Meters?
2. Is there a better way to design this relationship?
With regard to the second question, I have played with the concept of a MeterSet but found it a bit over-engineered:
class Widget < ActiveRecord::Base
has_one :meter_set, as: :requester
has_many :meters, through: :meter_set
end
class MeterSet < ActiveRecord::Base
belongs_to :requester, polymorphic: true
has_and_belongs_to_many :meters
end
I've moved the location of the join from one model to another... and I suppose I've reduced the number of tables in my application, overall?

Multiple Table Inheritance vs Polymorphic

I’m creating an app that allows a user to create a course and include different types of resources (document, video, quiz) in the course. I’m having a hard time figuring out the best way to set up association between a course and its resources.
Right now, I have:
class Course < AR::Base
has_many :documents
has_many :videos
has_many :quizzes
end
class Document <AR::Base
belongs_to :course
end
class Video <AR::Base
belongs_to :course
end
class Quiz <AR::Base
belongs_to :course
end
Documents, Videos, and Quizzes all have some common attributes (e.g., name, description) but also many attributes that differ. I considered using STI and having them inherit from a single class called “Lessons.” I decided against this approach because I need multiple controllers for each class.
Ultimately, I want to be able to perform operations on all the resources that belong to a course so for example list all the documents, videos, and quizzes together and display them as a sortable list.
Any suggestions on how best to set up this model. It seems as if I could do it two ways:
Reverse polymorphic has_one association (see example)
Multiple Table inheritance (see example)
I’m a rails noob so I’m having a hard time evaluating which approach is best for this situation. Any advice would be appreciated!
Ultimately, I want to be able to perform operations on all the resources that belong to a course so for example list all the documents, videos, and quizzes together and display them as a sortable list.
Either approach is doable, but when I read this requirement, it seems you'd be best off with STI. STI is a natively supported feature in rails and allows you to query and manipulate different types of resources within the same collection.

Resources