Functions

FUNCTIONS AND MODULES


Functions

Functions form the smallest executable unit in Python and are used throughout Django for routing, business logic, utilities, filters, signals, and background operations.

Where functions appear in Django

  • HTTP request handlers (function-based views)
  • Utility helpers in utils/
  • Template filters and simple template tags
  • Signal receivers
  • Form validation helpers
  • Management command helpers
  • Async tasks for Celery or built-in async views

Why functions matter in Django

Functions keep code small, focused, reusable, and testable. They allow separation of concerns, enabling clean views and consistent business logic across the project.

Basic function syntax


def add(a, b):
    """Add two numbers and return the result."""
    return a + b
  

Function parameters

Python supports positional, keyword, default, variable-length, and keyword-only arguments. Django utilities often rely on predictable keyword arguments to reduce ambiguity.


def process_order(order_id, *, include_tax=True, include_shipping=False):
    return {
        "id": order_id,
        "tax": include_tax,
        "shipping": include_shipping,
    }
  

Function parameters

Python functions support multiple parameter styles. Each style controls how callers pass values and how functions enforce structure.

1. Positional arguments


def area(length, width):
    return length * width

area(5, 3)
  

2. Keyword arguments


def greet(name, message):
    return f"{message}, {name}"

greet(name="Nischal", message="Hello")
  

3. Default arguments


def power(base, exponent=2):
    return base ** exponent

power(5)        # uses default exponent=2
power(5, 3)
  

4. Variable-length positional arguments *args


def total(*numbers):
    return sum(numbers)

total(1, 2, 3, 4)
  

5. Variable-length keyword arguments **kwargs


def build_profile(**info):
    return info

build_profile(name="Asha", age=22, city="Pokhara")
  

6. Keyword-only arguments

Everything after * must be passed by keyword.


def create_user(username, *, is_admin=False, active=True):
    return {
        "username": username,
        "is_admin": is_admin,
        "active": active,
    }

create_user("ram")                           # ok
create_user("ram", is_admin=True)            # ok
create_user("ram", True, False)              # error
  

Return values

Functions may return any Python object. Django views must return HttpResponse or a subclass, but helpers can return dicts, strings, or complex objects.

Example: function-based view


# views.py
from django.http import HttpResponse
from .utils import render_welcome_message

def welcome_view(request):
    message = render_welcome_message(request.user)
    return HttpResponse(message)


# utils.py
def render_welcome_message(user):
    if user.is_authenticated:
        return f"Welcome back, {user.username}!"
    return "Welcome, guest!"
  

Example: utility function for tests


# utils/strings.py
def slugify_title(title: str) -> str:
    """Return a URL-friendly slug for a blog title."""
    return "-".join(title.lower().split())
  

Higher-order functions

Functions can be passed as arguments or returned from other functions. Django middleware and decorators internally rely on higher-order functions.


def apply_twice(func, value):
    return func(func(value))

def double(x):
    return x * 2

apply_twice(double, 5)  # returns 20
  

Decorators


import time
from functools import wraps

def timing(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.3f}s")
        return result
    return wrapper

@timing
def expensive_calculation(n):
    return n * n
  

Closures

A closure stores variables from its enclosing scope. Some advanced Django utilities (e.g., dynamic validator factories) use closures.


def min_length_validator(min_len):
    def validator(value):
        if len(value) < min_len:
            raise ValueError(f"Value must be at least {min_len} characters.")
    return validator

validate_5 = min_length_validator(5)
  

Async functions in Django

Django supports async views. Async functions allow concurrency for IO-bound tasks.


# async view
from django.http import JsonResponse
import httpx

async def fetch_data(request):
    async with httpx.AsyncClient() as client:
        r = await client.get("https://api.example.com/data")
    return JsonResponse({"data": r.json()})
  

Type hints

Type hints help with readability, editor support, and static analysis (e.g., mypy).


from typing import List

def sum_list(nums: List[int]) -> int:
    return sum(nums)
  

Common pitfalls

  • Too much logic in views: move heavy logic into services/utilities.
  • Mutable default args: avoid lists/dicts as default parameters.
  • Circular imports: structure helpers in dedicated modules.
  • Ignoring return types: keep function outputs predictable.
  • Mixed responsibilities: split large functions into smaller units.

Exercise

Create format_currency(amount, currency="NPR") to produce NPR 1,234.00. Write a unit test asserting correct formatting for several values.

Next step

Move to Modules to organize related functions into reusable packages.

Watch Video in Nepali