Przejdź do treści
Backend

Django - Szybkie Tworzenie Aplikacji Webowych w Pythonie

Opublikowano:
·4 min czytania·Autor: MDS Software Solutions Group

Django Szybkie Tworzenie

backend

Django - Szybkie Tworzenie Aplikacji Webowych w Pythonie

Django to jeden z najpopularniejszych frameworków webowych na świecie, napisany w Pythonie i zaprojektowany z myślą o szybkim, pragmatycznym tworzeniu aplikacji. Od momentu swojego powstania w 2005 roku, Django napędza tysiące serwisów internetowych - od małych startupów po gigantów takich jak Instagram, Pinterest, Mozilla czy Disqus. W tym artykule szczegółowo omówimy kluczowe funkcje Django, pokażemy praktyczne przykłady kodu i wyjaśnimy, dlaczego ten framework jest doskonałym wyborem dla nowoczesnych projektów backendowych.

Czym jest Django i filozofia "batteries included"#

Django wyróżnia się na tle innych frameworków swoją filozofią "batteries included" (baterie w zestawie). Oznacza to, że framework dostarcza kompletny zestaw narzędzi potrzebnych do budowy aplikacji webowej - od systemu ORM, przez panel administracyjny, po mechanizmy uwierzytelniania i obsługę formularzy. Nie musisz szukać zewnętrznych bibliotek do podstawowych zadań.

Główne zasady projektowe Django to:

  • DRY (Don't Repeat Yourself) - unikanie powielania kodu i logiki
  • Szybki rozwój - framework zaprojektowano, aby przejść od koncepcji do gotowej aplikacji w jak najkrótszym czasie
  • Bezpieczeństwo - wbudowana ochrona przed CSRF, XSS, SQL Injection i clickjackingiem
  • Skalowalność - architektura umożliwiająca obsługę milionów użytkowników

Instalacja Django jest prosta:

# Utworzenie wirtualnego środowiska
python -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate   # Windows

# Instalacja Django
pip install django

# Utworzenie nowego projektu
django-admin startproject myproject
cd myproject

# Utworzenie aplikacji
python manage.py startapp blog

# Uruchomienie serwera deweloperskiego
python manage.py runserver

Django ORM i modele#

Jedną z największych zalet Django jest jego Object-Relational Mapping (ORM) - system, który pozwala na interakcję z bazą danych za pomocą kodu Pythona zamiast surowych zapytań SQL. Django ORM obsługuje PostgreSQL, MySQL, SQLite, Oracle i wiele innych baz danych.

# blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import slugify


class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    description = models.TextField(blank=True)

    class Meta:
        verbose_name_plural = "categories"
        ordering = ["name"]

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)


class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = "draft", "Szkic"
        PUBLISHED = "published", "Opublikowany"
        ARCHIVED = "archived", "Zarchiwizowany"

    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique_for_date="publish_date")
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    body = models.TextField()
    publish_date = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(
        max_length=10, choices=Status.choices, default=Status.DRAFT
    )
    tags = models.ManyToManyField("Tag", blank=True)

    class Meta:
        ordering = ["-publish_date"]
        indexes = [
            models.Index(fields=["-publish_date"]),
            models.Index(fields=["slug"]),
        ]

    def __str__(self):
        return self.title


class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    slug = models.SlugField(max_length=50, unique=True)

    def __str__(self):
        return self.name

Praca z ORM jest intuicyjna i wydajna:

# Tworzenie rekordów
category = Category.objects.create(name="Python", slug="python")
post = Post.objects.create(
    title="Mój pierwszy post",
    author=user,
    category=category,
    body="Treść artykułu...",
    status=Post.Status.PUBLISHED,
)

# Zapytania z filtrowaniem
published_posts = Post.objects.filter(status=Post.Status.PUBLISHED)
python_posts = Post.objects.filter(category__name="Python")
recent_posts = Post.objects.filter(
    publish_date__year=2025
).select_related("author", "category")

# Agregacje
from django.db.models import Count, Avg
stats = Post.objects.aggregate(
    total=Count("id"),
    avg_length=Avg(models.functions.Length("body")),
)

# Annotacje
categories_with_count = Category.objects.annotate(
    post_count=Count("post")
).order_by("-post_count")

Django REST Framework dla API#

Django REST Framework (DRF) to potężna biblioteka rozszerzająca Django o możliwość budowy REST API. Oferuje serializację, widoki API, uwierzytelnianie, paginację i wiele więcej.

pip install djangorestframework
# blog/serializers.py
from rest_framework import serializers
from .models import Post, Category, Tag


class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ["id", "name", "slug"]


class CategorySerializer(serializers.ModelSerializer):
    post_count = serializers.IntegerField(read_only=True)

    class Meta:
        model = Category
        fields = ["id", "name", "slug", "description", "post_count"]


class PostListSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField()
    category = CategorySerializer(read_only=True)

    class Meta:
        model = Post
        fields = [
            "id", "title", "slug", "author", "category",
            "publish_date", "status",
        ]


class PostDetailSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField()
    category = CategorySerializer(read_only=True)
    tags = TagSerializer(many=True, read_only=True)

    class Meta:
        model = Post
        fields = [
            "id", "title", "slug", "author", "category",
            "body", "publish_date", "updated", "status", "tags",
        ]


# blog/views_api.py
from rest_framework import viewsets, permissions, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Count


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.select_related("author", "category").prefetch_related("tags")
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ["status", "category__slug"]
    search_fields = ["title", "body"]
    ordering_fields = ["publish_date", "title"]

    def get_serializer_class(self):
        if self.action == "list":
            return PostListSerializer
        return PostDetailSerializer

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

    @action(detail=False, methods=["get"])
    def popular(self, request):
        popular_posts = self.get_queryset().annotate(
            tag_count=Count("tags")
        ).order_by("-tag_count")[:10]
        serializer = PostListSerializer(popular_posts, many=True)
        return Response(serializer.data)


# blog/urls.py
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"posts", PostViewSet)

urlpatterns = router.urls

Panel administracyjny - automatyczny CRUD#

Jedną z najbardziej wyróżniających cech Django jest automatyczny panel administracyjny. Wystarczy kilka linii kodu, aby uzyskać kompletny interfejs do zarządzania danymi.

# blog/admin.py
from django.contrib import admin
from .models import Post, Category, Tag


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ["name", "slug", "post_count"]
    prepopulated_fields = {"slug": ("name",)}
    search_fields = ["name"]

    def post_count(self, obj):
        return obj.post_set.count()
    post_count.short_description = "Liczba postów"


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "author", "category", "status", "publish_date"]
    list_filter = ["status", "category", "publish_date"]
    search_fields = ["title", "body"]
    prepopulated_fields = {"slug": ("title",)}
    raw_id_fields = ["author"]
    date_hierarchy = "publish_date"
    ordering = ["-publish_date"]
    list_editable = ["status"]
    list_per_page = 25

    fieldsets = (
        (None, {
            "fields": ("title", "slug", "author", "category")
        }),
        ("Treść", {
            "fields": ("body", "tags"),
        }),
        ("Publikacja", {
            "fields": ("status",),
            "classes": ("collapse",),
        }),
    )


@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ["name", "slug"]
    prepopulated_fields = {"slug": ("name",)}

Panel admina jest gotowy do użycia po uruchomieniu migracji i utworzeniu superużytkownika:

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

Routing URL i widoki#

Django oferuje elastyczny system routingu URL oparty na wzorcach. Wspiera zarówno widoki funkcyjne, jak i klasowe.

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("blog.urls")),
    path("", include("blog.web_urls")),
]


# blog/web_urls.py
from django.urls import path
from . import views

app_name = "blog"

urlpatterns = [
    path("", views.PostListView.as_view(), name="post_list"),
    path("category/<slug:slug>/", views.CategoryPostListView.as_view(), name="category"),
    path("<int:year>/<slug:slug>/", views.PostDetailView.as_view(), name="post_detail"),
    path("search/", views.SearchView.as_view(), name="search"),
]


# blog/views.py
from django.views.generic import ListView, DetailView
from django.db.models import Q
from .models import Post, Category


class PostListView(ListView):
    model = Post
    template_name = "blog/post_list.html"
    context_object_name = "posts"
    paginate_by = 10

    def get_queryset(self):
        return Post.objects.filter(
            status=Post.Status.PUBLISHED
        ).select_related("author", "category")


class CategoryPostListView(ListView):
    template_name = "blog/post_list.html"
    context_object_name = "posts"
    paginate_by = 10

    def get_queryset(self):
        self.category = Category.objects.get(slug=self.kwargs["slug"])
        return Post.objects.filter(
            category=self.category,
            status=Post.Status.PUBLISHED,
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["category"] = self.category
        return context


class PostDetailView(DetailView):
    model = Post
    template_name = "blog/post_detail.html"
    context_object_name = "post"

    def get_queryset(self):
        return Post.objects.filter(
            status=Post.Status.PUBLISHED,
            publish_date__year=self.kwargs["year"],
        )


class SearchView(ListView):
    template_name = "blog/search_results.html"
    context_object_name = "results"
    paginate_by = 10

    def get_queryset(self):
        query = self.request.GET.get("q", "")
        if query:
            return Post.objects.filter(
                Q(title__icontains=query) | Q(body__icontains=query),
                status=Post.Status.PUBLISHED,
            )
        return Post.objects.none()

Szablony i formularze#

System szablonów Django jest potężny i bezpieczny - automatycznie escapuje HTML, zapobiegając atakom XSS.

<!-- templates/blog/base.html -->
<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Blog{% endblock %}</title>
</head>
<body>
    <nav>
        <a href="{% url 'blog:post_list' %}">Strona główna</a>
        {% for category in categories %}
            <a href="{% url 'blog:category' category.slug %}">
                {{ category.name }}
            </a>
        {% endfor %}
    </nav>

    <main>
        {% block content %}{% endblock %}
    </main>
</body>
</html>

<!-- templates/blog/post_detail.html -->
{% extends "blog/base.html" %}

{% block title %}{{ post.title }}{% endblock %}

{% block content %}
<article>
    <h1>{{ post.title }}</h1>
    <p>Autor: {{ post.author }} | {{ post.publish_date|date:"d.m.Y" }}</p>
    <div>{{ post.body|linebreaks }}</div>

    <div>
        {% for tag in post.tags.all %}
            <span class="tag">{{ tag.name }}</span>
        {% endfor %}
    </div>
</article>

{% if post.category %}
<h3>Inne posty w kategorii {{ post.category.name }}:</h3>
{% for related in related_posts %}
    <a href="{% url 'blog:post_detail' related.publish_date.year related.slug %}">
        {{ related.title }}
    </a>
{% endfor %}
{% endif %}
{% endblock %}

Formularze Django automatycznie walidują dane i generują pola HTML:

# blog/forms.py
from django import forms
from .models import Post, Comment


class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ["title", "category", "body", "tags", "status"]
        widgets = {
            "title": forms.TextInput(attrs={"class": "form-control"}),
            "body": forms.Textarea(attrs={"class": "form-control", "rows": 10}),
            "category": forms.Select(attrs={"class": "form-select"}),
        }

    def clean_title(self):
        title = self.cleaned_data["title"]
        if len(title) < 5:
            raise forms.ValidationError("Tytuł musi mieć co najmniej 5 znaków.")
        return title


class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, label="Imię i nazwisko")
    email = forms.EmailField(label="Adres e-mail")
    subject = forms.CharField(max_length=200, label="Temat")
    message = forms.CharField(widget=forms.Textarea, label="Wiadomość")

    def send_email(self):
        from django.core.mail import send_mail
        send_mail(
            subject=self.cleaned_data["subject"],
            message=self.cleaned_data["message"],
            from_email=self.cleaned_data["email"],
            recipient_list=["kontakt@example.com"],
        )

Uwierzytelnianie i uprawnienia#

Django posiada rozbudowany system uwierzytelniania i autoryzacji, który można łatwo dostosować do potrzeb projektu.

# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUser(AbstractUser):
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to="avatars/", blank=True)
    website = models.URLField(blank=True)

    def __str__(self):
        return self.username


# accounts/views.py
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.decorators import login_required
from django.views.generic import CreateView


class CreatePostView(LoginRequiredMixin, CreateView):
    model = Post
    form_class = PostForm
    template_name = "blog/post_form.html"
    login_url = "/accounts/login/"

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)


class AdminPostView(PermissionRequiredMixin, ListView):
    model = Post
    permission_required = "blog.change_post"
    template_name = "blog/admin_posts.html"


# DRF - uwierzytelnianie tokenem JWT
# pip install djangorestframework-simplejwt

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticatedOrReadOnly",
    ],
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 20,
}

Middleware i sygnały#

Middleware pozwala na przetwarzanie żądań i odpowiedzi na poziomie globalnym, a sygnały umożliwiają wykonywanie kodu w reakcji na zdarzenia w aplikacji.

# blog/middleware.py
import time
import logging

logger = logging.getLogger(__name__)


class RequestTimingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        start_time = time.time()
        response = self.get_response(request)
        duration = time.time() - start_time

        logger.info(
            f"{request.method} {request.path} - {response.status_code} "
            f"({duration:.3f}s)"
        )

        response["X-Request-Duration"] = f"{duration:.3f}s"
        return response


class SecurityHeadersMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response["X-Content-Type-Options"] = "nosniff"
        response["X-Frame-Options"] = "DENY"
        response["Referrer-Policy"] = "strict-origin-when-cross-origin"
        return response


# blog/signals.py
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.core.cache import cache
from .models import Post


@receiver(post_save, sender=Post)
def invalidate_post_cache(sender, instance, **kwargs):
    cache.delete(f"post_{instance.slug}")
    cache.delete("post_list")
    if instance.status == Post.Status.PUBLISHED:
        logger.info(f"Post opublikowany: {instance.title}")


@receiver(pre_delete, sender=Post)
def cleanup_on_delete(sender, instance, **kwargs):
    cache.delete(f"post_{instance.slug}")
    logger.info(f"Post usunięty: {instance.title}")


# blog/apps.py
from django.apps import AppConfig


class BlogConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "blog"

    def ready(self):
        import blog.signals  # noqa: F401

Django Channels - WebSockety w czasie rzeczywistym#

Django Channels rozszerza Django o obsługę protokołów asynchronicznych, w tym WebSocketów. Jest idealny do czatów, powiadomień na żywo i aktualizacji w czasie rzeczywistym.

pip install channels channels-redis
# settings.py
INSTALLED_APPS = [
    "daphne",
    "channels",
    # ... inne aplikacje
]

ASGI_APPLICATION = "myproject.asgi.application"

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}


# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = f"chat_{self.room_name}"

        await self.channel_layer.group_add(
            self.room_group_name, self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name, self.channel_name
        )

    async def receive(self, text_data):
        data = json.loads(text_data)
        message = data["message"]
        username = self.scope["user"].username

        await self.channel_layer.group_send(
            self.room_group_name,
            {
                "type": "chat_message",
                "message": message,
                "username": username,
            },
        )

    async def chat_message(self, event):
        await self.send(text_data=json.dumps({
            "message": event["message"],
            "username": event["username"],
        }))


# chat/routing.py
from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

Celery - zadania asynchroniczne#

Celery to narzędzie do obsługi zadań asynchronicznych i harmonogramowania. W połączeniu z Django pozwala na wykonywanie czasochłonnych operacji w tle.

pip install celery redis
# myproject/celery.py
import os
from celery import Celery

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")

app = Celery("myproject")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()


# settings.py
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_TIMEZONE = "Europe/Warsaw"


# blog/tasks.py
from celery import shared_task
from django.core.mail import send_mass_mail
from django.template.loader import render_to_string


@shared_task(bind=True, max_retries=3)
def send_newsletter(self, post_id):
    try:
        from .models import Post
        from django.contrib.auth.models import User

        post = Post.objects.get(id=post_id)
        subscribers = User.objects.filter(
            profile__newsletter=True
        ).values_list("email", flat=True)

        messages = []
        for email in subscribers:
            subject = f"Nowy post: {post.title}"
            body = render_to_string("blog/email/newsletter.txt", {"post": post})
            messages.append((subject, body, "noreply@example.com", [email]))

        send_mass_mail(messages, fail_silently=False)
    except Exception as exc:
        self.retry(exc=exc, countdown=60 * (self.request.retries + 1))


@shared_task
def generate_sitemap():
    from .models import Post
    posts = Post.objects.filter(status=Post.Status.PUBLISHED)
    # Logika generowania sitemapy
    return f"Wygenerowano sitemapę z {posts.count()} postami"

Testowanie z pytest-django#

Django doskonale integruje się z pytest dzięki bibliotece pytest-django, oferując fixtures, klienta testowego i narzędzia do testowania baz danych.

pip install pytest pytest-django factory-boy
# conftest.py
import pytest
from django.contrib.auth.models import User


@pytest.fixture
def user(db):
    return User.objects.create_user(
        username="testuser",
        email="test@example.com",
        password="testpass123",
    )


@pytest.fixture
def api_client():
    from rest_framework.test import APIClient
    return APIClient()


@pytest.fixture
def authenticated_client(api_client, user):
    api_client.force_authenticate(user=user)
    return api_client


# blog/tests/factories.py
import factory
from blog.models import Post, Category, Tag


class CategoryFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Category

    name = factory.Sequence(lambda n: f"Category {n}")
    slug = factory.LazyAttribute(lambda o: o.name.lower().replace(" ", "-"))


class PostFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Post

    title = factory.Faker("sentence", nb_words=5)
    slug = factory.LazyAttribute(lambda o: o.title.lower().replace(" ", "-")[:200])
    author = factory.SubFactory("conftest.UserFactory")
    category = factory.SubFactory(CategoryFactory)
    body = factory.Faker("paragraphs", nb=3)
    status = Post.Status.PUBLISHED


# blog/tests/test_api.py
import pytest
from django.urls import reverse
from .factories import PostFactory, CategoryFactory


@pytest.mark.django_db
class TestPostAPI:
    def test_list_published_posts(self, api_client):
        PostFactory.create_batch(5, status="published")
        PostFactory.create_batch(3, status="draft")

        response = api_client.get(reverse("post-list"))
        assert response.status_code == 200
        assert len(response.data["results"]) == 5

    def test_create_post_authenticated(self, authenticated_client):
        category = CategoryFactory()
        data = {
            "title": "Nowy post testowy",
            "body": "Treść testowego posta",
            "category": category.id,
            "status": "draft",
        }
        response = authenticated_client.post(reverse("post-list"), data)
        assert response.status_code == 201
        assert response.data["title"] == "Nowy post testowy"

    def test_create_post_unauthenticated(self, api_client):
        response = api_client.post(reverse("post-list"), {"title": "Test"})
        assert response.status_code == 403

    def test_search_posts(self, api_client):
        PostFactory(title="Python Django Tutorial", status="published")
        PostFactory(title="JavaScript React Guide", status="published")

        response = api_client.get(reverse("post-list"), {"search": "Python"})
        assert response.status_code == 200
        assert len(response.data["results"]) == 1

Optymalizacja wydajności#

Django oferuje wiele narzędzi do optymalizacji wydajności aplikacji. Kluczowe techniki to cachowanie i optymalizacja zapytań do bazy danych.

Cachowanie#

# settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "db": "1",
        },
    }
}

# blog/views.py
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator


@method_decorator(cache_page(60 * 15), name="dispatch")  # Cache na 15 minut
class PostListView(ListView):
    model = Post
    paginate_by = 10

    def get_queryset(self):
        return Post.objects.filter(
            status=Post.Status.PUBLISHED
        ).select_related("author", "category").prefetch_related("tags")


# Cachowanie fragmentów w szablonach
# {% load cache %}
# {% cache 300 post_sidebar %}
#   ... kosztowna operacja ...
# {% endcache %}


# Cachowanie na poziomie zapytań
from django.core.cache import cache

def get_popular_posts():
    cache_key = "popular_posts"
    posts = cache.get(cache_key)
    if posts is None:
        posts = list(
            Post.objects.filter(status=Post.Status.PUBLISHED)
            .annotate(comment_count=Count("comments"))
            .order_by("-comment_count")[:10]
        )
        cache.set(cache_key, posts, timeout=60 * 30)
    return posts

Optymalizacja bazy danych#

# Unikanie problemu N+1 za pomocą select_related i prefetch_related
posts = Post.objects.select_related(
    "author", "category"
).prefetch_related(
    "tags", "comments"
).filter(status="published")

# Pobieranie tylko potrzebnych pól
titles = Post.objects.values_list("title", "slug", named=True)

# Bulk operations
Post.objects.filter(status="draft", publish_date__lt=threshold).update(
    status="archived"
)

# Indeksy bazy danych w modelach
class Post(models.Model):
    # ...
    class Meta:
        indexes = [
            models.Index(fields=["-publish_date"]),
            models.Index(fields=["status", "publish_date"]),
            models.Index(fields=["slug"]),
        ]

Django vs Flask - porównanie#

| Cecha | Django | Flask | |-------|--------|-------| | Filozofia | Batteries included | Mikroframework | | ORM | Wbudowany | Brak (SQLAlchemy opcjonalnie) | | Panel admina | Wbudowany | Brak (Flask-Admin opcjonalnie) | | Formularze | Wbudowane | WTForms opcjonalnie | | Uwierzytelnianie | Wbudowane | Flask-Login opcjonalnie | | Rozmiar projektu | Duże i średnie | Małe i średnie | | Krzywa uczenia | Średnia | Niska | | Elastyczność | Konwencja nad konfiguracją | Pełna elastyczność | | REST API | DRF (rozbudowany) | Flask-RESTful | | Async | Django Channels | Quart / Flask-SocketIO |

Kiedy wybrać Django?

  • Budowanie dużych, złożonych aplikacji z wieloma funkcjami
  • Potrzeba panelu administracyjnego
  • Projekty wymagające szybkiego prototypowania
  • Aplikacje z rozbudowanym modelem danych

Kiedy wybrać Flask?

  • Proste API i mikroserwisy
  • Projekty wymagające pełnej kontroli nad architekturą
  • Lekkkie aplikacje z minimalną liczbą zależności

Podsumowanie#

Django to dojrzały, wszechstronny framework, który znacząco przyspiesza tworzenie aplikacji webowych w Pythonie. Dzięki filozofii "batteries included", rozbudowanemu ORM, automatycznemu panelowi admina i potężnemu ekosystemowi bibliotek takich jak DRF, Celery czy Channels, Django jest idealnym wyborem dla projektów każdej skali.

Kluczowe zalety Django to bezpieczeństwo, wydajność, doskonała dokumentacja i ogromna społeczność. Framework ten jest szczególnie silny w tworzeniu aplikacji z rozbudowanym modelem danych, REST API i systemami zarządzania treścią.


Potrzebujesz profesjonalnej aplikacji webowej opartej na Django? W MDS Software Solutions Group specjalizujemy się w tworzeniu wydajnych, skalowalnych rozwiązań backendowych z wykorzystaniem Django, Django REST Framework i nowoczesnego stosu technologicznego. Od projektowania architektury, przez implementację API, po wdrożenie produkcyjne - nasz zespół doświadczonych programistów pomoże Ci zrealizować każdy projekt. Skontaktuj się z nami, aby omówić Twoje potrzeby i otrzymać bezpłatną wycenę!

Autor
MDS Software Solutions Group

Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.

Django - Szybkie Tworzenie Aplikacji Webowych w Pythonie | MDS Software Solutions Group | MDS Software Solutions Group