3

I'm trying to create a simple has_many association between my Game and DLC models. The challenge I'm facing is that since there's no DLC table because of the single table inheritance, there's no way to insert a game_id. So if I were to do the following I'd get this error:

game = Game.create
game.dlcs

SQLite3::SQLException: no such column: games.game_id

Here is how my models are currently setup:

class Game < ActiveRecord::Base
    has_many :dlcs
end

class DLC < Game
    belongs_to :game
end

Note: DLC refers to downloadable content

Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
  • Why is it polymorphic? – Mark Swardstrom Jun 20 '15 at 02:46
  • To be honest I'm not exactly sure in this case. I was just trying whatever I saw in a previous answer. – Carl Edwards Jun 20 '15 at 02:52
  • Have you read the Rails Guide on Associations? http://guides.rubyonrails.org/association_basics.html – 7stud Jun 20 '15 at 02:54
  • Yes but unfortunately there's nothing in there about STI associations. – Carl Edwards Jun 20 '15 at 02:58
  • *I'd like to do the exact opposite where the active record model, Game has many Downloads.* I'm not sure what exact opposite means. Typically, if a Game can ***have many*** Downloads, then a Download might only ***belong to*** one Game. However, if in your scheme a Download can belong to many Games, then check out the association `has_many_and_belongs_to_many`. – 7stud Jun 20 '15 at 02:58
  • You had it right the first time where a game has many downloads and downloads belong to one game – Carl Edwards Jun 20 '15 at 02:59
  • Well, then that is the typical, simple association. In your Game model you declare `has_many :downloads` and in your Download model, you declare `belongs_to :game`. Get rid of the `polymorphic: true`. – 7stud Jun 20 '15 at 03:00
  • Arghhhh...I missed the STI part. Scratch everything I said. Sorry about that. – 7stud Jun 20 '15 at 03:05
  • Why would it make any sense that Download is a kind of game? I can't really see why you think you should be using STI here. And what you would need to do is create a self joining relationship when both end of the association are on the same table. http://guides.rubyonrails.org/association_basics.html#self-joins – max Jun 20 '15 at 03:17
  • Changed the model name to `DLC` to hopefully make more since of the association. Basically give a definition in rossettistone's answer and provide a link to the definition in the question. – Carl Edwards Jun 20 '15 at 06:07

2 Answers2

4

Edit: This answer was based on a misunderstanding of the original question. OP has clarified that Download refers to DLC, and is in fact a decent case for STI.

It is not my intention to be contrarian, but I'm not sure I see the case for using single-table inheritance in this case. Single-table inheritance is best used when two models are extremely similar in terms of their database columns, yet appreciably different in the context of the application.

Hypothetical models called Cow and Horse might share the farm_animals table for example. You could even make Stallion a subclass of Horse. This would let you keep all of these models on one table while giving you the convenience of methods like Stallion.new, which is really just an abbreviation for FarmAnimal.new(type: 'horse', gender: 'male').

It's not clear to me that the same benefits are present here for the relationship between Game and Download. If I've missed something crucial, and am delivering useless information with this answer, my humble apologies! Otherwise, I'd suggest reevaluating the use of STI in this case.

rossettistone
  • 364
  • 2
  • 11
  • Are you familiar with the term DLC (downloadable content)? Think of it as an expansion pack for a video game. So for instance a game like "Bioshock" is a core title but then there's an expansion DLC called "Burial at Sea". The DLC inherits all attributes from a game (title, achievements, box art, etc). I wasn't sure if Rails would properly know how to pluralize `DLC` which is why I named the model `Download`. Does that now make sense? – Carl Edwards Jun 20 '15 at 05:52
  • Ah, that *does* make sense! Let me think on it and see if I can improve my answer. – rossettistone Jun 20 '15 at 20:44
  • I think maxcal has nailed it in the comments to your question. This is a great case for a self-join, and the docs he linked are just the right ones: http://guides.rubyonrails.org/association_basics.html#self-joins. I haven't seen a self-join with STI before, but I don't see why it wouldn't work! – rossettistone Jun 20 '15 at 20:51
4

The simplest alternative would be to just use self-joins and add a parent_id column to games.

class Game < ActiveRecord::Base
  has_many :dlcs unless self.name == 'DLC'
end

class DLC < Game
  belongs_to :game, foreign_key: :parent_id
end

If that's absolutely unthinkable you can create a join table.

# game_id: int
# dlc_id: int
class GameExtension
  belongs_to :game
  belongs_to :dlc
end

class Game < ActiveRecord::Base
  has_many :game_extensions
  has_many :dlcs, though: :game_extensions
end

class DLC < Game
  has_many :game_extensions
  belongs_to :game, though: :game_extensions
end
max
  • 96,212
  • 14
  • 104
  • 165
  • If you copy and paste this like I did, `through` is spelled incorrectly and it won't work. I tried editing the post, but I am unable to due to the 6 or more character change requirement enforced by stackoverflow. – Nitsew Jun 28 '18 at 18:20