How can I include ordering in an 'order' ActiveRelation call that's more than one level deep?
That is, I understand the answer when it's only one level deep (asked and answered at Rails order by associated data). However, I have a case where the data on which I want to sort is two levels deep.
Specifically, in my schema a SongbookEntry contains a Recording, which contains an Artist and a Song. I want to be able to sort SongbookEntry lists by song title.
I can go one level deep and sort Recordings by song title:
#recordings = Recording.includes(:song).order('songs.title')
...but don't know how to go two levels deep. In addition, it would be great if I could sort on the recording (that is, the song title and the artist name) -- is this possible without descending into SQL?
Thanks for any help,
Keith
If you model the association between SongbookEntry and Song as such:
class SongbookEntry < ActiveRecord::Base
# ...
has_one :song, through: :recording
end
you will be able to access #songbookentry.song and SongbookEntry.joins(:song) using your existing schema.
Edit:
Applying the same idea for Artist, a possible query would be:
SongbookEntry.joins(:song,:artist).order('songs.title','artists.name')
Note that this may not be the most efficient operation (multiple joins involved) even though it looks Rails-ish, so later on you may want to denormalize the tables as Ryan suggested, or find another way to model the data.
I would advise storing the artist name (and possibly the song title too) on the recording itself, so you don't have to "descend into SQL".
Try this
SongbookEntry.includes(:recording=>[:artist,:song]).order('songs.title, artists.name')
You can use joins in place of includes if you don't want to use associated tables fields in views
Related
I'm a Rails beginner and I'm having trouble wrapping my head around how I should design my database schema. Here's a quick breakdown of some of my models:
Submission: Contains multiple Guesses.
Guess: Contains one Song.
Song: Contains song-related info, has a many-to-many relationship with Shows.
Show: Contains show-related info, including Songs that were played.
The relationship between the last two models are managed through the Song_Show relationship model. Things get a little trickier when I try and relate the Guess model to the Song model. Semantically, I think of it like each Guess has_one Song and each Song belongs_to many Guesses; however, there is no belongs_to_many relation.
In the classic example that uses the Customer and Order models, the semantic relationship matches the Rails relationship - each Order belongs_to a Customer and a Customer has_many Orders; however, trying to apply that logic to my example doesn't quite work if the proper way to do things is a Song has_many Guesses and each Guess belongs_to a Song. Is the way I'm thinking about these relationships semantically getting in my way of understanding Rails associations?
On a related topic, creating all of these relationships seems to be a hassle, at least for the initial version of this application where both the Guess and Song models seem rather useless, as they each essentially store a single song_name property. One of the critical features of the application is to compare a Submission to a Song and see if any of the song_names from the Submission match those in the Show. Is it a bad idea to simply serialize a list of song_names in both the Submission and Show instances and compare those directly, rather than complicating things with the Song and Guess models?
Don't get too bogged down by the meaning of has_many and belongs_to. At some point in your Rails career, you'll start to see them for what they really mean: which model keeps track of the relationship.
In a has_many relationship, it's the other model that keeps track of the relationship. If a Song has_many Guesses, then the Guess would have the song_id.
In a belongs_to relationship, the class that you're in is the thing that keeps the id. If a Guess belongs_to a Song, then the Guess has the song_id.
In a has_many...through model, it's the joining class that keeps track of everything.
As for your last question, I think you have to figure out how you're going to compare Guesses to Songs. Seems like a Guess might not merit it's own class—could be serialized—but a Song could probably merit its own class. Doing it like this might make it easier to search too:
Song.where(song_name: #submission.guesses) # where guesses is an array.
I have this app I am writing in Rails 3.1, I am wondering the best way to model this.
Would it be best if I created a model called "Movie" with a "title" and then create a new model for each "movie asset" such as "poster, trailer, screener" etc and relate it to the "Movie" by associations? Or would it be best if I just created this as one and do-away with the of associations of each asset to "Movie"?
My assumption is to just make it as one as it will remove all the overhead of the FK's and joins to get retrieve the data related to the movie but I am looking for opinions/suggestions. Thanks
There can be three types of attributes(columns) for movies.
Which have exactly one value, and are present in every movie e.g. title, year, official trailer etc.
Keep them in the movie table.
Which have exactly one value, but are present in few of the movies e.g. total Academy Awards.
Keep them in separate table, and use has_one+belongs_to association.
Which have multiple values e.g. trailers
Keep them in separate table, and use has_many+belongs_to association.
More suggestions:
For many key-value attributes, it is easier to use one json/yaml column using serialize instead of creating one column for each key.
Do not store images in DB, keep them in file-system or cloud storage.
You can create your models in the order you want since you have to fill in both models to create a unique association (such as belongs_to and has_many) so I think it doesn't really matter !
Sorry the title is pretty unclear - I'm just not quiet sure how to phrase the question without explaining it.
I would like to record workouts with my app. I would like a Workout Table (what I'm calling the parent) that has basic information like date and sub_workout_type_id
A workout record can have either a Cardiovascular workout (One Model) or a Strength Workout (Another Model).
My thought on have 3 tables instead of just the 2 Cario Workout model and strength workout model is that I would be able to pull a feed of any type of workout, by pulling the Workout Records and then dig deeper as needed.
Perhaps there is a more ruby-ish way to do this? Because right now I don't know of a way to say has_one_model_or_the_other. Thanks!
I see two options, either you use STI (single table inheritance) : in that case you would have a single table that would be able to contain both a cardiovascular model or a strength workout, and a type. This would only work if the two models share some common characteristics.
Another solution is to write something like
has_one :cardiovascular
has_one :strength
and then use validations to enforce that only one of them is set.
Hope this helps.
As mentioned by #nathanvda, STI could be a good choice.
If you're looking to store class specific data with your models, maybe check out Modeling inheritance with Ruby/Rails ORMs to see if that answer gives you any ideas on how to model this relationship.
Note, the example there uses has_many's but a lot of the ideas are similar.
As a simplification of my problem, lets say I have a songs and an artists table:
songs
-----
id
name
artist_id
artists
-------
id
name
The majority of the time I'm querying songs and I always need the artist name attached. Would it make sense to add another column to song called artist_name so that I don't need to do .includes(:artist) eager loading?
I think my current design is the typical one but what is the most efficient one or what is the best way to handle this situation in rails?
Song.find(2, :include => :artist)
To answer my own question:
It seems database denormalization makes sense only in heavy read applications and one should profile extensively first.
Some options:
Add an includes to the default scope for songs
Denormalize artist name and use model hooks to ensure data integrity
I'm teaching myself rails, and want to build a blog similar to tumblr. It will have a couple different post types, such as written text, photo posts, audio posts, and video posts.
My initial thought was to have different models for each type of post, since there will be different rules for each type of post. However, I'm still learning and don't know what I don't know, so maybe there is a better way to go about things (maybe only one model for posts, and a table for post types?).
Any feedback would be appreciated.
Probably a good relational database and object oriented design would be to have one main post model, which will probably share mostly same attributes and behaviors with all the types of posts. This could even act as your "text" type posts.
This could also simplify relationships with the posts also (eg. "users has many posts" vs "users has many text posts and/or video posts and/or etc").
Then have a sort of "attachments" join table, which determines the type of attachment (so you can have multiple attachments per post):
CREATE TABLE attachments (post_id, media_type, media_id)
Then have a table and model for each type for specific behaviors and handlers for the media types.
CREATE TABLE audios (id, transcription, storage);
CREATE TABLE videos (id, location, format, storage);
This will probably require some sort of polymorphic relationship, though, which could be a debatable DB design... you'll need views and triggers to query easily and maintain integrity... but Rails handles it quite well.
The post model would have
has_many :attachments
and attachments would have
belongs_to :post
belongs_to :media, :polymorphic => true
and each of the media model would have
has_one :attachment, :as => :media
then you can access your media via
post.attachments[0].media
You can skip the attachments table and merge the attributes with the posts table if you only need one type of media per post
Sorry i keep editing, i keep thinking of more things to to say :)
Here's a couple of options that could work.
First, you could just make one Model with columns for text_content, video_link, photo_link, etc.
Then in your view, you could render the post's view to the user (probably using a partial) with a different look depending on which attributes have values.
A second option would be to make a smaller Post table that just had key information and use a series of 'has_one' relationships to the other items.
The only advantage I see to the second option is that your DB table would be smaller since you don't have to represent the null cells over and over. Unless your worried about some huge scaling issues, I'd go with the 1st option though.