How to validate parent-child relationships in Rails? - ruby-on-rails

I have a Category model which can have multiple parent and child categories. I have modelled this using a Hierarchy model which contains parent_category and child_category attributes.
I can validate that the rows are unique using
validates_uniqueness_of :parent_category_id, scope: :child_category_id
This (and the corresponding unique index in database) takes care that there are not multiple rows representing the same parent-child relationship.
But, I want to prevent someone assigning parent of a child as it's child. For ex. if Category A is a parent of Category B, assigning Category A as a child of Category B should result in a validation error.
The only approach I can think of is querying the database in a validate method.
def child_parent_messup
unless Hierarchy.where(child_category_id: parent_category_id, parent_category_id: child_category_id).blank?
errors[:base] << "This child is also a parent of the same class."
end
end
How can this be improved?

What you are referring to is a 'cyclic' relationship - and, IMHO, there is no default validator built into rails for this.

To make it simpler, you can do the following:
Add a collection with has_many relation say, parents, which returns its parent as well its parent's parent
Check whether the children_id is included in the above parents collection
Your design seems little complicated, see whether you can make it simpler. May be SIngle Table Inheritance makes sense here, including for better validation handling. I might be wrong as I don't have much idea about the requirement.

Related

rails STI: specific attributes for subclasses

I'm starting building my first rails app and i already have user model with STI (admin, employee, public and representative all inherits from user model).
but now i want to add specific columns (adress, state, phone) for representative subclass but i cannot apply migration directly for subclass model.
the first solution is to add these columns tu user model but i don't know how th restrict acess to only representant subclass
the second solution is to create a separate contact table and and then use polymorphic association (i want to associate with other model) and add the attributes
my question is what is the best solution for this case? and if there is better solution?
Thanks
Hope you Doing well
I think second option is better then first.
Reason:
1) If this all filed are optional then it will create record with null value but in second case if all filed are optional then record will not be created,there is no need to any entry for that.
2) In future there is requirement of add or use this filed into other model then you can do by easily with polymorphic association .
It sounds like you are abusing the idea of an STI in this case. The general rule, is that you might have different associations for different child models and different behaviour BUT you ALWAYS have all the table columns used by all child models.
The whole Idea for why you would want to use STI is that all models contain the same structure of data, but maybe have different behaviours. In your case, I would suggest using associations (as you suggested yourself) and then add the has_one/has_many in the child model, which would limit the associations scope within the inheritance chain.
It is not possible to restrict columns to only some child models without patching ApplicationRecord. But in any case, even if you manage to make a patch to introduce this kind of behaviour, your database table will still have all the columns for all the tables, thus resulting in bigger database tables due to half empty columns, increased size and reduced performance.
I hope that answers your question.

Rails: Models that inherit columns from parent, and each have additional columns

I'm creating a logbook app that will work for multiple sports (e.g. scuba-diving and sky-diving) and I'm trying to find a way to have children models inheriting not only parent methods, but parent (database) columns.
An image may explain better:
Logbook database sample
So, any models under the category ScubaDiving, FreeDiving and SkyDiving are models that I will be calling from the views.
The models under Parent or Diving however, will never be called, and are only meant to populate their children with database columns (and methods perhaps).
What is the best way to go about creating the models + associations + inheritance ?
From the research I've done, I've read about:
Single Table Inheritance:
Will only pass parent methods, not database columns
Polymorphic Associations:
I compared my situation to the example I found (comments on photos, articles, posts), but my Divespot and Airport (equivalent: Photo, Article) can't have many Spots (eq: Comments).
Multiple Table Inheritance:
Couldn't make it work, but it seemed the closest to what I was looking for?
Simple models:
I could always create a model for each child (with no parent) and repeat the code. I still wanted to see if there was a way to refacto.
I am using Rails 5 and a PostGresql database.
Single Table Inheritance:
Will only pass parent methods, not database columns
Of course all classes inherit the database columns. Since all classes "live" in the same table, they have the same columns. STI is probably not the best solution in your case for another reason - the columns seem to be different for each model, so you would have a bunch of columns being "NULL" for the models that don't have these columns.
I'd suggest considering to create a model / table for each of your classes and then linking the children objects to an object representing the parent. You can then delegate methods like name, location to the Spot, for example. You will probably then need to work with polymorphic associations. You can be a bit fancy and try "proper" MTI, see this guide

Ruby on Rails order a parent model by child attribute

How would I go about doing this? I would like to sort a selection parent model by an attribute on it's children. The parent model has a has many relationship with it's child. I'm not sure how to form the ActiveRecord query to get this.
Example: A thread has many posts. I want to grab a collection of threads ordered by the most recent post associated with it.
I've found a few solutions that convert the selection to an array and do a sort on that array, but I need the selection to stay as an ActiveRecord selection, so I can continue to chain queries onto it.
I would need more information about the layout of your database to give a better example, but you can join another table and then order by that table like so:
Thread.joins(:posts).order("posts.created_at").group("threads.id")
For your example, it would be far more efficient to simply have the children touch the parent:
class Thread < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :thread,
touch: true
end
This will change Thread's updated_at column whenever a post is created or changed, so that thread is considered updated at that time. You may need to actually access the child records in a more complicated example, but for ordering by the date of the child posts this saves you having to get the extra records out of the database when you don't actually need information from that table.
By using just plain queries, with no direct iteration over arrays, you can do something like:
Post.joins(:thread).includes(:thread).order("posts.created_at").group("thread_id").take(5)
# Maybe you can take only a few samples of your collection to show on Last Posted Topics,
# then you just replace take(5) with the number of posts you want to take.

Ruby on Rails: How to implement this particular model (with inheritance, N-to-M relationship, etc)

I've already defined the data model for my application, which will contain this particular part:
Now, my particular issues are related to modelling "Value" and it's child models "Value_Decimal" and "Value_Text". Basically I wan't to have this hierarchy as it's expected to have several other value types, and each value model with have several other different columns (I'm not considering Single Table Inheritance due to this fact).
How can I implement this with Rails' Active Record, can someone point some directions?
Many thanks in advance!
The item_property_value table should have a value_type column allowing for polymorphic associations. Then you can create different tables named for each of your various 'value' models inheriting from an abstract value model (that has no table). This way you can avoid STI, but still inherit from one base model.
Because the item_property_value model is combining 3 different relationships it will need to be treated as a first-class Rails model which means it will also need an id as a primary key. You can then use a has-many-through mapping to access the actual objects it is joining.
Read up here on polymorphic associations. Abstract classes are simply flagged as such:
class Value < ActiveRecord::Base
self.abstract_class = true
end

ActiveRecord has_n association

I was wondering what the best way to model a relationship where an object is associated with exactly n objects of another class. I want to extend the has_one relationship to a specific value of n.
For example, a TopFiveMoviesList would belong to user and have exactly five movies. I would imagine that the underlying sql table would have fields like movie_id_1, movie_id_2, ... movie_id_5.
I know I could do a has_many relationship and limit the number of children at the model level, but I'd rather not have an intermediary table.
I think implementing this model through a join model is going to be you're best bet here. It allows the List model to worry about List logic and the Movie model to worry about Movie logic. You can create a Nomination (name isn't the greatest, but you know what I mean) model to handle the relationship between movies and lists, and when there's a limit of 5, you could just limit the number of nominations you pull back.
There are a few reasons I think this approach is better.
First, assuming you want to be able to traverse the relationships both ways (movie.lists and list.movies), the 5 column approach is going to be much messier.
While it'd be so much better for ActiveRecord to support has n relationships, it doesn't, and so you'll be fighting the framework on that one. Also, the has n relationship seems a bit brittle to me in this situation. I haven't seen that kind of implementation pulled off in ActiveRecord, though I'd be really interested in seeing it happen. :)
My first instinct would be to use a join table, but if that's not desirable User.movie[1-5]_id columns would fit the bill. (I think movie1_id fits better with Rails convention than movie_id_1.)
Since you tagged this Rails and ActiveRecord, I'll add some completely untested and probably somewhat wrong model code to my answer. :)
class User < ActiveRecord::Base
TOP_N_MOVIES = 5
(1..TOP_N_MOVIES).each { |n| belongs_to "movie#{n}".to_sym, :class_name => Movie }
end
You could wrap that line in a macro-style method, but unless if that's a common pattern for your application, doing that will probably just make your code that harder to read with little DRY benefit.
You might also want to add validations to ensure that there are no duplicate movies on a user's list.
Associating your movie class back to your users is similar.
class Movie < ActiveRecord::Base
(1..User::TOP_N_MOVIES).each do |n|
has_many "users_list_as_top_#{n}".to_sym, :class_name => User, :foreign_key => "movie#{n}_id"
end
def users_list_as_top_anything
ary = []
(1..User::TOP_N_MOVIES).each {|n| ary += self.send("users_list_as_top_#{n}") }
return ary
end
end
(Of course that users_list_as_top_anything would probably be better written out as explicit SQL. I'm lazy today.)
I assume you mean "implement" rather than "model"? The modeling's pretty easy in UML, say, where you have a Person entity that is made up of 5 Movie entities.
But the difficulty comes when you say has_one, going to has_5. If it's a simple scalar value, has_one is perhaps a property on the parent entity. Has_5 is probably 2 entities related to one another through an "is made up of" relationship in UML.
The main question to answer is probably, "Can you guarantee that it will always be 'Top 5'?" If yes, model it with columns, as you mentioned. If no, model it with another entity.
Another question is perhaps, "How easy will it be to refactor?" If it's simple, heck, start with 5 columns and refactor to separate entities if it ever changes.
As usual, "best" is dependent on the business and technical environment.

Resources