Simple Django mistake - Not debugging using network responses πŸ•ΈοΈ

Photo of Tom Dekan
by Tom Dekan
Updated: Fri 03 May 2024

This guide would have saved me a load of time (at least 100 hours) when I was learning.

This small mistake wastes a load of time when debugging Django apps, particularly when you are interacting with third party servers and APIs (often AWS).

This mini-guide will unlock a simple debugging technique for you: using network responses in your dev tools.

But, many people don't do this. Instead forcing themselves to debug their requests and responses with the trial-and-error of print statements and guessing.

So, I'll show you an example with AWS, using a modified version of my guide Build a simple file uploader with Django (and Alpine.js) πŸ”οΈπŸŒ….

Image of uploader

TLDR: Use the network tab in your dev tools to debug your Django app requests. It's much more precise than print statements and guessing.

Error - 400 Forbidden

Here's a modified version of the code for my tutorial on how to upload files really easily to S3 on AWS, using Django + AlpineJS.

With the modified code below, we get an error when we try to upload a file:

400 forbidden image

When we start to upload, we get the error of 400 forbidden. That's an imprecise error message.

The mistake here would be to start by adding print statements or searching online for "Django S3 upload 400 forbidden".

Here's the modified code for reference:

views.py

import json
import os

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.shortcuts import render
from django.http import JsonResponse
from .services import generate_presigned_post


@method_decorator(csrf_exempt, name='dispatch')
def uploader(request):
    if request.method == 'GET':
        return render(request, 'upload.html')

    elif request.method == 'POST':
        body = json.loads(request.body)
        file = body.get('file')
        if not file:
            return JsonResponse({'error': 'Missing file in request body'}, status=400)

        presigned_data = generate_presigned_post(
            bucket_name=os.getenv('BUCKET_NAME'), filename=file['name']
        )
        return JsonResponse(presigned_data)

services.py

import os
import boto3


def generate_presigned_post(bucket_name, filename, expiration=600):
    """
    Docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/generate_presigned_post.html#generate-presigned-post
    """
    s3_client = boto3.client(
        's3',
        aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
        aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY')+'deliberate_mistake'
    )
    return s3_client.generate_presigned_post(
        Bucket=bucket_name, Key=filename,
        ExpiresIn=expiration,
    )

Instead, here's what you should do instead of inserting print statements or guessing:

Solution: Look at your network response

  • Go to your dev tools for whatever browser you're using
  • Go to the network tab.
  • Click on the request that you made to upload
  • Look at the preview.
  • Look the response

This gives us a much more precise error message: The request signature does not match the signature you required.

403 devtools

Now we can identify the error: we were loading an incorrect access key here from our environment variables here.

Corrected code:

import os
import boto3


def generate_presigned_post(bucket_name, filename, expiration=600):
    """
    Docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/generate_presigned_post.html#generate-presigned-post
    """
    s3_client = boto3.client(
        's3',
        aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
        aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY')  # mistake was here
    )
    return s3_client.generate_presigned_post(
        Bucket=bucket_name, Key=filename,
        ExpiresIn=expiration,
    )

Error - 403 Bad Request

Now let's try a slightly different situation. Here's our code containing a different (hidden) mistake.

import os
import boto3


def generate_presigned_post(bucket_name, filename, expiration=600):
    """
    Docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/generate_presigned_post.html#generate-presigned-post
    """
    s3_client = boto3.client(
        's3',
        aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
        aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY')
    )
    return s3_client.generate_presigned_post(
        Bucket=bucket_name, Key=filename,
        ExpiresIn=expiration,
    )

We upload again.

This time, the error message is Bad request. Now it's a 400 error rather than a 403.

So that suggests something is wrong with the data we're sending rather than the authentication. But still, it's unclear.

400

Solution: Look at your network response

As before,

  • Go to your dev tools (for whatever browser you're using)
  • Go to the network tab
  • Click on the request that you made to upload
  • Look at the preview
  • Look the response

-> Clicking on the preview shows us The region US West is wrong. Expecting EU Central 1.:

Image

Great. So, now we add region eu-central-1:

import os
import boto3


def generate_presigned_post(bucket_name, filename, expiration=600):
    """
    Docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/generate_presigned_post.html#generate-presigned-post
    """
    s3_client = boto3.client(
        's3',
        aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
        aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
        region_name='eu-central-1'  # Or whatever your region is. You can also set this in your environment variables using the AWS CLI.
    )
    return s3_client.generate_presigned_post(
        Bucket=bucket_name, Key=filename,
        ExpiresIn=expiration,
    )

Upload successful βœ…

File upload successful

Congrats - simple debugging technique unlocked βœ…

Here are some related guides about how to upload files simply with Django:

P.S Want to build Django frontend faster?

Probably like you, I want to get my Django frontend out fast as possible (preferably instantly).

So, I'm building Photon Designer

Photon Designer lets me produce Django frontend visually and extremely quickly - like a painter sweeping his brush across the page πŸ–ŒοΈ.

If you found this guide helpful, you can check out Photon Designer .

Let's get visual.

Do you want to create beautiful frontends effortlessly?
Click below to book your spot on our early access mailing list (as well as early adopter prices).
Copied link to clipboard πŸ“‹

Made with care by Tom Dekan

Β© 2024 Photon Designer