Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialjames white
78,399 PointsInline formsets - the outer edge of arcane Django trivia
I'm glad there is only one challenge dealing with inline (nested?) formsets.
Unfortunately it has two parts and I can't even imagine what they want for part one.
So I watched the video dozens of times --especially the part where Instructor Love admits that even he has had few occasions to use Django inline formsets...
So then I dug through the zip downloads to look over the code presented in the video directly.
.
In the views.py file (under Final\Learning Site\Courses) is the code that
somehow might provide a clue as what is the needed code,
but looking at said code I still have no idea how to translate this code's 'pattern'
into what the challenge might be looking for ):
from itertools import chain
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from . import forms
from . import models
def course_list(request):
courses = models.Course.objects.all()
return render(request, 'courses/course_list.html', {'courses': courses})
def course_detail(request, pk):
course = get_object_or_404(models.Course, pk=pk)
steps = sorted(chain(course.text_set.all(), course.quiz_set.all()),
key=lambda step: step.order)
return render(request, 'courses/course_detail.html', {
'course': course,
'steps': steps
})
def text_detail(request, course_pk, step_pk):
step = get_object_or_404(models.Text, course_id=course_pk, pk=step_pk)
return render(request, 'courses/text_detail.html', {'step': step})
def quiz_detail(request, course_pk, step_pk):
step = get_object_or_404(models.Quiz, course_id=course_pk, pk=step_pk)
return render(request, 'courses/quiz_detail.html', {'step': step})
@login_required
def quiz_create(request, course_pk):
course = get_object_or_404(models.Course, pk=course_pk)
form = forms.QuizForm()
if request.method == 'POST':
form = forms.QuizForm(request.POST)
if form.is_valid():
quiz = form.save(commit=False)
quiz.course = course
quiz.save()
messages.add_message(request, messages.SUCCESS,
"Quiz added!")
return HttpResponseRedirect(quiz.get_absolute_url())
return render(request, 'courses/quiz_form.html', {'form': form, 'course': course})
@login_required
def quiz_edit(request, course_pk, quiz_pk):
quiz = get_object_or_404(models.Quiz, pk=quiz_pk, course_id=course_pk)
form = forms.QuizForm(instance=quiz)
if request.method == 'POST':
form = forms.QuizForm(instance=quiz, data=request.POST)
if form.is_valid():
form.save()
messages.success(request, "Updated {}".format(form.cleaned_data['title']))
return HttpResponseRedirect(quiz.get_absolute_url())
return render(request, 'courses/quiz_form.html', {'form': form, 'course': quiz.course})
@login_required
def create_question(request, quiz_pk, question_type):
quiz = get_object_or_404(models.Quiz, pk=quiz_pk)
if question_type == 'tf':
form_class = forms.TrueFalseQuestionForm
else:
form_class = forms.MultipleChoiceQuestionForm
form = form_class()
answer_forms = forms.AnswerInlineFormSet(
queryset=models.Answer.objects.none()
)
if request.method == 'POST':
form = form_class(request.POST)
answer_forms = forms.AnswerInlineFormSet(
request.POST,
queryset=models.Answer.objects.non()
)
if form.is_valid() and answer_forms.is_valid():
question = form.save(commit=False)
question.quiz = quiz
question.save()
answers = answer_forms.save(commit=False)
for answer in answers:
answer.question = question
answer.save()
messages.success(request, "Added question")
return HttpResponseRedirect(quiz.get_absolute_url())
return render(request, 'courses/question_form.html', {
'quiz': quiz,
'form': form,
'formset': answer_forms
})
@login_required
def edit_question(request, quiz_pk, question_pk):
question = get_object_or_404(models.Question,
pk=question_pk, quiz_id=quiz_pk)
if hasattr(question, 'truefalsequestion'):
form_class = forms.TrueFalseQuestionForm
question = question.truefalsequestion
else:
form_class = forms.MultipleChoiceQuestionForm
question = question.multiplechoicequestion
form = form_class(instance=question)
answer_forms = forms.AnswerInlineFormSet(
queryset=form.instance.answer_set.all()
)
if request.method == 'POST':
form = form_class(request.POST, instance=question)
answer_forms = forms.AnswerInlineFormSet(
request.POST,
queryset=form.instance.answer_set.all()
)
if form.is_valid() and answer_forms.is_valid():
form.save()
answers = answer_forms.save(commit=False)
for answer in answers:
answer.question = question
answer.save()
for answer in answer_forms.deleted_objects:
answer.delete()
messages.success(request, "Updated question")
return HttpResponseRedirect(question.quiz.get_absolute_url())
return render(request, 'courses/question_form.html', {
'form': form,
'quiz': question.quiz,
'formset': answer_forms
})
@login_required
def answer_form(request, question_pk, answer_pk=None):
question = get_object_or_404(models.Question, pk=question_pk)
formset = forms.AnswerFormSet(queryset=question.answer_set.all())
if request.method == 'POST':
formset = forms.AnswerFormSet(request.POST,
queryset=question.answer_set.all())
if formset.is_valid():
answers = formset.save(commit=False)
for answer in answers:
answer.question = question
answer.save()
messages.success(request, "Added answers")
return HttpResponseRedirect(question.quiz.get_absolute_url())
return render(request, 'courses/answer_form.html', {
'formset': formset,
'question': question
})
Since Instructor Love's video was in no way helpful in understanding the use of inline formsets
I did a little googling around, but didn't find much:
http://kevindias.com/writing/django-class-based-views-multiple-inline-formsets/
http://charlesleifer.com/blog/djangos-inlineformsetfactory-and-you/
https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#inline-formsets
https://gist.github.com/neara/6209563
http://stackoverflow.com/questions/1113047/creating-a-model-and-related-models-with-inline-formsets
http://stackoverflow.com/questions/1913913/django-inlineformsetfactory-what-is-it-good-for
http://stackoverflow.com/questions/11976563/django-inline-formset-example-from-documentation
https://djangosnippets.org/snippets/1246/
https://code.google.com/p/django-dynamic-formset/wiki/Usage#Working_with_inline_formsets
Maybe someone looking at the above links can piece together some code that the challenge
might like, but I'm totally lost...
By the way I use the word "arcane" in technical contexts to describe anything so obscure and
uncommon that I am unlikely to actually use it in real life.
I know the word has other more non-technically related meanings to the A.D. &D/RPG crowds..
1 Answer
Chris Freeman
Treehouse Moderator 68,441 PointsHi James, lets walk through a solution.
The challenge asks:
I want to be able to create a Review
in the same form as where I create a Product
. That means I need an inline form! Create an inline model formset factory, named ReviewFormset
, for the Review
model. You need to include all the same fields as the existing ReviewForm
. Remember, the first argument to the factory is the parent model (Product
) and the second is the model the factory is for (Review
).
At 5:40 in the video, Kenneth describes how this would be coded.
# the given code
from django import forms
from . import models
from products.models import Product
class ReviewForm(forms.ModelForm):
class Meta:
model = models.Review
fields = ('headline', 'rating', 'content', 'writer', 'publish_date')
# new code here
# I want to be able to create a Review in the same form as where I
# create a Product. That means I need an inline form!
# Step 1: Create an inline model formset factory, named ReviewFormset, for the
# Review model.
ReviewFormset = forms.inlineformset_factory()
# Step2: Remember, the first argument to the factory is the parent model(Product)
# and the second is the model the factory is for (Review).
ReviewFormset = forms.inlineformset_factory(
Product,
models.Review
)
# Step 3: You need to include all the same fields as the existing ReviewForm.
ReviewFormset = forms.inlineformset_factory(
Product,
models.Review,
fields=('headline', 'rating', 'content', 'writer', 'publish_date')
)
Task 2 says: Great! By default, I get 3 extra forms. That's a lot for a single view since they're big forms. Can you change it so I only get 1 extra?
ReviewFormset = forms.inlineformset_factory(
Product,
models.Review,
fields=('headline', 'rating', 'content', 'writer', 'publish_date'),
# Step 1: Change it so I only get 1 extra
extra=1
)