🔹 Decorators in Python, Django and FastAPI in details with examples
In Python, a decorator is a function that wraps another function or class to modify or extend its behavior without changing its code directly.
Think of it like:
“A decorator takes a function/class as input → adds some extra functionality → and returns a new function/class.”
🔹 Example 1 – Simple function decorator
def my_decorator(func):
def wrapper():
print("Before function runs")
func()
print("After function runs")
return wrapper
@my_decorator # same as: hello = my_decorator(hello)
def hello():
print("Hello, World!")
hello()
Output:
Before function runs
Hello, World!
After function runs
Here:
@my_decoratorwraps thehello()function.- When you call
hello(), actuallywrapper()runs.
🔹 Example 2 – Decorator with arguments
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3) # repeat hello 3 times
def hello(name):
print(f"Hello {name}!")
hello("Atif")
Output:
Hello Atif!
Hello Atif!
Hello Atif!
🔹 Example 3 – Django decorator
In Django, you’ve seen things like:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
Here @login_required ensures that only logged-in users can access that view.
🔹 Example 4 – Class decorator (Django admin)
In your admin file, you saw:
@admin.register(Payment)
class PaymentAdmin(admin.ModelAdmin):
...
This is a class decorator.
It tells Django: “Register the Payment model in the admin, using this custom admin class.”
Equivalent without decorator:
class PaymentAdmin(admin.ModelAdmin):
...
admin.site.register(Payment, PaymentAdmin)
🔹 Example 5 – FastAPI decorator
In FastAPI, routes are defined with decorators:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def say_hello():
return {"msg": "Hello World"}
Here @app.get("/hello") is a decorator that says: “When someone sends a GET request to /hello, call this function.”
###✅ Summary for decorators:
- Decorators = wrappers that extend/modify behavior of functions/classes.
- They’re widely used in Django (admin, views, permissions) and FastAPI (routes, middlewares).
##how to write your own decorator step by step
Let’s build your own decorators step by step. We’ll start from very basic → then add arguments → then apply in Django-like use cases.
🛠 Step 1: Basic decorator
def simple_decorator(func):
def wrapper():
print("👉 Before the function")
func()
print("👉 After the function")
return wrapper
@simple_decorator
def say_hello():
print("Hello Atif!")
say_hello()
Output:
👉 Before the function
Hello Atif!
👉 After the function
📌 Here:
@simple_decoratoris applied tosay_hello.- When you call
say_hello(), Python actually runswrapper().
🛠 Step 2: Decorator for any function with arguments
def log_args(func):
def wrapper(*args, **kwargs):
print(f"Function {func.__name__} called with args={args}, kwargs={kwargs}")
return func(*args, **kwargs) # run the real function
return wrapper
@log_args
def add(a, b):
return a + b
print(add(3, 5))
Output:
Function add called with args=(3, 5), kwargs={}
8
🛠 Step 3: Decorator with arguments
Sometimes you want to pass options to your decorator itself.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n):
print(f"Run {i+1} of {n}")
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3) # repeat the function 3 times
def greet(name):
print(f"Hello {name}")
greet("Atif")
Output:
Run 1 of 3
Hello Atif
Run 2 of 3
Hello Atif
Run 3 of 3
Hello Atif
Decorator in Django
🛠 A Django-like decorator
Let’s make our own login_required style decorator:
def my_login_required(func):
def wrapper(request, *args, **kwargs):
if not getattr(request, "user", None): # check if request has a user
return "❌ User not logged in!"
return func(request, *args, **kwargs)
return wrapper
# fake request objects
class Request:
def __init__(self, user=None):
self.user = user
@my_login_required
def dashboard(request):
return f"Welcome {request.user}!"
print(dashboard(Request())) # no user
print(dashboard(Request("Atif"))) # with user
Output:
❌ User not logged in!
Welcome Atif!
🛠 Using class decorator (like Django Admin)
def register_model(model_name):
def decorator(admin_class):
print(f"✅ Registered {model_name} with admin class {admin_class.__name__}")
return admin_class
return decorator
@register_model("Payment")
class PaymentAdmin:
pass
Output:
✅ Registered Payment with admin class PaymentAdmin
📌 This is exactly how @admin.register(Model) works internally.
###✅ Summary for Django Decorators:
- A decorator is a function that wraps another function/class.
@decorator_nameis just shorthand forfunction = decorator_name(function).- They’re useful for authentication checks, logging, caching, registering routes/admins, etc.
##decorators in FastAPI. They work the same as Python decorators, but in FastAPI they’re often used for middleware-like behavior (before/after running your endpoint).
🛠 Example 1: Simple logging decorator
from fastapi import FastAPI
app = FastAPI()
# Custom decorator
def log_request(func):
async def wrapper(*args, **kwargs):
print(f"👉 Calling endpoint: {func.__name__}")
result = await func(*args, **kwargs)
print(f"✅ Finished endpoint: {func.__name__}")
return result
return wrapper
@app.get("/hello")
@log_request
async def say_hello():
return {"message": "Hello Atif!"}
When you visit /hello:
👉 Calling endpoint: say_hello
✅ Finished endpoint: say_hello
🛠 Example 2: Decorator to check API Key
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
def require_api_key(func):
async def wrapper(request: Request, *args, **kwargs):
api_key = request.headers.get("X-API-Key")
if api_key != "secret123":
raise HTTPException(status_code=403, detail="Invalid API Key")
return await func(request, *args, **kwargs)
return wrapper
@app.get("/secure")
@require_api_key
async def secure_endpoint(request: Request):
return {"message": "You are authorized!"}
🔑 If you call /secure without X-API-Key: secret123, you’ll get:
{"detail": "Invalid API Key"}
🛠 Example 3: Decorator with arguments (rate limiter style)
import time
from fastapi import FastAPI, HTTPException
app = FastAPI()
def rate_limit(seconds: int):
last_called = {}
def decorator(func):
async def wrapper(*args, **kwargs):
now = time.time()
if func.__name__ in last_called and now - last_called[func.__name__] < seconds:
raise HTTPException(status_code=429, detail="Too many requests")
last_called[func.__name__] = now
return await func(*args, **kwargs)
return wrapper
return decorator
@app.get("/ping")
@rate_limit(5) # limit calls to every 5 seconds
async def ping():
return {"message": "pong!"}
- First request works ✅
- Second request within 5s → 429 Too Many Requests
🛠 Example 4: Class decorator for routes (like Django’s @admin.register)
def tag_routes(tag: str):
def decorator(func):
func._tag = tag # attach metadata
return func
return decorator
app = FastAPI()
@app.get("/items")
@tag_routes("inventory")
async def get_items():
return {"items": ["apple", "banana"]}
# Later you could inspect `get_items._tag` == "inventory"
##✅ Summary for FastAPI decorators
-
Work same as Python decorators
-
Useful for:
- Logging
- Auth / API keys
- Rate limiting
- Attaching metadata
-
You can mix them with FastAPI’s built-in dependencies, but decorators give more fine-grained control.
![Apple: iPhone China sales slide as Huawei soars, report says 1 iPhone 15 [GETTY IMAGES]](https://360world.tech/wp-content/uploads/2024/03/image-1.png)
![TikTok sparks user revolt in US over sale plan 2 Tiktok [GETTY IMAGES]](https://360world.tech/wp-content/uploads/2024/03/image.png)