relating has_and_belongs_to_many table to a fourth table - ruby-on-rails

Thanks for the help first of all.
I have User(table) and has_and_belongs_to_many Products(table) with a joining table UserJoinProducts. I have this and works.
My problem is..
I want to create a table where Users have a records when they CheckIn or Out a Product and also have record of each Iteration.
I want user to have a record for each time they check_in or out the same product.
|user|produ| _date-- |in |
|bob | eggs |1/1/2016| X |
|bob | Coke |1/1/2016| X |
|bob | eggs |1/5/2016| -- |
|bob | Coke |1/7/2016| -- |
|bob | eggs |1/9/2016| X |
Click here for A sad example of my table ;) lol
Thanks Again For your Help.

I wouldn't do it this way. You're probably better off using has_many through relation for User and Product models.
And then use the through table to store the check_in or check_out datetime fields. So this way you'll have a model for your joining table to access these fields.
It's always recommended to use has_many through as whenever you use has_and_belongs_to_many, most of the time you end up in a similar situation, and would want to change the architecture.

Related

Seeking help to prevent adding table with too many columns

In my app I have a model called Post, that has a belongs to association with the Blog. That's how they look like:
app/models/post:
class Post < ApplicationRecord
belongs_to :blog
end
app/models/blog:
class Blog < ApplicationRecord
has_many :posts
end
The app will collect and store posts of a lot of content management systems (like Wordpress), and there are columns that should only exist for certain types of posts. I would like to know if it's possible to have some kinda of hierarchy, like this:
Post
- WordpressPost
- DrupalPost
This is important because that are columns that should only exist when it's a WordpressPost, for example.
I've tried to understand something called Polymorphic association (I really did), but I just can't get my head around the concept, and I'm also not sure if that's what's going to allow me that flexibility.
Right now the posts table look like this:
+------------+---------------------------------------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------------------------------------------------------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| site_id | bigint(20) | NO | MUL | NULL | |
| status | enum('auto_draft','draft','future','pending','private','publish','trash') | NO | | NULL | |
| date | datetime | NO | | NULL | |
| title | varchar(255) | NO | | NULL | |
| url | varchar(255) | NO | | NULL | |
| body | longtext | NO | | NULL | |
| created_at | datetime(6) | NO | | NULL | |
| updated_at | datetime(6) | NO | | NULL | |
+------------+---------------------------------------------------------------------------+------+-----+---------+----------------+
But things like:
- status
- date
Should only exist for Wordpress posts. I really would like to understand the best way to do what I want.
Thank you.
when mapping OO inheritance to relational database we traditionally have three ways of doing it (as highlighted in this link):
Single Table Inheritance(STI): One table with all the attributes used by all the subclasses. This way is more suitable for cases where the attributes don't change much from one subclass to another.
Class Table Inheritance(MTI): One dad table with common attributes, and child tables for each subclass with their specific attributes and a foreign key to the dad table. Good when there are many different attributes between each subclass and even different associations.
Concrete Table Inheritance: One table for each class with all the needed attributes. May be good if there are little common attributes.
Rails allow us to create a Single Table Inheritance(STI) and we can also use the Concrete Table Inheritance. But it doesn't have a built in way of dealing with the Class Table Inheritance(known as well as Multiple Table Inheritance - MTI), what doesn't prevents you from creating your own way of doing it, like it is done in this post.
Rails also allow us to have a Polymorphic Association, which is useful when we want an association of one type for certain records and of another for others, this can be used for inheritance, but was not actually designed for this. In this link you will find an example of all the three types and how to use them.
Another way to achieve it is by having an options jsonb field. This is a good approach when you have unpredictable/changeable fields that will vary from one record to another. Which is a much simpler way but must be well designed, cause may have some downsides.
Proposed Solution:
I wouldnt use:
STI: as you said it is going to be a lot of different management systems, I believe it is not a good idea because you would have too many unused columns.
Concrete Table Inheritance: I guess you want to make it easy to add new management systems as you go, treating them as one and ignoring their nuances. So it is not really a good option because it would make it harder to understand their commonalities.
I could use:
MTI: it would be a good database design, but as youre using rails you would have to deal with it your own way and it could add some complexity to the code. I would use it if I were more concerned about having a normalized database.
Polymorphic Association: would add some complexity to the database when compared to the MTI and to make it usable as an inheritance would add some complexity to the code as well. I prefer using it with an actual polymorphic association.
I would use:
An options json field: I believe you will need to access this specific attributes just in some specific moments and that each different management system wouldn't have other associations. So I guess it would be the most straightforward solution being totally supported by both rails and the database without adding complexity to neither one.
Ps: If you have just some little different attributes, I would use a STI instead.
As u need different set of columns for different post type based on the cms(post source),
you have to setup specific set of columns required by each post source.
setting up all the required columns in the same Post model itself is not recommended.
because scenarios like removing a post source or adding a post source into your application will be hard in future.
The solution I suggest is to setup a separate model for each post source like
class WordpressPost < ActiveRecord::Base
belongs_to :post
end
class JoomlaPost < ActiveRecord::Base
belongs_to :post
end
Each of these model will have their own specific set of columns like status and data for WordpressPost model and so on to hold the post data along with the post_id.
Then, the Post model can be updated like
class Post < ApplicationRecord
belongs_to :blog
has_one: wordpress_post
has_one: joomla_post
end
Additionally, in the posts table,
you have to include a column called 'post_source' which stores the integer values to denote the post source type.
the reference value for each integer value can be obtained using an helper function like
def get_post_source(source_val)
if (source_val == 1)
source_info = { name: 'Wordpress', model: 'WordpressPost' }
elsif (source_val == 2)
source_info = { name: 'Joomla', model: 'JoomlaPost' }
end
return source_info
end
Using this approach,
you can maintain scalability with ease.
If you have to remove joomla posts entirely from your application, then you can do that without affecting the structure of any other model.
Similarly, you can also add other new post sources like drupal, medium and so on by adding separate models for each.
I think there is another way to do that, changing the database. Like mongoDB, it's easier to meet your goal. If you dont't like mongoDB you can choose Postgres either, which can store hash like data structure.
Back to your polymorphic problem
class Person < ActiveRecord::Base
has_one :address, :as => :addressable
end
class Company < ActiveRecord::Base
has_one :address, :as => :addressable
end
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
end
In above example, address is the common part.
So you should define two classes
Post
- WordpressPost
- Content
- DrupalPost
- Content
From the answer from Rafael, you could use the singl etable inheritance.
first, allow null data in the status and date columns (you need to create a migration for that), and even create a new column that stores the content management system so later you could do a validation before saving (ensuring that the column exist in the cases that are needed) or to make querys only on some of your content by cms.

Ruby on Rails: Join Tables Concept

So I have been out of the coding game for a while and recently decided to pick up rails. I have a question about the concept of Join tables in rails. Specifically:
1) why are these join tables needed in the database?
2) Why can't I just JOIN two tables on the fly like we do in SQL?
A join table allows a clean linking of association between two independent tables. Join tables reduce data duplication while making it easy to find relationships in your data later on.
E.g. if you compare a table called users:
| id | name |
-----------------
| 1 | Sara |
| 2 | John |
| 3 | Anthony |
with a table called languages:
| id| title |
----------------
| 1 | English |
| 2 | French |
| 3 | German |
| 4 | Spanish |
You can see that both truly exist as separate concepts from one another. Neither is subordinate to the other the way a single user may have many orders, (where each order row might store a unique foreign_key representing the user_id of the user that made it).
When a language can have many users, and a user can have many languages -- we need a way to join them.
We can do that by creating a join table, such as user_languages, to store every link between a user and the language(s) that they may speak. With each row containing every matchup between the pairs:
| id | user_id | language_id |
------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 4 |
| 4 | 2 | 1 |
| 5 | 3 | 1 |
With this data we can see that Sara (user_id: 1) is trilingual, while John(user_id: 2) and Anthony(user_id: 3) only speak English.
By creating a join table in-between both tables to store the linkage, we preserve our ability to make powerful queries in relation to data on other tables. For example, with a join table separating users and languages it would now be easy to find every User that speaks English or Spanish or both.
But where join tables get even more powerful is when you add new tables. If in the future we wanted to link languages to a new table called schools, we could simply create a new join table called school_languages. Even better, we can add this join table without needing to make any changes to the languages SQL table itself.
As Rails models, the data relationship between these tables would look like this:
User --> user_languages <-- Language --> school_languages <-- School
By default every school and user would be linked to Language using the same language_id(s)
This is powerful. Because with two join tables (user_languages & school_languages) now referencing the same unique language_id, it will now be easy to write queries about how either relates. For example we could find all schools who speak the language(s) of a user, or find all users who speak the language(s) of a school. As our tables expand, we can ride the joins to find relations about pretty much anything in our data.
tl;dr: Join tables preserve relations between separate concepts, making it easy to make powerful relational queries as you add new tables.

Order by A then B using Ruby on Rails Model

This is not a homework question. I am trying to learn more.
I have the following entities with attributes
Manufacturer {name} //Store Manufactueres
Model {manufacturer_id, name} //Store Models
Tint {manufacturer_id, model_id, front, side, rear} //Store measurements
I have the follow data in my Tint entity. Alphabets stands for different manufacturer name and models.
Manufacturer | Model | Front | Side | Rear |
-------------+-------+-------+------+-------
A | AD | 10 | 10 | 10 |
B | AB | 10 | 10 | 10 |
A | AA | 10 | 10 | 10 |
A | AC | 10 | 10 | 10 |
B | AA | 10 | 10 | 10 |
A | AB | 10 | 10 | 10 |
When I print it out in view, I would like to have it sorted based on Manufacturer name and then Model. So the result will be as below. The name of the Manufactures will be sorted alphabetically, then Models.
Manufacturer | Model | Front | Side | Rear |
-------------+-------+-------+------+-------
A | AA | 10 | 10 | 10 |
A | AB | 10 | 10 | 10 |
A | AC | 10 | 10 | 10 |
A | AD | 10 | 10 | 10 |
B | AA | 10 | 10 | 10 |
B | AB | 10 | 10 | 10 |
I have setup the model to make sure Manufacturer and Model is a distinct pair of values.
My question is since I am referencing using manufacturer_id and model_id, how can I get the name of the Manufacturer and Model from Manufacturer and Model table.
In my tints_controller.rb, I have #tints = Tint.all.order(:manufacturer_id). However, it will only sort based on the manufacturer_id (as in numbers) instead of the name of the manufacturer.
I know that I can do it in SQL way (SELECT, FROM, WHERE) in RoR model. However, I would like to know is it possible to use ActiveRecord to sort the data based on their name.
If I understand correctly, you have 3 models, Tint, Manufacturer and Model. I am assuming you have the appropiate has_many and belongs_to associations setup correctly.
Tint.rb
belongs_to :workspace
Manufacturer.rb
has_many :models
has_many :tints, through: :models
Model.rb:
belongs_to Manufacturer
has_many :tints
You need to first join the three models together, and then order by some criteria
tints_controller.rb
#tints = Tint.joins(model: :manufacturer).order('manufacturers.name, models.name').pluck('manufacturers.name, models.name, tints.front, tints.side, tints.rear')
That will give you all tints records and they appropiate models and manufacturers.
Any time you have the id of an entity in Rails, you can easily retrieve other associated fields simply by instantiating that entity:
#manufacturer = Manufacturer.find(params[manufacturer_id])
Then it's a simple matter to retrieve any of the other fields:
#manufacturer_name = #manufacturer.name
If you need a collection of manufacturers or manufacturer names, then it's advisable to build yourself an ActiveRecord::Relation object immediately via a scoped query (as you already know). I have no idea what your criteria are, otherwise, I'd supply some sample code. I can tell you that your scoped query should include an .order clause at the end:
#manufacturers = Manufacturer.where("some_column = ?", some_criterion).order(:sort_field)
In the above example, :sort_field would be the field by you want to sort your ActiveRecord::Relation. I'm guessing in your case, it's :name.
All this having been said, if you want fancy sorted tables, you should look into the JQuery DataTables gem. DataTables can do a lot of the heavy lifting for you, and it's convenient for your users because they can then sort and resort by any column you present.
In your tints_controller.rb, instedad of
#tints = Tint.all.order(:manufacturer_id)
please write:
#tints = Tint.all.order(:manufacturer_id, :model_id)
Answer to my question:
In tints_controller.rb, I wrote
#tints = Tint.joins(:manufacturer, :model).order("manufacturers.name ASC, models.name ASC") to join the table and order them accordingly.
I tried the answer provided by #Goston above and I had an issue when I was trying edit the tints. It did not allow me to edit.
Note: Answer provided by #Goston will order them, but it broke the edit function for my case.

Retrieving data in Rails both ways from 2 column join table

I have a ChunkRelationship model with a table that looks like this:
+----+---------------+----------------+---------------------+---------------------+
| id | chunk_id | chunk_partner | created_at | updated_at |
+----+---------------+----------------+---------------------+---------------------+
| 1 | 1 | 2 | 2010-02-14 12:11:22 | 2010-02-14 12:11:22 |
| 2 | 2 | 1 | 2010-02-14 12:11:22 | 2010-02-14 12:11:22 |
+----+---------------+----------------+---------------------+---------------------+
Both entries are foreign keys to a Chunk model. Right now, the relationship is being saved twice, once in both directions ( 2 => 1 and 1 => 2). But the relationship can be saved once, because if one ID is known then the other can be found (What is this type of table called?).
I am wondering what the Rails way of doing that would be. I was thinking of creating a before_validation callback on the ChunkRelationship model and taking the smallest number of the two and always saving that to the chunk_id column, which would allow for checking for duplicates easier before saving. But from there I'm not sure how I would retrieve them.
The intended end result would be for chunk.partners to return all the rows paired with it, no matter which column either one is in.
Perhaps you are looking for the has_many_and_belongs_to association: http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
This should create a many-to-many relationship which I believe you are describing.

Dynamic categories criteria with matching products rails 3

I'm working on a new rails project, and in this project I have products and product categories.
So these categories are very different from each other, to name some, Boats, Houses, Cars.
The car category might have criterias like "Mph", "Model", "Brand", "Year" and so on. Where the house category will have something like "Rooms", "Year", "City", "Postal Code" etc.
I would like this to be very dynamic, so that i would be able to add/remove criterias and add/remove categories from a backend panel.
Now to my question, i have been playing around with this, and i can't really figure out the logic of this concept, i have tried some solutions, however they are very weird and quite inefficient. Maybe some hardcore rails coder could give me a hint, on how to solve this puzzle?
So the best solution i could come up with, was this:
Four models :
_______________________
| Product.rb |
-----------------------
| id | integer |
-----------------------
| category_id | integer |
-----------------------
| Title | string |
-----------------------
| Description | text |
-----------------------
_______________________
| Category.rb |
-----------------------
| id | integer |
-----------------------
| Title | string |
-----------------------
| Description | text |
-----------------------
_______________________
| Criteria.rb |
-----------------------
| id | integer |
-----------------------
| category_id | integer |
-----------------------
| Name | string |
-----------------------
| Default | string |
-----------------------
| Description | text |
-----------------------
_______________________
| ProductInfo.rb |
-----------------------
| id | integer |
-----------------------
| product_id | integer |
-----------------------
| Name | string |
-----------------------
| Value | text |
-----------------------
How it's connected :
Criteria.rb is connected to Category.rb with a category_id and has_many/belongs_to relation
Product.rb is connected to Category.rb with a category_id and has_many/belongs_to relation
ProductInfo.rb is connected to Product.rb with a product_id and has_many/belongs_to relation.
Category.rb is the heart og this solution. The category model, both have many products and criterias.
How it should work, in reality :
In the show category page, i would first print out all the criterias for the given category.
Afterwards i would make a #products.each do |product|.
In the #products.each block, i would make a #category.criterias.each do |criteria|.
In the #category.criterias.each block, i would then run something like product.productinfos.where(:name => criteria.name).
And then run it one by one.
Conclusion, this solution do work, however i doubt that it is the best solution. It will make an extremely big loadtime, with high traffic and many data. And i will need to write very weird and unreadable code.
This is a rather long question, and it might be very confusing, so if there is anything please just say. Also, i have searched quite alot for a question like this, both on Stackoverflow, and on google but i have not been able to find anything like this.
Oluf Nielsen.
In my opinion, it's better not to define additional tables to handle this due lots of performance issues. My preference to handle such things is to use a serialized column in the products table. Ability to search directly in the database is reduced with this approach, but then you wouldn't want to do that anyway. To handle search, you have to add some sort of indexed searching mechanism. Like acts_as_ferret or even Solr or ElasticSearch.
If you are using postgres check out https://github.com/softa/activerecord-postgres-hstore
For Mysql, use the rails's built in 'store'
http://api.rubyonrails.org/classes/ActiveRecord/Store.html
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
store :settings
end
To set criteria for each category do something similar to this:
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
def criteria
#criteria_list ||= self[:criteria].split('|')
#criteria_list
end
def criteria=(names)
self[:criteria] = names.join('|')
end
end
Everytime a product is added to a category, check if all of the criteria in that category is available in the product's properties hash keys. If not, add it with a default value if needed.
You can also setup accessors for the properties hash store using a proc that dynamically gets the accessor names from the all the criteria field of the categories of the product? (not sure about this, cause I haven't done this before)
You can also look into using STI (Single table Inheritance) using a type field in your products table. (It's well documented) This approach is slightly better 'cause when products move from one category to another, the properties won't change.
class Gadget < Product
store_accessor :manufacturer, :model
end
class Phone < Gadget
store_accessor :os, :touch_screen, :is_smart
end
Hope this helps
Or else, second approach would be go with a nosql database. Try mogodb with mongoid, which is quite stable. This will suite your requirements for variable attributes very well. Also you can add any other categories later with very ease.
As far as I can see, with mysql, you will end up creating multiple dbs for storing this dynamics data and that is bound hamper the performance.
UPDATE -
Apart from the point that your data can be flexible with nosql, there are many things you need to consider before shifting there. I just suggested based on fact that you need flexible database structure. Start with mogodb docs, they are good starting point.

Resources