Website Backend Development with Python (Django)

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Showing 1 of 1 servicesAll 2065 services
Website Backend Development with Python (Django)
Medium
from 1 week to 3 months
FAQ
Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Website Backend Development with Python (Django)

Django is "batteries included" in the literal sense. ORM, migrations, admin panel, authentication, forms, caching, i18n — all included out of the box, without library selection and component integration. This makes Django the fastest way to launch a full-fledged backend when you don't need exotic features.

Typical use cases: corporate websites, CMS, portals, APIs for SPA and mobile applications, systems with rich business logic and role-based access.

Project Structure

A Django project consists of a project and apps. Good practice is to keep apps small and functionally isolated:

myproject/
  manage.py
  config/
    settings/
      base.py
      development.py
      production.py
    urls.py
    wsgi.py
    asgi.py
  apps/
    users/
      models.py
      views.py
      serializers.py  # for DRF
      urls.py
      admin.py
      services.py      # business logic separate from views
      tests/
        test_models.py
        test_views.py
    products/
    orders/
  requirements/
    base.txt
    development.txt
    production.txt

Settings are divided by environment and secrets are never committed to the repository:

# config/settings/base.py
from pathlib import Path
import environ

env = environ.Env()
BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = env('DJANGO_SECRET_KEY')
DATABASES = {
    'default': env.db('DATABASE_URL', default='postgres://localhost/mydb')
}
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': env('REDIS_URL', default='redis://localhost:6379/0'),
        'OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'}
    }
}

Models and ORM

Django's strength is an expressive ORM with support for complex queries:

from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.contrib.postgres.indexes import GinIndex

class Product(models.Model):
    name = models.CharField(max_length=255)
    slug = models.SlugField(unique=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    category = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True)
    attributes = models.JSONField(default=dict)
    tags = ArrayField(models.CharField(max_length=50), default=list)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        indexes = [
            models.Index(fields=['slug']),
            models.Index(fields=['category', 'is_active']),
            GinIndex(fields=['attributes']),  # for JSONB search
        ]
        ordering = ['-created_at']

    def __str__(self):
        return self.name

Complex queries via annotate, aggregate, prefetch_related:

from django.db.models import Count, Avg, Q, Prefetch

# Get categories with count of active products and average price
categories = Category.objects.annotate(
    products_count=Count('product', filter=Q(product__is_active=True)),
    avg_price=Avg('product__price', filter=Q(product__is_active=True))
).filter(products_count__gt=0).order_by('-products_count')

# N+1 problem solved with prefetch_related
orders = Order.objects.prefetch_related(
    Prefetch('items', queryset=OrderItem.objects.select_related('product'))
).select_related('user').filter(status='processing')

Django REST Framework

DRF is the standard for Django APIs. Serializers, ViewSets, pagination:

from rest_framework import serializers, viewsets, permissions, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend

class ProductSerializer(serializers.ModelSerializer):
    category_name = serializers.CharField(source='category.name', read_only=True)

    class Meta:
        model = Product
        fields = ['id', 'name', 'slug', 'price', 'category', 'category_name', 'attributes']
        read_only_fields = ['slug']

    def validate_price(self, value):
        if value <= 0:
            raise serializers.ValidationError('Price must be positive')
        return value

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.select_related('category').filter(is_active=True)
    serializer_class = ProductSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ['category', 'is_active']
    search_fields = ['name', 'description']
    ordering_fields = ['price', 'created_at']
    ordering = ['-created_at']

    @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
    def favorite(self, request, pk=None):
        product = self.get_object()
        request.user.favorites.add(product)
        return Response({'status': 'added'})

Authentication

JWT via djangorestframework-simplejwt:

# config/urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path('api/auth/login/', TokenObtainPairView.as_view()),
    path('api/auth/refresh/', TokenRefreshView.as_view()),
]

Custom JWT payload:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class CustomTokenSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        token['email'] = user.email
        token['role'] = user.role
        return token

Celery for Background Tasks

# tasks.py
from celery import shared_task
from django.core.mail import send_mail

@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def send_order_confirmation(self, order_id: int) -> None:
    try:
        order = Order.objects.select_related('user').get(id=order_id)
        send_mail(
            subject=f'Order #{order.id} confirmed',
            message=render_email_template('order_confirmation', order),
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[order.user.email]
        )
    except Order.DoesNotExist:
        # don't retry — object not found
        return
    except Exception as exc:
        raise self.retry(exc=exc)

Caching

from django.core.cache import cache
from django.views.decorators.cache import cache_page
from functools import wraps

# Low-level caching
def get_popular_products():
    cache_key = 'popular_products'
    result = cache.get(cache_key)
    if result is None:
        result = list(Product.objects.filter(is_active=True).order_by('-views')[:10])
        cache.set(cache_key, result, timeout=300)
    return result

# Invalidation by tags via django-cache-memoize
from cache_memoize import cache_memoize

@cache_memoize(300, extra_verbose_cache_key=True)
def get_category_products(category_id: int, page: int = 1):
    # ...

Admin Panel

Django's built-in admin panel saves weeks of work:

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ['name', 'price', 'category', 'is_active', 'created_at']
    list_filter = ['category', 'is_active', 'created_at']
    search_fields = ['name', 'slug']
    list_editable = ['is_active', 'price']
    prepopulated_fields = {'slug': ('name',)}
    readonly_fields = ['created_at', 'updated_at']

    def get_queryset(self, request):
        return super().get_queryset(request).select_related('category')

Development Timelines

  • Project setup, DB, auth — 3–5 days
  • Models + migrations + admin — 1 week
  • API on DRF — 1–3 weeks depending on complexity
  • Celery + Redis + queues — 3–5 days
  • Integrations (payments, email, third-party APIs) — 1–2 weeks
  • Tests (pytest-django) — 1 week

Full backend for a corporate website or portal: 5–10 weeks. Django pays for itself with the built-in admin panel — often the client manages content through it without a separate CMS.