Inline Model Formset12:18 with Kenneth Love
Something we've actually used before, but in Admin, is inlines. Inlines let us create and edit multiple *related* model instances at once.
Like I've been saying, inline formsets, and formsets in general, aren't necessary something you'll be using all the time. They're a great tool to have in your toolset, though, when you do need them.
How about some docs?
FormWizard (we didn't use this, but I mentioned it so thought you might want to see. It's a way to link several forms together in a row.)
In Django Basics, we made an inline in the admin 0:00 to make it a little easier to create steps in our courses. 0:03 We can do something very similar with our own forms and model forms. 0:06 I'll admit formsets and 0:10 inline formsets aren't something I've used very often in my own career. 0:12 You may very well have the same experience, but 0:16 they're a great thing to know about just in case you do need them. 0:18 And so, with that, let's jump into workspaces. 0:22 Another way of doing formsets, and it's a way that we've done in the admin, 0:25 is actually using what's called an inline formset. 0:30 So I'm gonna scroll up here in the docs. 0:33 And right over here on the side, it says inline formsets. 0:35 Now inline formsets are different from regular formsets 0:38 because inline form sets appear in the model form for another model. 0:44 It's kinda like working in admin when we had the inlines for 0:50 working with our courses so that we could add steps to the courses. 0:54 This is the same idea, but it's for whatever model or form that you want. 0:58 So, let's see about how to.add those. 1:02 I'm gonna do this a little bit backwards, maybe? 1:05 And I'm actually gonna do it over here in question_form. 1:08 I'm gonna start on the template first. 1:10 So you remember in answer_form, 1:13 we just printed out formset, like that. 1:16 So we can totally do that with the model formsets, but 1:21 they don't always look that great that way. 1:26 So I don't want to do it that way, [LAUGH] I want to do it in a nicer way. 1:30 So what I'm gonna do, is right here, after this form.as_p, 1:35 this is where I am gonna add in the extra bits. 1:38 So first we need to add in formset.managment_form, 1:41 and this is a special set of fields that control how many items represented, 1:47 how many forms there are, stuff like that. 1:53 So, this is an important form for controlling whether or 1:55 not your formset is valid. 1:59 And then, we're gonna do a bunch of HTML, but 2:02 we're gonna add in our forms individually, sort of. 2:05 So, role is grid, class is stack and 2:10 hover, style="width: 100%". 2:13 Cool no problem there. 2:19 <thead> <tr> <th 2:22 scope="col" class="text-center" 2:26 style='"width: 10%" 2:34 </th> </tr> </thead>. 2:39 And we're gonna say order, and then I'm actually gonna copy this row. 2:46 Three, four. 2:53 So this one's gonna be Text. 2:57 And we're gonna take off all of that stuff. 3:01 And this one is going to be Correct. 3:07 And this one is going to be Delete, 3:10 because we're gonna bring in the ability to delete answers as well. 3:13 All right, so then <tbody> and 3:18 we need to add in class="order". 3:22 This is for a video or so from now. 3:27 We're gonna add in the ability to reorder these using drag and drop, but 3:30 let's not worry that for just yet. 3:34 So for form in formset, cuz formset is a set of forms. 3:36 Logically, right? 3:42 endfor, all right. 3:46 So, then we do a <tr>. 3:48 And we give this a class of answer-form. 3:51 And if form.instance.pk. 3:56 And we'll say item. 3:59 Again this is for our plug in that we're gonna use in a little bit. 4:02 Actually I need an else in there, else new. 4:06 All right, cool. 4:12 There's our </tr>. 4:15 And then we need a <td>. 4:18 And we're gonna say form.id, cuz we want the id to hang around. 4:21 And then form.order and that's gonna print 4:26 out the HTML field for the order field on the form. 4:31 And then form.text. 4:37 And then <td class="text-center">. 4:41 And then here we're gonna print out form.correct. 4:49 And then if we have an instance, we want to offer 4:54 them the ability to delete that instance. 5:00 So, form, 5:05 and then this field is all caps, which is a little bit strange. 5:10 All right and if, oh no, we need an else. 5:16 But there's nothing in that one. 5:26 And then we close our tr and we close our endfor. 5:29 And we close our tbody and our table, okay. 5:32 So that's cool, that's all of our HTML work. 5:35 And of course I'm gonna have all this in a download for you. 5:37 It's a decent amount of work there, isn't it? 5:41 All right, so now, let's go over here to forms.py. 5:44 So we already have this AnswerFormSet. 5:47 We're gonna add a new one that is our AnswerInlineFormSet. 5:49 So, AnswerInlineFormSet is forms.inlineformset_factory. 5:55 And then we specify the model that the inline is going to appear in. 6:04 So models.question. 6:11 That's the one that we're saving with. 6:12 And then models.answer is the model that we're going to be editing in our form. 6:16 And I don't want any extra for right now. 6:22 And the fields. 6:26 It's weird that we have to specify fields. 6:28 But we still have to specify fields, even though we could 6:30 just specify them in a form or something. 6:36 But, nope. 6:38 And then we can tell it the FormSet that we want to use, 6:40 which is the AnswerFormSet, and that just handles some of these little tidbits. 6:43 That's not required. 6:49 And min_num=1, we have to have at least 1. 6:51 And then I think we're good for right now. 6:55 And then we need to close our FormSet factory. 7:02 Let's do an extra 2 for right now. 7:10 All right, so that's got our form, all that kinda stuff, so 7:12 now let's handle our formset in a view. 7:16 The two views that we're gonna need to edit are the edit_question and 7:20 the create_question. 7:24 So in create_question, right here if we do this form = form_class(), 7:27 let's add in answer_forms = forms.AnswerInlineFormSet. 7:33 And since we're creating a question, this question has no answers. 7:39 So our queryset is going to be models.Answers.objects.none(). 7:46 And that just says, hey give me a blank queryset, which is fine. 7:54 So, let's look down here. 7:59 So inside of our POST we get our form class. 8:01 We also need to get our answer_forms again. 8:05 So answer_forms is gonna be forms.InlineFormSet, 8:09 and I realize I forgot the word Answer on there. 8:14 And our request.POST goes into that. 8:18 And our queryset is, once again, 8:21 models.Answer.objects.none() because we don't have any. 8:25 So if the form.is.valid and 8:31 if the answer_forms are valid, then we wanna do this. 8:34 We wanna create the question. 8:40 We wanna actually create and save that question. 8:41 Then we wanna do answers = answer_forms.save, 8:44 and again, commit = False. 8:49 This should look very familiar to you. 8:52 And then for answer in answers, answer.question = question, answer.save(). 8:54 Cool, so nothing too special there. 9:03 All right, let's go ahead and do the edit one as well. 9:07 And then we will finish this up. 9:10 Oh, we do need to do 'formset' : answer_forms. 9:14 We gotta send that out to the template. 9:19 Okay now we need to edit our edit_question view. 9:21 And this actually ends up being pretty similar to our create_question view. 9:25 So down here where we do the form_class, 9:29 we need to add in the answer_forms = forms.AnswerInlineFormSet. 9:33 And our queryset here is form.instance.answer_set.all(). 9:41 And in here we do the same idea. 9:49 You know what, 9:52 let's just copy and paste. 9:55 The one thing different here is we wanna pass in request.POST. 10:01 And I have to spell request correctly. 10:05 All right, so if the form is valid and 10:07 answer_forms is valid, then we wanna do form.save. 10:11 And we could actually just do answer_forms.save(). 10:16 But since we might have new answer_forms, 10:22 new answers being filled out, 10:27 we need to do our old friend here of, 10:31 answers = answer_forms.save(commit=False) for answer in answers. 10:36 answer.question = question answer.save(). 10:45 And that just makes sure that they all have that same thing. 10:51 And we need to send back our formset for answer_forms. 10:55 And I think we're good. 11:01 Let's give it a try. 11:02 So let's go to Edit our question. 11:03 Here's our question form. 11:07 And then here's our inline formset. 11:08 All right let's add a new one here. 11:11 And let's just say ASCII. 11:13 And hit Save. 11:18 Let's make sure that it updates correctly. 11:19 Okay so that says ASCII. 11:21 Okay now let's add a new one. 11:23 Let's add Emoji only. 11:24 Wow, some days I cannot type. 11:30 And now let's hit Save. 11:33 And now we've got that one in there as well. 11:35 So cool, we have inline formsets and that means we can go back 11:37 to our question_form.html and we can take off this answers thing. 11:42 Because we don't need that anymore, 11:49 no more button down here that says Answers, cuz we don't need to go there. 11:51 We can get rid of that view and that template and all of that. 11:57 Again, this isn't something I've done very often in my own Django career. 12:01 Another area that isn't super common is Django's form wizard. 12:05 I'll link to that in the teacher's notes if you want to check it out. 12:08 Neither of these things, formsets or form wizards, are everyday needs with Django. 12:11 But they're really handy when the need arises. 12:16
You need to sign up for Treehouse in order to download course files.Sign up