1 00:00:00,540 --> 00:00:03,680 Now we're going to be learning about another type of association that you'll 2 00:00:03,680 --> 00:00:07,240 probably be using more frequently than has_and_belongs_to_many. 3 00:00:07,240 --> 00:00:08,920 It's called has_many_through. 4 00:00:08,920 --> 00:00:12,350 Here we have a new app with subscriber and magazine models. 5 00:00:12,350 --> 00:00:15,640 We want people to be able to subscribe to multiple magazines and 6 00:00:15,640 --> 00:00:19,110 we wanna be able to look up all of the magazine subscribers. 7 00:00:19,110 --> 00:00:22,670 We could do this through a has_and_belongs_to_many association. 8 00:00:22,670 --> 00:00:25,790 We'd have a simply joined table with subscriber_id and 9 00:00:25,790 --> 00:00:30,210 magazine_id call ups that could be used to link subscribers to magazines. 10 00:00:30,210 --> 00:00:33,840 But what if we wanna track additional info regarding the subscription itself, 11 00:00:33,840 --> 00:00:37,290 like how many months a person has subscribed to a magazine for? 12 00:00:37,290 --> 00:00:40,530 has_and_belongs_to_many associations can't do that sort of thing. 13 00:00:41,820 --> 00:00:45,330 Instead, we could set up a has_many_through association. 14 00:00:45,330 --> 00:00:49,940 has_many_through basically says that one model is associated with many instances of 15 00:00:49,940 --> 00:00:52,590 another model through a third model. 16 00:00:52,590 --> 00:00:53,260 In the case, 17 00:00:53,260 --> 00:00:58,080 we could say that a subscriber has many magazines through subscriptions. 18 00:00:58,080 --> 00:01:01,970 A subscription model instance joins a subscriber to a magazine and 19 00:01:01,970 --> 00:01:04,230 a magazine to a subscriber. 20 00:01:04,230 --> 00:01:07,550 It works a lot like a has_and_belongs_to_many association. 21 00:01:07,550 --> 00:01:10,697 The table for the subscriptions model has id fields for 22 00:01:10,697 --> 00:01:14,127 the two models it's joined, subscribers and magazines. 23 00:01:14,127 --> 00:01:17,641 But in addition to the subscriber and magazine id fields, 24 00:01:17,641 --> 00:01:20,030 it also has an id field of its own. 25 00:01:20,030 --> 00:01:24,635 Allowing you to look up subscriptions independently of subscribers or magazines. 26 00:01:24,635 --> 00:01:27,602 [SOUND] And because a subscription is a model on its own, 27 00:01:27,602 --> 00:01:29,872 it can also have additional attributes. 28 00:01:29,872 --> 00:01:34,310 Here we show an attribute that tracks the number of months a subscription is for. 29 00:01:34,310 --> 00:01:38,470 In a has_many_through association, two models are still joined by a database 30 00:01:38,470 --> 00:01:42,820 table but that table also represents an entire third model. 31 00:01:42,820 --> 00:01:46,650 The first thing we're going to need to do is generate the subscription model. 32 00:01:46,650 --> 00:01:52,780 So we'll run a bin/rails generate model Subscription. 33 00:01:53,832 --> 00:01:56,838 We'll set up the subscription zone attributes, so 34 00:01:56,838 --> 00:01:59,380 the number of months, that's an integer. 35 00:02:00,750 --> 00:02:05,190 And then we'll need to set up the foreign keys that will associate the subscriber 36 00:02:05,190 --> 00:02:12,101 and the magazine together, so subscriber_id is gonna be an integer. 37 00:02:14,369 --> 00:02:18,260 And that'll reference the id column of the subscriber's table. 38 00:02:18,260 --> 00:02:19,810 And then we'll need to do the same for 39 00:02:19,810 --> 00:02:24,710 magazines, magazine_id, that's also gonna be an integer. 40 00:02:27,286 --> 00:02:31,230 Then we'll need to go through and update all three model classes with associations. 41 00:02:31,230 --> 00:02:35,710 So we'll go to the newly generated subscription model. 42 00:02:37,080 --> 00:02:42,011 And we're going to say that it belongs_to a subscriber, 43 00:02:44,722 --> 00:02:48,610 And it also belongs_to a magazine. 44 00:02:49,870 --> 00:02:52,160 That will allow us to access the subscriber and 45 00:02:52,160 --> 00:02:55,170 the magazine if we look at the subscription first. 46 00:02:55,170 --> 00:02:59,500 Then we'll need to go into the Subscriber model class, and 47 00:02:59,500 --> 00:03:05,587 we're going to say that a subscriber has_many :subscriptions. 48 00:03:07,204 --> 00:03:11,090 That'll allow us to look up the subscriptions that belong to a subscriber. 49 00:03:11,090 --> 00:03:16,920 But we're also going to say that it has_many :magazines 50 00:03:19,428 --> 00:03:23,051 through its subscriptions. 51 00:03:25,242 --> 00:03:30,119 This will add a magazines method to any subscriber model instance that will look 52 00:03:30,119 --> 00:03:32,752 up all subscriptions the subscriber has. 53 00:03:32,752 --> 00:03:36,800 And then look up all magazines that belong to those descriptions. 54 00:03:36,800 --> 00:03:40,090 We'll also need to do the same for the Magazine model class. 55 00:03:41,340 --> 00:03:47,312 We'll say that magazine has_many :subscriptions. 56 00:03:49,314 --> 00:03:53,879 And it has_many :subscribers, 57 00:03:55,427 --> 00:03:58,715 through its subscriptions. 58 00:04:01,124 --> 00:04:03,185 Okay, let's save all that and 59 00:04:03,185 --> 00:04:09,500 go to our Rails console, and try our new associations out. 60 00:04:09,500 --> 00:04:11,620 First, we'll create a new subscription object. 61 00:04:11,620 --> 00:04:17,046 So we'll say, subscription = Subscription class.create, 62 00:04:17,046 --> 00:04:24,082 we'll set its month attribute to 12, so this will be a 12 month subscription. 63 00:04:24,082 --> 00:04:28,880 And we'll say the magazine that the subscription is for 64 00:04:28,880 --> 00:04:35,209 is a result of Magazine class find_by, 65 00:04:37,393 --> 00:04:42,470 And we'll look a magazine up by title or look up magazine titled Ruby Reader. 66 00:04:45,318 --> 00:04:48,298 I'm getting the error, could not find table subscriptions and 67 00:04:48,298 --> 00:04:51,179 I know exactly what happened, I forgot to run my migrations. 68 00:04:51,179 --> 00:04:54,853 So bin/rails db migrate. 69 00:04:57,088 --> 00:04:59,682 There we go, now I'll run Rails console again. 70 00:05:01,069 --> 00:05:05,032 And previously, my call to Subscription.create was unsuccessful 71 00:05:05,032 --> 00:05:07,530 because the table didn't exist. 72 00:05:07,530 --> 00:05:08,780 This time it should work. 73 00:05:08,780 --> 00:05:09,820 Yeah, there we go. 74 00:05:09,820 --> 00:05:14,380 So we've created a 12 month subscription to the Ruby Reader magazine, 75 00:05:14,380 --> 00:05:15,680 now let's find the subscriber. 76 00:05:17,502 --> 00:05:23,955 Subscriber.find_by, Could have find one with a name of Jay, 77 00:05:25,097 --> 00:05:32,680 And I'm going to add to that subscriber's subscriptions collection. 78 00:05:32,680 --> 00:05:34,970 I'm gonna add the current subscription to it. 79 00:05:38,035 --> 00:05:41,282 And that subscription should now be associated with that subscriber. 80 00:05:41,282 --> 00:05:47,934 So let's pretty print Subscriber.find_by, name: Jay. 81 00:05:50,819 --> 00:05:54,763 And let's print that subscriber's collection of subscriptions. 82 00:05:56,438 --> 00:05:59,210 So there's that 12 month subscription. 83 00:05:59,210 --> 00:06:02,960 And the magazine itself should also be associated with that subscriber 84 00:06:02,960 --> 00:06:04,150 through the subscription. 85 00:06:04,150 --> 00:06:08,310 So I'll pretty print the subscriber Jay's magazines, 86 00:06:09,735 --> 00:06:11,452 And there's the Ruby Reader magazine. 87 00:06:12,912 --> 00:06:15,522 If we don't wanna create a subscription manually, 88 00:06:15,522 --> 00:06:19,290 we can auto-create it by adding a magazine direct to the subscriber. 89 00:06:19,290 --> 00:06:22,246 So let's say, Subscriber.find_by(name: "Jay"). 90 00:06:23,300 --> 00:06:30,311 magazines, and we'll append to that collection, Magazine.find_by 91 00:06:31,889 --> 00:06:36,386 title: iOS Inspired. 92 00:06:40,294 --> 00:06:44,104 And now if we look at that subscriber's collection of subscriptions, 93 00:06:44,104 --> 00:06:47,962 we'll see a new automatically created subscription on there. 94 00:06:47,962 --> 00:06:51,850 The auto-created join model won't have its attributes set by default though. 95 00:06:51,850 --> 00:06:56,030 You'll notice that this subscription is for nill months. 96 00:06:56,030 --> 00:06:59,540 So you'll need to update those attributes if you want those set. 97 00:06:59,540 --> 00:07:05,014 We can say, Subscriber.find_by(name: "Jay").subscriptions.last.update, 98 00:07:05,014 --> 00:07:08,358 and we'll update its months attribute to 6. 99 00:07:11,661 --> 00:07:15,949 Now if we print the list of subscriptions again, we'll see that the last one's, 100 00:07:15,949 --> 00:07:17,939 months attribute has been set to 6. 101 00:07:20,775 --> 00:07:24,191 There's a lot more you can do with has_many_through associations. 102 00:07:24,191 --> 00:07:26,520 See the teacher's notes if you'd like more info.