Django REMOTE_IP to X-Forwarded-For middleware

Bij het uitrollen van een Django applicatie met Gunicorn als WSGI server, klopt de request.META[“REMOTE_IP”] waarde vaak niet. Deze toont dan het IP van de reverse proxy, of is zelfs leeg. We kunnen dit oplossen met een klein stukje middleware:

# app/middleware.py
class XForwardedForMiddleware(MiddlewareMixin):
def process_request(self, request):
if 'HTTP_X_FORWARDED_FOR' in request.META:
request.META['REMOTE_ADDR'] = request.META['HTTP_X_FORWARDED_FOR'].split(",")[0].strip()
return None

Vervolgens kunnen we deze middleware inschakelen door ‘app.middleware.XForwardedForMiddleware’ aan onze middleware toe te voegen in settings.py

Coderunner.app en python virtual environments

Ik ben de laatste tijd wat aan het experimenteren met CodeRunner als mijn IDE, maar ik stuitte op 1 probleem: Ik gebruik Python Virtual Environments maar Coderunner gebruikte deze standaard niet. Gelukkig is er niets dat je niet kan oplossen met wat scripting en code, en CodeRunner laat je toe een compilatiescript te gebruiken voor het uitvoeren.

Als Python compiler script kan je het volgende gebruiken:

# Initialize the current directory
dir="$(pwd)"

# Search up to three parent directories
for i in {1..4}; do
	# Check if the .venv directory exists
	if [[ -d "$dir/.venv" ]]; then
		echo "$dir/.venv/bin/activate"
		exit 0
	fi
	# Move up one directory
	dir="$(dirname "$dir")"
done

# If no .venv is found, print a message and exit with non-zero status
echo "No .venv directory found within the current or parent directories."
exit 1

Vervolgens moet je het volgende toevoegen als compiler commando

source $compiler; python3 $filename

Viola, als je nu een script uitvoert binnen CodeRunner, gebruikt het automatisch de juiste .venv!

Page generation time tonen in Django

Bij het bouwen van een django applicatie kan het soms handig zijn om te weten hoe lang het duurde om een pagina te genereren. We kunnen namelijk wel zien hoe lang het duurde om een pagina te laden in de developer tools van onze browser, maar niet hoelang het werkelijk duurde om de pagina te genereren. In Django kunnen we dit oplossen met een stukje middleware

In een bestand (bijvoorbeeld “app/middleware.py” kunnen we het volgende toevoegen:

import time
class StatsMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        request.start_time = time.time()

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.
        duration = time.time() - request.start_time
        response["X-Page-Generation-Duration"] = str(round(duration,3))+"s"
        return response

Vervolgens kunnen we de middleware laden. Het is belangrijk dat deze middleware als eerste in de MIDDLEWARE configuratie instelling.

Deze middleware zal een header “X-Page-Generation-Duration” toevoegen met de tijd die het duurde om de pagina te genereren.

 

Django Caching, i18n en Authenticatie

Django bied geweldige tools voor caching eenvoudig maken. Het heeft naar mijn mening wel 2 grote beperkingen:

  • Het stuurt automatisch Cache-control headers, waardoor de client’s cache verhinderd dat de pagina goed herlaad als de taal gewijzigd word en er geen taal prefix in de url gebruikt word
  • Het cached niet per gebruiker, waardoor als we niet opletten er mogelijks private informatie gelekt kan worden.

Beide van deze beperkingen waren oorspronkelijk een probleem bij het herschrijven van Skyz. Het eerste probleem kon deels verholpen worden door taalprefixes toe te voeren aan de url’s, de andere problemen kon ik oplossen op de volgende manieren.

Lees verder

Cache toevoegen aan Django

Een cache toevoegen kan een enorme performantieboost zijn voor een website. Django maakt het eenvoudig om een cache toe te voegen, het laat out of the box 3 manieren om te cachen toe:

  • Volledige site caching (elke pagina krijgt dezelfde behandeling)
  • Per view caching (Er kan per view bepaald worden of er gecached moet worden, en voor hoelang)
  • Low level caching (waar individuele objecten of databasequeries gecached kunnen worden.

Als ik bij een test applicatie per-view caching toevoeg als test, kan ik op een VM met 2 cores 600 requests/seconde verwerken in plaats van 200/seconde. In deze post zal ik toelichten hoe je per-view caching kan implementeren binnen Django

Lees verder

File uploads automatisch hernoemen in Django

Als we in Django een ImageField toevoegen aan een model gebruikt het de bestandsnaam van het originele bestand. Dit is niet ideaal voor applicaties waar gebruikers hun eigen foto’s kunnen uploaden. We kunnen dit oplossen door een functie te geven aan het argument “upload_to”. Het volgende voorbeeld vervangt de bestandsnaam door een automatisch gegenereerde bestandsnaam

import os
from uuid import uuid4
from django.utils.deconstruct import deconstructible

@deconstructible
class PathAndRename(object):

    def __init__(self, sub_path):
        self.path = sub_path

    def __call__(self, instance, filename):
        ext = filename.split('.')[-1]
        # set filename as random string
        filename = '{}.{}'.format(uuid4().hex, ext)
        # return the whole path to the file
        return os.path.join(self.path, filename)

# Then in your model
class Image(models.Model):
    #... other fields
    path_and_rename = PathAndRename('uploads/images/')
    image = models.ImageField(upload_to=path_and_rename)