I've a quick and quite basic question to ask.
I would like to create a new model which has a parameter that can be one of several model types.
Ex: the param 'targeted_object' can be either an instance of Model A or an instance of Model B.
For the moment I don't think I need a similar behavior for Model A and Model B, so my first guess is to create a Master model for Model A and Model B named TargetableObject: create inheritance.
But is it the best way to do this or I need to make something else regarding that I presume for now no related behavior for Master object children?
Thanks
If I understand correctly, Polymorphic associations could be what you need.
From the rails guides:
With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model.
Related
I have an application where a User can create many Links, and each Link can store different type of data, depending on what type of Link it is. For example, a TelephoneLinkData stores a telephone number, an EmailLinkData stores an email address, a subject and a body. Each Link also has some fields in common, such as a reference to the user and a name.
I've tried to map this into ActiveRecord as cleanly as I can. Currently, I have a polymorphic relationship from Link to its data-type:
class Link < ApplicationRecord
belongs_to :user
belongs_to :link_data, polymorphic: true
...
class EmailLinkData < ApplicationRecord
has_one :link, as: :link_data
accepts_nested_attributes_for :links
...
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes, what I'm trying to model is a class having multiple possible different child classes. This works fine, and I'm able to create Links through the various *LinkData controllers, but what I'd really want to do is have the Link act as the primary source of interaction for the user, so that the user manages their links through the /links path. For example, I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
I've looked around for other ways to model this relationship, and the most common other suggestion seems to be Single-Table Inheritance, but the majority of my columns will differ between LinkData classes, so that feels like the wrong abstraction.
Is there a more idiomatic way to model this data structure?
As is always the case, the best choice depends on the business or application needs, so it's difficult to provide a recommendation without knowing more about what you're trying to do.
It sounds like you prefer the MTI approach, essentially using actual foreign keys and an XOR constraint to the Link table instead of a type column. That's a totally reasonable (although not as common) alternative to a polymorphic association.
However, I think there was a bit of a misunderstanding in your question.
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes...
A polymorphic association in Ruby/Rails doesn't have anything to do with class inheritance (e.g. parents and children). You might be thinking of Single table inheritance. A polymorphic association allows one class (e.g. a Link) to be associated a record in any other table (e.g. the various classes of LinkData) via two fields, a association_id and association_type. These associated classes need not be related to each other. For example, a common use case might be the acts_as_commentable gem, that allows you to add a comment to any other object, and the comment would have a polymorphic association with the other classes.
In the second part of your question you mention that you'd like the User to interact with Link's via a single controller.
I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
There's nothing stopping you from implementing this using the initially proposed data model. ActiveRecord may not handle this completely for you out of the box, but you can imagine implementing a link_data= method on the Link class that would create the appropriate associated object.
I'd say the pros/cons of using a polymorphic association would be...
Pros:
easy to setup and use
easy to make required (validate presence of / not null)
easy to associate with a new class
Cons:
no referential / database integrity
have to migrate data if you change a class name
And using the MTI approach is basically the opposite. A bit harder to setup and use, harder to add a new association/table, harder to ensure exactly one association exists... but the long term data quality benefits are significant.
I was able to get things to work the way I wanted to using multiple table inheritance, based largely on this chapter: https://danchak99.wordpress.com/enterprise-rails/chapter-10-multiple-table-inheritance/
I have a model called Client.
This extends not from ActiveRecord::Base but from my own wrapper class.
These objects are not stored in my own database.
I have some other models called Answer and Device for example. These are stored in my database with a client_id.
Now, what I want to do is that is can call Answer.client and Client.answers for example. The normal way would be with ActiveRecord associations but that doesn't work is this case.
I can define my own .answers and .client method but in my opinion that's not the way to go with Rails.
Thanks in advance for replying!
I am modelling a Trainer/Member relationship on Ruby on Rails and I was wondering what is the best way to model this relationship.
Currently I only defined roles for a User class using Rolify for view and controller access.
Trainers and Members are users.
Should I do a recursive relation between the User model, or should I create a Trainer and a Member model specifically on Rails, and set up hierarchy between them, and create the relationship between the child models?
I would setup two different models for trainers and members as you proposed and use the rails association helpers. For example, if a member can have several trainers and trainers can have many members you would want to implement a many-to-many relationship. This is most easily done in rails via the "has_many :through" model helpers. You can then add an attribute to these classes that would specify the levels of authorization or controller access on your app if need be.
I'm passing params to a model instance and saving it with update_attributes. It is associated with several other models and I've configured it to update some of these with accepts_nested_attributes_for.
This is very nice and clean as I only have to update the one model, but I'd like to get a list of the associated(nested) models that were also updated so that I can give the user feedback about some of the fields that have changed.
Is there a way to do this, or am I approaching the problem in the wrong way?
I've found a solution to my question, maybe not the best one but it will work.
For a list of models that are associated and have accepts_nested_attributes_for configured we go:
associations = ModelClass.reflect_on_all_autosave_associations()
Each of these association objects has a name attribute(the association name), which can be used to access the association on the instance, and then we can check whether this association has changed:
associations.each{|assoc|
model_instance.send(assoc.name).changed?
}
It should be noted that we cannot use update_attributes with this solution, as all the models are saved before we can check whether anything has changed. So we have to assign_attributes and save the model in separate steps:
model_instance.assign_attributes(params[:model_instance])
// check for changes on associations here
model_instance.save()
I'm new to Rails and have been struggling with the naming conventions to work out what I'm actually referring to. With many items named very similarly (e.g. model class, db table, controller) it isn't always obvious from the examples which object is being referred to.
In the guides for associations, it uses posts and comments as examples. Can you help me understand what is actually being referred to?
With the association
has_many :comments
What is :comments? Is this the model class or the controller?
When referring to the associated object:
#post.comments.build
What is comments in this example? Is it the class, the controller, or the instance variable?
Comments is a model, not a controller. This is true in both of your examples. In the first it is a definition of a relationship between two models. In the second it is a new object of type comment which is created and populated with a reference to the post.
A rule of thumb you can use is that models will typically not reference controllers, but other models.
Models represent the real objects.(Hence they are called models). Comments, posts are the actual object representations.
has_many :comments is just a way of saying a post has many comments. So it is referring to the model.
Controllers are where an action takes place for the model. As you go along you will realize there can be multiple controllers for a given model. Controllers is a place where you decide what to do on various events. Hence any route will get mapped to a controller action.
#post.comment.build is saying for the model post which has may comments, build an instance of the model comment. This will lead to triggering of a function in the controller.