Django 1.11 TypeError context must be a dict rather than Context

Question:

Just received the Sentry error TypeError context must be a dict rather than Context. on one of my forms. I know it has something to do with Django 1.11, but I am not sure what to change to fix it.

Offending line

message = get_template('email_forms/direct_donation_form_email.html').render(Context(ctx))

Entire View

def donation_application(request):
    if request.method == 'POST':
        form = DirectDonationForm(data=request.POST)
        if form.is_valid():
            stripe.api_key = settings.STRIPE_SECRET_KEY
            name = request.POST.get('name', '')
            address = request.POST.get('address', '')
            city = request.POST.get('city', '')
            state = request.POST.get('state', '')
            zip = request.POST.get('zip', '')
            phone_number = request.POST.get('phone_number', '')
            support = request.POST.get('support', '')
            agree = request.POST.get('agree', '')
            email_address = request.POST.get('email_address', '')
            number = request.POST.get('number', '')
            cvc = request.POST.get('cvc', '')
            exp = request.POST.get('exp', '')
            # token = form.cleaned_data['stripe_token'],
            # exp_m = int(request.POST.get('exp_month', ''))
            # exp_y = int(request.POST.get('exp_year', ''))

            exp_month = exp[0:2]
            exp_year = exp[5:9]

            subject = 'New Donation'
            from_email = settings.DEFAULT_FROM_EMAIL
            recipient_list = ['deniselarkins@/////\\.com',
                              'charles@/////\\.net',
                              'marcmunic@/////\\.com',
                              ]

            token = stripe.Token.create(
                card={
                    'number': number,
                    'exp_month': exp_month,
                    'exp_year': exp_year,
                    'cvc': cvc
                },
            )

            customer = stripe.Customer.create(
                email=email_address,
                source=token,
            )

            total_support = decimal.Decimal(support) / 100
            total_charge = decimal.Decimal(int(support)) / 100

            # Charge the user's card:
            charge = stripe.Charge.create(
                amount=total_charge,
                currency='usd',
                description='Donation',
                customer=customer.id
            )

            ctx = {
                'name': name,
                'address': address,
                'city': city,
                'state': state,
                'zip': zip,
                'phone_number': phone_number,
                'email_address': email_address,
                'agree': agree,
                'charge': charge,
                'customer': customer,
                'total_support': total_support,
                'total_charge': total_charge
            }

            message = get_template('email_forms/direct_donation_form_email.html').render(Context(ctx))
            msg = EmailMessage(subject, message, from_email=from_email, to=recipient_list)
            msg.content_subtype = 'html'
            msg.send(fail_silently=True)

            return redirect(
                '/contribute/donation-support-thank-you/?name=' + name +
                '&address=' + address +
                '&state=' + state +
                '&city=' + city +
                '&zip=' + zip +
                '&phone_number=' + phone_number +
                '&email_address=' + email_address +
                '&total_support=' + str(total_support) +
                '&total_charge=' + str(total_charge)
            )
    context = {
        'title': 'Donation Pledge',
    }

    return render(request, 'contribute/_donation-application.html', context)
Asked By: Charles Smith

||

Answers:

In Django 1.8+, the template’s render method takes a dictionary for the context parameter. Support for passing a Context instance is deprecated, and gives an error in Django 1.10+.

In your case, just use a regular dict instead of a Context instance:

message = get_template('email_forms/direct_donation_form_email.html').render(ctx)

You may prefer to use the render_to_string shortcut:

from django.template.loader import render_to_string

message = render_to_string('email_forms/direct_donation_form_email.html', ctx)

If you were using RequestContext instead of Context, then you would pass the request to these methods as well so that the context processors run.

message = get_template('email_forms/direct_donation_form_email.html').render(ctx, request=request)
message = render_to_string('email_forms/direct_donation_form_email.html', ctx, request=request)
Answered By: Alasdair

Migrated from Django 1.8 to Django 1.11.6

Wherever i had a RequestContext class, there is a method flatten() wich return the result as a dict.

So if the class is RequestContext….

return t.render(context)

becomes

return t.render(context.flatten())

And in a case wich the context is is wrapped by Context(), just remove it. Because Context() is deprecated.

return t.render(Context(ctx))

becomes

return t.render(ctx)
Answered By: VIctor Angelov

For django 1.11 and after, context must be dict.

You can use:

context_dict = get_context_dict(context)
return t.render(context_dict)

or

context_dict = context.flatten()
return t.render(context_dict)
Answered By: Yvette Tan

I got here because I had the same issue. I’m learning Django with Django Unleashed by Andrew Pinkham. It’s a book from 2015.

I found in the official documentation, that a dictionary must be passed to the context parameter and not a Context instance (from django.template.Context).

@Alasdair suggested to use render_to_string, but, at least in Django 3.2 the
render method use render_to_string method intrinsically.

def render(request, template_name, context=None, content_type=None, status=None, using=None):
"""
Return a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)

so, using just the render method could be better. I provide this answer because was the one that I was looking for and it may help some one reaching this Stack Overflow question.

Answered By: Eadwulf
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.