Perform a join on two Rails models implementing single table inheritence - ruby-on-rails

I have three models, let's call them Product, TemplateProduct and ReadyProduct. There is only one table for these, the products table, and both TemplateProduct and ReadyProduct inherit from the Product model. There is a has_many/belongs_to association between TemplateProduct and ReadyProduct. Templates are used to lay out general characteristics for products, Readys are used to customize the products and what are actually made available for view by the customer. Each TemplateProduct has an id and each ReadyProduct has a template_product_id which ties to it's template.
The project is built using Rails 5.
What I want to be able to do is to gather a list of TemplateProducts, then get a count of each templates associated ReadyProducts and do so in such a way that won't hammer the database. I understand ActiveRecord associations but my SQL is weak and I have only a limited understanding of joins. I can gather a list of TemplateProducts with a simple Product.where(conditions) but I don't know what to once I have this. For the sake of flexability sake I want to be able to base my ReadyProduct count off of this initial collection as sometimes I'll need the additional count and sometimes I won't. I'm sure there must be a simple way to do this but I haven't found a solution.

If you just need a mapping of TemplateProduct ids to ReadyProduct counts, then all you need is:
TemplateProduct.joins(:ready_products).group(:id).count
If you want TemplateProduct instances with a baked in ReadyProduct count, then you'll need this instead:
tps = TemplateProduct.joins(:ready_products).select('products.*, COUNT(ready_products_products.id) ready_product_count').group(:id)
tps.first.ready_product_count
#=> 6
ready_products_products gets defined by Rails, it prefixes the actual table name (products) with the model name's "table form" (pluralized, snake case, lower case, ready_products), joined with an underscore.

Related

How to create an Order Model with a type field that dictates other fields

I'm building a Ruby on Rails App for a business and will be utilizing an ActiveRecord database. My question really has to do with Database Architecture and really the best way I should organize all the different tables and models within my app. So the App I'm building is going to have a database of orders for an ECommerce Business that sells products through 2 different channels, a subscription service where they pick the products and sell it for a fixed monthly fee and a traditional ECommerce channel, where customers pay for their products directly. So essentially while all of these would be classified as the Order model, there are two types of Orders: Subscription Order and Regular Order.
So initially I thought I would classify all this activity in my Orders Table and include a field 'Type' that would indicate whether it is a subscription order or a regular order. My issue is that there are a bunch of fields that I would need that would be specific to each type. For instance, transaction_id, batch_id and sub_id are all fields that would only be present if that order type was a subscription, and conversely would be absent if the order type was regular.
My question is, would it be in my best interest to just create two separate tables, one for subscription orders and one for regular orders? Or is there a way that fields could only appear conditional on what the Type field is? I would hate to see so many Nil values, for instance, if the order type was a regular order.
Sorry this question isn't as technical as it is just pertaining to best practice and organization.
Thanks,
Sunny
What you've described is a pattern called Single Table Inheritance — aka, having one table store data for different types of objects with different behavior.
Generally, people will tell you not to do it, since it leads to a lot of empty fields in your database which will hurt performance long term. It also just looks gross.
You should probably instead store the data in separate tables. If you want to get fancy, you can try to implement Class Table Inheritance, in which there are actually separate but connected table for each of the child classes. This isn't supported natively by ActiveRecord. This gem and this gem might be able to help you, but I've never used either, so I can't give you a firm recommendation.
I would keep all of my orders in one table. You could create a second table for "subscription order information" that would only contain the columns transaction_id, batch_id and sub_id as well as a primary key to link it back to the main orders table. You would still want to include an order type column in the main database though to make it a little easier when debugging.
Assuming you're using Postgres, I might lean towards an Hstore for that.
Some reading:
http://www.devmynd.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination
https://github.com/devmynd/hstore_accessor
Make an integer column called order_type.
In the model do:
SUBSCRIPTION = 0
ONLINE = 1
...
It'll query better than strings and whenever you want to call one you do Order:SUBSCRIPTION.
Make two+ other tables with a foreign key equal to whatever the ID of the corresponding row in orders.
Now you can keep all shared data in the orders table, for easy querying, and all unique data in the other tables so you don't have bloated models.

Rails: Multiple trees for a single item

I want to categorize objects in multiple trees to reflect their characteristics and to build a navigation on.
So, given the following trees:
Category1
-Category-1-1
-Category-1-2
Category2
-Category-2-1
-Category-2-2
--Category-2-2-1
An object could e.g. belong to both Category-1-2 and to Category-2-2-1.
The goal is to be able to fetch all objects from the database
that belong to a certain category
that belong to a certain category or its decendants
A more practical example:
A category might have a hierarchy of 'Tools > Gardening Tools > Cutters'.
A second category: 'Hard objects > Metal objects > Small metal objects'
An object 'Pruners' would be categorized as belonging to 'Cutters' as well as 'Small metal objects'.
I want to be able to
retrieve all 'Gardening Tools' -> 'Pruners'
retrieve all Category children of 'Gardening Tools' -> 'Cutters'
retrieve all 'Hard objects' -> 'Pruners'
retrieve all 'Hard objects' that are also 'Cutters' -> 'Pruners'
retrieve all 'Soft objects' that are also 'Cutters' -> []
Any pointers? I have briefly looked at closure_tree, awesome_nested_sets etc., but I am not sure they are a good match.
Please note that the code here is all pseudo code.
I would use ancestry gem and would model your data with three model classes.
This way your data is normalized and it's a good base to build on.
Category - ancestry tree
has_may Memberships
has_may Products through Memberships
Membership
belongs_to Category
belongs_to Products
Products
has_may Memberships
has_may Categories through Memberships
From there on you need to figure out how to perform the equerries efficiently.
My way of doing this is to understand how to do it with SQL and then figure out how to express the queries with activercord's DSL.
Some resources:
http://railsantipatterns.com/ This book has some examples of complex SQL queries turned into reusable scopes and helpers
http://guides.rubyonrails.org/active_record_querying.html#joining-tables Rails's documentation, see section on joins and includes
http://stackoverflow.com/questions/38549/difference-between-inner-and-outer-join A great explanation of SQL joins
Queries examples:
Find a category.
Category.find(category_id)
Find a category and include it's products inside the specified category.
Category.find(category_id).join(:memberships => :products)
Find a category's sub-tree ind include products
Category.subtree_of(category_id).join(:memberships => :products)
Find all categories a products belongs to.
Product.find(product_id).categories
I just did this and I chose not to use ancestry, but closure_tree because the author says it is faster and I agree with him. Know you need a `has_and_belongs_to_many' between Categories (which I like to call tags whenever I add multiple to a single object) and Objects.
Now the finders, the bad news is that without your own custom query you might not be able to do it with one. Using the gems methods you will do something like:
Item.joins(:tags).where(tags: {id: self_and_descendant_ids })
The code is clean and it executes two queries, one for the descendant_ids and another one in Objects. Slight variations of this, should give you what you need for all except the last. That one is tough and I haven't implemented it (I'm in the process).
For now, you will have to call tag.self_and_ancestor_ids on both (Query count: 2), all items in those tags (Query count: 4) and intersect. After this, some serious refactoring is needed. I think we need to write SQL to reduce the number of queries, I don't think Rails query interface will be enough.
Another reason I chose *closure_tree* was the use of parent_id, all siblings share it (just like any other Rails association) so it made it easier to interface with other gems (for example RankedModel to sort).
I think you could go for one of the tree gems, personally I like Ancestry. Then make an association for each category to have many objects and each object can belong to many categories.
Have you stumbled on any problems already or are you just researching your options?

Rails: associations in app with one model

I've read so many Rails books/tutorials, but when it comes time to actually make the app, I keep tripping over myself, so I'm trying this little exercise to help me get it better.
I have an app with 3 classes (Link, Url, Visit) that have associations defined between them, such as has_one, belongs_to etc. This allows me to call methods like Link.url
If I were to convert this into an app with a single model, is it possible to create the convenience methods (such as Link.url) even though there are no relationships between models, because there is only one model.
This might not be the 'Rails way' but if you answer this question it'll help me get it more.
I guess another way to ask this is, do the Rails associations only exist because the tab
Thanks
Models exist to represent tables in a database. If you have 3 different conceptual objects, then you need 3 different models. Keeping those objects separate and in different tables/models is essential to good programming in any language. The relations are there to help you understand the correlation of each object to the others.
If you think all of data from each of the models can be represented in one table sensibly, then combine them in to one model with a name that encompasses all of the data. If you choose this option, you'll use columns for that one table which represent each of the pieces of data you need. Those column names come free in the model when you create the migration. A table with a column named "url" on a model named "Hit" could be used like this:
Hit.first.url

Rails - ActiveRecord - associating a combination of elements to a specific ID

I have an interesting problem that I never had to deal with before. I'm looking for the best way to approach it.
I'm creating an admin site linking students to virtual machines. A Student can sign up for multiple courses and has one virtual_machine_id which depends on the combination of courses they take. An Admin can create new courses and (separately) map combinations of courses to specific virtual_machine_ids.
1) What's the best way to associate a combination of variable elements with a specific id? If the elements were fixed, I would create a table with columns for each element and one column for the virtual_machine_id. But since these elements can change (as admins add or remove courses), how do I map them in a way that I can easily query for a combination and it's associated id?
2) Right now, I have Students mapped to Courses using a has_many :through association and a third table with student_id and course_id. Is this the right way if I need to collect combinations of courses and assign the entire combination a single virtual_machine_id (i can't assign the id to the student because they could potentially have more than one virtual_machine depending on how many courses they take)
I was looking at the EAV (entity-attribute-value) model as a solution but the general consensus seems to be that it's a bad a idea because you lose some ActiveRecord features.
If you wish to use EAV with ActiveRecord ORM you can look at hydra_attribute gem which allows to create new attributes in runtime and has possibility to find/sort/group by them.
Currently the 0.3.2 version doesn't support attribute sets but in the next release I'll add this feature and you will be able to assign the unique attribute collection to each Student record.

Chosing categories rails

Hopefully we have good rails developer who can definitely give correct answer! For 2 days I didn't receive any valid answer for my question
I will explain in a very simple example
Customer is offering product. When he pushes create it gives form. Choose a category. Once he chooses another form will pop up.
Depending on a category, form should have totally different attributes.I can't have Product.new for every category. Reason is they have different attributes(Logicaly true). So do I have to create 100 models for 100 categories
Categories are : cars, apartments, coupons, books and many more
If you can give just one example I will be gratefull and call you expert
Thanks
It sounds like you're getting there. However, I wouldn't have a bunch of models like you're indicating in your question. I would say that you need a Product model and a Category model. The Category model will belong_to Product. The Product model would have many Categories. The Category model can use the acts_as_tree gem so that you can have categories and subcategories. Use javascript or jQuery (there was a recent Railscasts on this) to dynamically change and post a different field with a set of choices based on what was chosen.
EDIT:
I would have three Models; Product, Category, Specification
Product has many Categories
Product has many Specifications through Categories
Category belongs to Product
Category has many Specifications
Specification belongs to Category
This way I can create a product that has several categories. I can create several categories that have several specifications. Specifications are linked to the respective category. This will allow you to have three models and limited number of classes. Once your project is complete, new categories and specifications can be maintained by a web admin instead of a programmer.
This isn't the answer you want, but you're going to need a lot of models.
The attributes associated with an apartment (square meters, utilities, floor of building) are completely different from the attributes associated with a car (make, model, mileage, condition) which are completely different from a book (title, author, publisher, edition, etc). These items are so fundamentally different that there is no way to manage them in a single model.
That being said, there may be a core collection of attributes that might be associated with a product that is for sale (seller, price, terms). You have basically two paths forward:
You could decide to use Single Table Inheritance. In this case, you'd create an abstract class that defines the attributes that are common to all products that you are selling (seller, price, item). You'd then add a "type" column to your database that would be used to determine what type of product it is (mapped to your categories), and define all of the possible attributes in a single table.
You could choose a core set of attributes, and use these as a part of any other object that is considered a product. You'd have multiple tables that would have the full record for any given object.
Without knowing a lot of details about your application, it's hard to make a specific recommendation about which approach is right for you. Your best bet at this point is to spend a lot of time on google with "single table inheritance rails" and "multi table inheritance rails" and figure out which one is right for you (though my gut says multi table).

Resources