Does 1-1 polymorphic association make sense? - ruby-on-rails-3

I have two models, Article and Document. Each needs an Editor i.e. contains the content body of the Article or Document. A polymorphic association makes sense.
Article
class Article < ActiveRecord::Base
has_one :editor, :as => :editable
end
Document
class Document < ActiveRecord::Base
has_one :editor, :as => :editable
end
Editor
class Editor < ActiveRecord::Base
belongs_to :editable, :polymorphic => true
end
The Editor model contains the following attributes:
content, editable_id, editable_type (name of model i.e. Article or Document)
This all works fine but do I need it?
If I wish to access the content body for a given Article can I not just get the id of the editor field directly like so?
#article = Article.find(params[:id])
#editor_id = #article.editor.id
and perform my Editor find on that.
I don't see the benefit of the one-to-one polymorphic relationship in this case. Can anyone set me straight?

First, for your very specific question on why you can't do the following with out the polymorphic relationship:
#article = Article.find(params[:id])
#editor_id = #article.editor.id
The entire reason #article.editor.id works is because of the polymorphic relationship. It's going something on the lines of:
You have a has_one :editor which means #article.editor = this editor model based on it's editable fields
Now that you have found this editor model here's it's id
If you took out the has_one it would stop working. Also, you don't need to do a find at all because this is also true:
#article = Article.find(params[:id])
#editor = #article.editor
No for the larger question of is there a way to set this up with out using polymorphic at all.
To make things as simple as possible you could just put content fields in document and article. This would make sense if:
You will always use the content field so you aren't taking up database space for nothing
There is no special code, validation, etc that goes with the content field and would then be repeated in two places.
I'm going to assume by the fact that you have an Editor model there are functions, validations, etc in that model that require it to be its own entity to avoid code duplication. At this point you have two routes you could go. The first would avoid polymorphic by putting the editor_id in Article and Document and making them belongs_to :editor. Yes, you avoid polymorphic behavior but you have some serious downsides:
The Editor database table is referenced in multiple locations but there is nothing in the table to tell you who is using what column. Having had to "read" and "port" databases more often then I'd like to it's a good future consideration. (This isn't a problem in cases where you wouldn't use polymorphism because you would know all items in this table can only be linked to one other table.)
Every time you want to use Editor in a model you have to add another migration to add editor_id to that model and modify Editor to have the link back to that model. (You could technically skip that step but for code readability you really want to mark what models it could be linked to. Especially if you are stuck doing the previous point.)
So that gets us back to your original setup. The polymorphic relationship does a few things:
Makes it clear in the database who each row belongs to
Lets you make another model have and editor with only code and not database changes
Keeps all the functionality specific to Editor content in one nice spot
So, yes, I think the one to one polymorphic makes sense. It leads to:
- an easier to maintain database
- easier to add future functionality
- clearer relationship
The only thing I would consider is if there is almost no code in Editor just dumping the field directly in the Article and Document models and dumping Editor completely.

Related

Can't figure out how to query a 2nd level resource in Rails

I'm having trouble when querying Users.
My nesting resources are:
resources :users do
resources :photos do
resources :pins
end
end
1.) I have a user model, that has_many :photos.
2.) :photos has_many :pins
I want to list my users on which users have more pins in their photos.
So, I tried:
#members_ordered = User.includes(photos: :pins).group("users.id").group("photos.id").group("pins.id").order('COUNT(pins.id) DESC')
Not working though. Any ideas? Thanks guys
I have two observations, but neither directly fix the code in your example.
First, looking at my output from trying something similar, it seems like you either need quite complex SQL (which really isn't Rails' forte) or several simple queries (which, depending on the size of your app, could hit performance) to achieve this.
A little experimenting doesn't seem to show a significant difference (<1ms) in the time that one more complex query takes compared to that which three simple queries require (as in solution one)
Solution one, if performance is not crucial, for example, if this is a small, low-traffic solution, my instinct would be to add that the User model has_many :pins, through: :photos, which lets you call things like User.includes(photos: :pins).all, then user.pins.count, although, as I've mentioned, this causes a bit more database use.
Solution two, if performance is important, my suggestion would be to cache the count of pins against the user model. This could be as simple as an extra database column to store it, and have a background process (using delayed_job or similar) re-calculate the count each time it changes (so, maybe after_create in the Pin model.
The benefit of this is the slow, time-consuming query only gets run when the value changes, and the rest of the time, the value gets lifted from a single-table SELECT, which should take quite a bit less time than either solution one or the more complex query.
Both of these are less-than-perfect, and I think the most elegant and efficient way of working is to use a combination of a built-in function and a beautifully simple query:
The third solution, which brings together both of these options to some extent, is Rails' counter_cache option. As there are two levels to it, I can't see a native way to include all of these in one query, so we will automatically generate a count for each Photo, then add these up to get the User count.
Create a migration to add a pins_count field to the Photo model, so, in terminal, type;
rails g migration AddPinsCountToPhotos pins_count:integer
Update the belongs_to :photo line of the Pin model to;
belongs_to :photo, counter_cache: true
Now, every time a Pin gets created or deleted, the pins_count column of its Photo will be updated.
Now, to get the values for users;
Create a migration to add a pins_count field to the User model, so, in terminal, type;
rails g migration AddPinsCountToUsers pins_count:integer
Now we need to create an method in the Photo model, which we will run each time a pin is saved, so add this to your Photo model;
def update_user_counts
total_photos = self.user.photos.sum(:pins_count)
self.user.update_attribute(:pins_count, total_photos)
end
Finally, we need to tell Rails to call this whenever a pin is created or updated. We do this with a simple method that just calls the action from the Photo model;
after_save :update_photo_counts
def update_photo_counts
photo.update_user_counts
end
Now, whenever a pin is saved, it automatically updates the Photos pins_count, and then our new method totals the pins_counts from all of the Photos for that user, and saves them to the Users pins_count

Rails 4 SQL Join Code

I'm building a marketplace app. I have a Listing model (users list items to sell) and a User model. In the listing model, I have a userid column. And in the User model, I have a name field. In my listing show page, I want to display something like the below:
"Sold by #{#listing.user.name}"
But the join doesn't work in retrieving the name from the user table. If I change it to listing.userid then it works but I want to display the users name.
my user model has has_many :listings, dependent: :destroy
My listings model has belongs_to :user.
How can I display the user's name on the listing show page?
If you really have a column called userid instead of user_id then you have something very slightly different to what Rails expects... which is why Rails isn't finding it for you automatically.
Your best bet is to rename the column (using a migration) to user_id to take advantage of the Rails default behaviour. Trust me - it's worth the effort up front if you can do this.
If for some odd reason you can't (serious business constraints), then there are ways of telling rails that you are using a non-standard foreign-key... but lets not get to that unless you have to.

Rails - How to order multiple associations?

I want to enable users of my app to create online polls which have an arbitrary amount of questions. Questions come in two flavours: multiple choice and open ended
My idea is to build something like this:
Poll
has_many open_question
has_many multichoice_questions
With apropriate belongs_to in the associated models.
How do i make it possible to save the order in which questions appear, so that it can be recreated when the poll is taken?
I'm thinking about serializing an ordered 3D array with question id's and types, but that feels wrong (it's saving the same information twice).
What would be a Rails way to model this?
If it were me I would set up my model like this:
User has_many Polls has_many OpenQuestions && MultichoiceQuestions
Then I could do something like this:
#user = current_user
#poll = #user.polls.find(params[:poll_id])
#open_questions = #poll.open_questions.order('created_at ASC')
Alternatively, if you feel you need even more control you could leverage some scopes.
http://guides.rubyonrails.org/active_record_querying.html#scopes

Correct Association Between Models in Rails 3

I have a Record model and I want to create a Field model such that a given Record has_many Fields. Similarly I want each field to be associated with a Tag such that each Field has_one Tag. But each Tag can be reused many times between Field objects.
In this case would I just say that a Tag belongs_to_many Fields? Likewise would it be right to say that the Field belongs_to_many Records?
(Ultimately I want the Record object to be a container for multiple Fields. I envision having a form where I can dynamically add new Field and Tag, so that a Record might look like:
Record 1
Tag 1
Field 1
Tag 2
Field 2
...
where each Tag can either be pulled from a pre-existing pool or created on the fly) Thanks for the help!
First, I would highly recommend reading RoR Guide on Associations:
http://guides.rubyonrails.org/association_basics.html
Whether you are new or need a touch up on using Associations in RoR, read that guide.
Your question seems to be more of a logic question, but I'll start with the
code for it:
class Record < ActiveRecord::Base
has_and_belongs_to_many :fields
...
class Field < ActiveRecord:Base
has_and_belongs_to_many :records
belongs_to :tag
...
class Tag < ActiveRecord:Base
has_many :fields
...
Each Record connects to many different fields, and each field connects to many different Records. This is a classic example of a many to many association. The logic in your code when you actually use these models is what will make the Record Model seem like a container (Because technically you could say a Field is a container for many records).
Each Field will have 1 Tag associated with it, but that same Tag could be used with any number of fields (You could say the Fields are reusing the tags). This is a one to many association. When making this connection you would use belongs_to in the Field model, and has_many in the Tag model.
Since Tag is connected to the Field Model, the logic you are looking for: A Record is a container for Fields and Tags, makes sense with this setup.
Here is a simple example of fetching a Tag inside a record:
#Returns the Tag Associated with the first field
#"inside" the first Record in the database.
Record.first.fields.first.tag
Likewise, one could easily go the opposite direction:
Tag.first.fields.first.records.first
Make sure to leverage the Rails commands via command line to quickly setup your migrations and models. As far as options on how you want your associations to handle things when one as deleted, saved, and so on, just read the guide at the top to find what you are looking for.

What's the right way to make forms for creating/updating records with Single Table Inheritance (STI) in Rails?

If you have Single Table Inheritance in Rails something like this:
class Vehicle < ActiveRecord::Base
class Car < Vehicle
class Truck < Vehicle
What's the recommended way to create a single template which allows the user to create either a Car or a Truck from a single vehicles/new.html.erb? I think the tricky bit is how to lay out the html form so that BOTH car's and truck's fields are present, but when the POST occurs, the controller somehow knows to select the right form based on an html select or radio. I'm hoping someone a lot smarter than I has a great best-practices answer!!
Extra credit: the Vehicle is actually a nested association under a Person, so it is really Person#vehicle_attributes...