PrepBro
Профессии
PrepBro
Профессия:

Подготовка

  • Вопросы3841
  • Задачи106

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Профессия:

Подготовка

  • Вопросы3841
  • Задачи106

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Все 24 профессии
Android DeveloperData AnalystSystem Analyst1С DeveloperiOS DeveloperBusiness AnalystJava DeveloperData ScientistQA EngineerQA AutomationPHP BackendC/C++ BackendDevOps EngineerIT Project ManagerFrontend DeveloperNode.js BackendUnity DeveloperC# BackendProduct AnalystFlutter DeveloperPython DeveloperIT Product ManagerGo DeveloperData Engineer

© 2026 PrepBro. Все права защищены.

Telegram-бот

Вопросы по Python Developer

Является ли запрос по шаблону idempotent?
2.0 Middle🔥 30💬 1

Идемпотентность запросов (Idempotency)

Идемпотентный запрос — это запрос, который может быть выполнен несколько раз подряд с одинаковым результатом, без изменения состояния сервера или получения разных ответов. Это критически важное понятие для проектирования надёжных API и обработки сетевых ошибок.

Определение идемпотентности

Запрос идемпотентен, если:

  1. Первое выполнение даёт результат X
  2. Второе выполнение даёт результат X
  3. N-е выполнение даёт результат X
  4. Состояние сервера остаётся неизменным

HTTP методы и идемпотентность

Идемпотентные методы:

# GET — только читает данные, ничего не меняет
GET /users/123
GET /users/123  # Второй раз вернёт то же самое

# PUT — заменяет ресурс полностью (предсказуемый результат)
PUT /users/123
{
    "name": "John",
    "email": "john@example.com"
}
# Выполнено 1 раз или 5 раз — результат одинаковый
Читать полностью ->
Что такое шаблон Builder?
3.0 Senior🔥 30💬 1

Паттерн Builder (Строитель)

Builder — это порождающий паттерн проектирования, который используется для пошагового создания сложных объектов. Он отделяет процесс конструирования объекта от его представления, позволяя создавать различные варианты объекта.

Основная идея

Вместо передачи множества параметров в конструктор, мы создаём промежуточный объект (Builder), который накапливает значения параметров и затем создаёт целевой объект с помощью метода build().

Когда использовать Builder

  • Много параметров в конструкторе (более 3-4)
  • Много опциональных параметров
  • Разные комбинации параметров нужны в разных местах
  • Создание иммутабельных объектов
  • Пошаговое конструирование объекта

Пример с классом и Builder

Читать полностью ->
Что такое аутентификация?
2.0 Middle🔥 30💬 1

Аутентификация (Authentication)

Аутентификация — это процесс проверки подлинности пользователя. Система убеждается, что пользователь действительно тот, за кого себя выдаёт. Ответ на вопрос: "Ты кто?"

Аутентификация vs Авторизация

Эти два термина часто путают:

  • Аутентификация — кто ты? (Identity: "я Alice")
  • Авторизация — что ты можешь делать? (Permissions: "Alice может редактировать посты")
# Пример: проверка доступа
1. Аутентификация → login("alice", "password123") → Хорошо! Ты Alice
2. Авторизация → can_edit_post(alice, post_id) → Да, Alice может редактировать

Методы аутентификации

1. Basic Authentication (логин + пароль)

# Самый простой метод
from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import hashlib

app = FastAPI()
security = HTTPBasic()
Читать полностью ->
Что такое S3 бакеты и как они используются?
2.4 Senior🔥 30💬 1

S3 бакеты: облачное хранилище объектов

S3 (Simple Storage Service) — это облачный сервис хранения объектов от Amazon Web Services (AWS). S3 бакеты — это контейнеры (хранилища) для организации и управления файлами в облаке.

Основные характеристики S3

  1. Масштабируемость — неограниченное количество файлов
  2. Надёжность — 99.99% доступности и 11 девяток долговечности
  3. Стоимость — платишь только за использованное хранилище
  4. Управление версиями — ведение истории изменений файлов
  5. Контроль доступа — гибкая система прав и ACL
  6. CDN интеграция — распределение контента через CloudFront

Структура и терминология

Бакет — это как папка верхнего уровня:

 mybucket/
  ├── images/
  │   ├── photo1.jpg
  │   └── photo2.jpg
  ├── documents/
  │   ├── report.pdf
  │   └── whitepaper.docx
  └── videos/
      └── intro.mp4
Читать полностью ->
Что такое CQRS?
2.7 Senior🔥 30💬 1

CQRS (Command Query Responsibility Segregation)

CQRS — это архитектурный паттерн, который разделяет операции на две категории: команды (записи) и запросы (чтение). Основная идея заключается в том, что модели для создания и обновления данных отличаются от моделей для чтения данных.

Основные принципы

Традиционный подход использует одну модель для всех операций. CQRS же предлагает:

  • Command (Команда) — операция, которая изменяет состояние системы (CREATE, UPDATE, DELETE)
  • Query (Запрос) — операция, которая только читает данные без побочных эффектов (SELECT)
  • Разделение моделей — команды и запросы могут иметь разные структуры данных

Пример реализации на Python

from dataclasses import dataclass
from typing import List
from datetime import datetime

# Команды (write model)
@dataclass
class CreateUserCommand:
    username: str
    email: str
    password: str

@dataclass
class UpdateUserCommand:
    user_id: int
    email: str
Читать полностью ->
Что происходит внутри Python при создании класса?
1.2 Junior🔥 30💬 1

Что происходит внутри Python при создании класса

Этот вопрос о метаклассах, дескрипторах и подробном разборе процесса. Это средний-продвинутый уровень, но важно для глубокого понимания Python.

1. Основной процесс: от синтаксиса к объекту

# Когда мы пишем:
class User:
    count = 0  # Class attribute
    
    def __init__(self, name):
        self.name = name  # Instance attribute
        User.count += 1
    
    def greet(self):
        return f"Hello, {self.name}"

# Внутри Python происходит ЭТО:

2. Фаза 1: Парсинг и выполнение класс-блока

# Синтаксис class создаёт НОВЫЙ scope (пространство имён)
Читать полностью ->
Что означает сильная и слабая типизация в языке программирования?
1.3 Junior🔥 30💬 1

Сильная и слабая типизация в языках программирования

Это фундаментальное понятие, которое часто путают с динамической и статической типизацией. Объясню разницу и покажу примеры на Python.

Определения

Сильная типизация — язык не позволяет неявные преобразования типов между несовместимыми типами. Операции между разными типами либо запрещены, либо требуют явного преобразования.

Слабая типизация — язык автоматически преобразует типы при необходимости, скрывая несовместимость и часто приводя к неожиданному поведению.

Примеры в Python (сильная типизация)

# Python — СТРОГО типизированный язык

# ❌ ОШИБКА: нельзя складывать число и строку
result = 10 + "20"  # TypeError: unsupported operand type(s)

# ✅ ПРАВИЛЬНО: явное преобразование
result = 10 + int("20")  # 30

# ❌ ОШИБКА: нельзя вызвать метод строки на числе
x = 5
print(x.upper())  # AttributeError: 'int' object has no attribute 'upper'
Читать полностью ->
Что делает оператор DELETE?
3.0 Senior🔥 30💬 1

Оператор DELETE в Python

DELETE — это оператор в Python, который удаляет объект из памяти и делает имя переменной недоступным. Это важный инструмент для управления памятью.

Основное назначение DELETE

Оператор del удаляет ссылку на объект. Если это последняя ссылка, garbage collector удаляет сам объект из памяти.

# Создаём переменную
x = [1, 2, 3]
print(x)  # [1, 2, 3]

# Удаляем ссылку на объект
del x

# Теперь переменная не существует
print(x)  # NameError

Удаление элементов списка

list_items = ["apple", "banana", "cherry", "date"]

# Удалить элемент по индексу
del list_items[1]
print(list_items)  # ["apple", "cherry", "date"]

# Удалить срез
list_items = ["apple", "banana", "cherry", "date", "elderberry"]
del list_items[1:3]
print(list_items)  # ["apple", "date", "elderberry"]

# Удалить с шагом
list_items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
del list_items[::2]
print(list_items)  # [2, 4, 6, 8, 10]

Удаление из словаря

Читать полностью ->
Что будешь делать, если эндпоинты Django-сайта обрабатываются в течение минуты?
3.0 Senior🔥 30💬 1

Диагностика и оптимизация медленных Django endpoints

Ситуация, когда эндпоинты обрабатываются 1+ минуту, явно указывает на проблему. Я бы начал систематическую диагностику, используя проверенный подход.

Шаг 1: Определить узкое место

Использую Django Debug Toolbar для профилирования:

# settings.py для development
INSTALLED_APPS = [
    'debug_toolbar',
    # ... другие приложения
]

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    # ... другие middleware
]

if DEBUG:
    INTERNAL_IPS = ['127.0.0.1']
# urls.py
from django.conf import settings
if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [
        path('__debug__/', include(debug_toolbar.urls)),
    ] + urlpatterns

Debug Toolbar показывает:

  • Количество SQL запросов
  • Время выполнения каждого
  • Параметры запросов
  • Stack trace проблемных операций

Также использую django-silk для production:

Читать полностью ->
Чем можно заменить pass?
3.0 Senior🔥 30💬 1

Альтернативы pass в Python

pass — это no-op (пустая операция) в Python, которая требуется для синтаксически пустых блоков кода. Однако существует несколько способов его замены в зависимости от контекста.

Когда используется pass?

# Синтаксис требует тело блока, но логики нет
class MyClass:
    pass  # Пустой класс

def placeholder_function():
    pass  # Пустая функция

if condition:
    pass  # Пока логики нет

Альтернативы pass

1. Документация (docstring) вместо pass

Это самая частая и рекомендуемая альтернатива:

class BaseDatabase:
    """Абстрактный базовый класс для работы с БД"""

def not_implemented_yet():
    """Эта функция будет реализована позже"""

class MyException(Exception):
    """Кастомное исключение для нашего приложения"""

2. Исключение NotImplementedError для абстрактных методов

Это правильный способ для определения интерфейсов:

Читать полностью ->
Какой код у ошибки авторизации?
2.0 Middle🔥 30💬 1

HTTP Status Code для ошибки авторизации

Ошибка авторизации в HTTP протоколе обозначается кодом 401 Unauthorized. Это наиболее корректный код для использования, когда запрос не содержит необходимых учётных данных или они неверны.

Различие между 401 и 403

Важно понимать разницу между 401 и 403:

  • 401 Unauthorized — клиент не предоставил валидные учётные данные для авторизации. Если добавить правильный токен или пароль, запрос пройдёт. Браузер обычно показывает диалог аутентификации.
  • 403 Forbidden — клиент авторизован, но ему недостаточно прав для доступа к ресурсу. Исправить ситуацию может только администратор.

Примеры использования в коде

Вот как обрабатываются эти ошибки в FastAPI и других фреймворках:

from fastapi import FastAPI, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthCredentials

app = FastAPI()
security = HTTPBearer()
Читать полностью ->
Как сохранить зависимости библиотек в pip?
3.0 Senior🔥 30💬 1

Как сохранить зависимости библиотек в pip

Сохранение зависимостей проекта критически важно для воспроизводимости окружения. Это предотвращает ситуации, когда код работает у вас, но падает у коллеги или на сервере.

1. requirements.txt через pip freeze

# Сохранить все установленные пакеты в файл
pip freeze > requirements.txt

# Содержимое requirements.txt:
Django==4.2.0
celery==5.3.1
requests==2.31.0
psycopg2-binary==2.9.6

# Установить все зависимости
pip install -r requirements.txt

2. Ручное создание requirements.txt

# Вместо pip freeze можно написать вручную
echo 'Django==4.2.0
celery>=5.3
requests>=2.31
psycopg2-binary>=2.9' > requirements.txt

# Преимущества:
# - Контролируешь версии
# - Более гибкие версии (>=)
# - Явная информация о главных пакетах

3. Разделение по окружениям

requirements/
├── base.txt      # основные
├── dev.txt       # для разработки
└── prod.txt      # для production
Читать полностью ->
Как сделать конкатенацию двух листов?
1.0 Junior🔥 30💬 1

Конкатенация списков в Python

Есть несколько способов объединить два или более списков. Выбор зависит от контекста и производительности.

1. Оператор + (самый простой)

list1 = [1, 2, 3]
list2 = [4, 5, 6]

# Оператор + создаёт новый список
result = list1 + list2
print(result)  # [1, 2, 3, 4, 5, 6]

# Исходные списки не изменяются
print(list1)  # [1, 2, 3] — без изменений

2. Метод extend() (изменяет оригинал)

list1 = [1, 2, 3]
list2 = [4, 5, 6]

# extend() добавляет элементы list2 в list1
list1.extend(list2)
print(list1)  # [1, 2, 3, 4, 5, 6]
print(list2)  # [4, 5, 6] — не меняется

# extend() изменяет оригинальный список (in-place)
# и не возвращает результат
result = list1.extend(list2)  # Это вернёт None!
print(result)  # None

3. Распаковка (Unpacking)

list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]

# Способ 1: используя *
result = [*list1, *list2, *list3]
print(result)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Читать полностью ->
Как перехватить исключения в Python?
1.0 Junior🔥 30💬 1

Обработка исключений в Python

Обработка исключений (exception handling) — это критический инструмент для написания надежного и устойчивого к ошибкам кода. Python предоставляет мощный механизм try-except-finally.

1. Базовый синтаксис try-except

# Базовая структура
try:
    # Код, который может вызвать исключение
    result = 10 / 0
except ZeroDivisionError:
    # Обработка конкретного исключения
    print("Нельзя делить на ноль!")

# Результат: печатает сообщение об ошибке вместо краша

2. Перехват нескольких исключений

try:
    value = int("abc")  # ValueError
except ValueError:
    print("Ошибка: неверное значение")
except TypeError:
    print("Ошибка: неверный тип")
except Exception as e:
    print(f"Неожиданная ошибка: {e}")

Порядок важен — специфичные исключения должны идти первыми.

3. Несколько исключений в одном блоке

try:
    value = int("abc")
except (ValueError, TypeError) as e:
    print(f"Ошибка: {e}")
Читать полностью ->
Зачем нужна функция property() в Python?
2.0 Middle🔥 30💬 1

Функция property() в Python

property() — это встроенная функция, которая позволяет создавать управляемые атрибуты класса. Она превращает методы в свойства, позволяя вам вызывать методы как обычные атрибуты, но с дополнительной логикой валидации, преобразования или ограничения доступа.

Проблема без property

# ❌ Плохо: прямой доступ к атрибутам

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person('Alice', 30)
person.age = -5  # Ничто не помешает!
print(person.age)  # -5 — невалидное значение

# Проблемы:
# - Нет валидации
# - Нет логики при установке значения
# - Если позже добавим логику, сломаем существующий код

Решение 1: Методы (старый способ)

Читать полностью ->
Зачем нужен метод save?
1.0 Junior🔥 30💬 1

Зачем нужен метод save?

Вопрос зависит от контекста. В Python есть несколько интерпретаций метода save().

1. В ORM (SQLAlchemy, Django ORM)

# Django ORM
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()

user = User(name="John", email="john@example.com")
user.save()  # Сохраняет в БД

# SQLAlchemy
from sqlalchemy.orm import Session

session = Session()
user = User(name="John", email="john@example.com")
session.add(user)
session.commit()  # Эквивалент save()

2. В модельных классах (Machine Learning)

from sklearn.externals import joblib

model = train_model(data)
model.save('model.pkl')  # Сохраняет модель на диск

# Загрузка
loaded_model = joblib.load('model.pkl')

3. В файловых операциях

import json

data = {"key": "value"}
with open('data.json', 'w') as f:
    json.dump(data, f)  # Сохраняет JSON

Итог

Читать полностью ->
С чем работал
1.3 Junior🔥 30💬 1

Описание поведения объекта в Java

Основные способы

Поведение объекта описывается несколькими способами:

1. Interface (Интерфейс)

Самый явный способ — интерфейс определяет контракт (какие методы должны быть и что они делают):

public interface PaymentProcessor {
    /**
     * Обрабатывает платёж
     * @param amount сумма платежа
     * @return результат обработки
     */
    boolean processPayment(double amount);
    
    /**
     * Проверяет статус платежа
     */
    PaymentStatus getStatus();
}

2. Abstract Class (Абстрактный класс)

Описывает поведение + состояние через методы и поля:

public abstract class Animal {
    private String name;
    
    // Конкретное поведение (реализовано)
    public void sleep() {
        System.out.println(name + " спит");
    }
    
    // Абстрактное поведение (подклассы должны реализовать)
    public abstract void makeSound();
}

3. Javadoc (Документация)

Читать полностью ->
Для чего нужен первичный ключ (Primary Key)?
1.0 Junior🔥 30💬 1

Первичный ключ (Primary Key)

Первичный ключ — это одно или несколько полей в таблице, которые однозначно идентифицируют каждую строку. Это фундаментальное понятие в реляционных БД.

Основные функции Primary Key

  1. Уникальность — каждая строка имеет уникальное значение
  2. Идентификация — по PK можно быстро найти конкретную строку
  3. Связь между таблицами — Foreign Key ссылаются на Primary Key
  4. Целостность данных — предотвращает дубликаты

Синтаксис в SQL

-- Single column Primary Key
CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(100),
    name VARCHAR(100)
);

-- Или как constraint
CREATE TABLE users (
    id INT,
    email VARCHAR(100),
    PRIMARY KEY (id)
);

-- Composite Primary Key (несколько полей)
CREATE TABLE user_roles (
    user_id INT,
    role_id INT,
    assigned_date DATE,
    PRIMARY KEY (user_id, role_id)  -- Уникальная комбинация
);

В SQLAlchemy (Python ORM)

Читать полностью ->
Что такое хеширование?
1.6 Junior🔥 29💬 1

Хеширование (Hashing)

Хеширование — это процесс преобразования данных произвольного размера в фиксированный размер строки символов (хеш-сумма или digest) с помощью специальной функции называемой хеш-функцией. Основное назначение хеширования — быстро проверять целостность данных, создавать индексы для быстрого поиска и криптографически защищать информацию. Хеширование является одним из ключевых технологий в информационной безопасности и информатике в целом.

Основные свойства хеш-функций

Качественная хеш-функция должна обладать следующими свойствами:

Читать полностью ->
Что такое паттерн наблюдатель?
2.0 Middle🔥 29💬 1

Паттерн Наблюдатель (Observer Pattern)

Observer Pattern (Наблюдатель) — это поведенческий паттерн проектирования, который создаёт механизм подписки на события. Когда состояние объекта изменяется, все заинтересованные объекты (наблюдатели) автоматически уведомляются об этом. Паттерн реализует loose coupling — объекты слабо связаны между собой.

Основная архитектура

Subject (издатель): объект, который отслеживает и публикует события Observer (наблюдатель): объект, который слушает события и реагирует на них

Простой пример на чистом Python

from abc import ABC, abstractmethod
from typing import List

# Observer Interface
class Observer(ABC):
    """Интерфейс для наблюдателей"""
    
    @abstractmethod
    def update(self, subject):
        """Метод, вызываемый при изменении subject"""
        pass
Читать полностью ->
Что такое изоляция в ACID?
1.2 Junior🔥 29💬 1

Изоляция в ACID

Изоляция (Isolation) — это четвёртый принцип ACID (Atomicity, Consistency, Isolation, Durability), который гарантирует, что одновременно выполняемые транзакции не влияют друг на друга и не видят незавершённые изменения друг друга.

Что такое ACID

ACID — это набор свойств, которые гарантируют надёжность транзакций в базе данных:

  • Atomicity (Атомарность) — транзакция либо полностью выполняется, либо полностью откатывается
  • Consistency (Консистентность) — база данных переходит из одного согласованного состояния в другое
  • Isolation (Изоляция) — одновременные транзакции не влияют друг на друга
  • Durability (Долговечность) — завершённая транзакция остаётся в базе даже при сбое

Проблема конкурентности без изоляции

Без правильной изоляции возникают следующие проблемы:

Dirty Read (грязное чтение):

Читать полностью ->
Что такое snapshot в PostgreSQL?
3.0 Senior🔥 29💬 1

Snapshot в PostgreSQL

Snapshot (снимок) в PostgreSQL — это снимок состояния базы данных на конкретный момент времени, который определяет, какие строки видны для конкретной транзакции. Snapshot обеспечивает изоляцию транзакций, позволяя разным транзакциям видеть разные версии одних и тех же данных в зависимости от уровня изоляции.

Проблема многовверсионности

Постгре использует MVCC (Multi-Version Concurrency Control) — каждая транзакция видит свою версию данных:

-- Сессия 1:
BEGIN;
UPDATE users SET balance = 100 WHERE id = 1;
-- Баланс изменяется, но COMMIT ещё не выполнен

-- Сессия 2 (одновременно):
BEGIN;
SELECT balance FROM users WHERE id = 1;  -- Видит старое значение!
-- Из-за SNAPSHOT: видит состояние БД на момент начала транзакции

Что такое Snapshot

Snapshot содержит информацию о:

Читать полностью ->
Что такое Load Average в Linux?
2.0 Middle🔥 29💬 1

Load Average в Linux: нагрузка на систему

Load Average — это метрика, которая показывает среднее количество процессов, находящихся в очереди на выполнение (или активно выполняющихся) на протяжении определённого времени. Это одна из самых важных метрик для мониторинга производительности серверов.

Где увидеть Load Average

# Команда uptime
$ uptime
 14:30:42 up 10 days,  2:15,  3 users,  load average: 0.52, 0.48, 0.45

# Команда top
$ top
top - 14:30:45 up 10 days,  2:15,  3 users,  load average: 0.52, 0.48, 0.45

# Файл /proc/loadavg
$ cat /proc/loadavg
0.52 0.48 0.45 1/342 12345

Три значения Load Average

Load average всегда показывает три числа:

0.52  0.48  0.45
  |     |     |
  1min  5min  15min
  • Первое число (1 минута) — средняя нагрузка за последнюю минуту
  • Второе число (5 минут) — средняя нагрузка за последние 5 минут
  • Третье число (15 минут) — средняя нагрузка за последние 15 минут

Что означают эти цифры

Читать полностью ->
Что будет если не использовать await в асинхронной функции?
1.8 Middle🔥 29💬 1

Что будет если не использовать await в асинхронной функции?

Это критичный баг, который приводит к проблемам с race conditions и потере данных. Давайте разберёмся.

Основное правило

Если функция асинхронная — её результат нужно await-ить!

async def fetch_user(user_id: int):
    # Имитируем задержку БД (1 сек)
    await asyncio.sleep(1)
    return {"id": user_id, "name": "John"}

async def main():
    # НЕПРАВИЛЬНО: забыли await
    result = fetch_user(1)  # Возвращает Coroutine объект
    print(result)  # <coroutine object fetch_user at 0x7f8b8c8c5f40>
    print(type(result))  # <class coroutine>

# ПРАВИЛЬНО: используем await
    result = await fetch_user(1)
    print(result)  # {"id": 1, "name": "John"}

Что происходит без await

При отсутствии await функция не выполняется. Вместо этого возвращается Coroutine объект — это как повисшая задача.

import asyncio
from datetime import datetime
Читать полностью ->
Чем может помочь DNS маршрутизация?
3.0 Senior🔥 29💬 1

DNS маршрутизация: Практическое применение

DNS маршрутизация (DNS routing) — это техника использования DNS для распределения трафика между серверами. Это мощный инструмент для масштабирования и отказоустойчивости.

Как работает DNS

Клиент запрашивает: api.example.com
     ↓
DNS resolver проверяет:
  1. Локальный кэш браузера
  2. Кэш ISP
  3. Корневой DNS сервер
  4. Авторитетный DNS сервер (example.com)
     ↓
DNS сервер возвращает IP адрес(а)
     ↓
Клиент подключается к IP адресу

В этом процессе DNS может сделать МНОГО полезного!

Типы DNS маршрутизации

1. Round-Robin (циклическое распределение)

# В DNS записи указываем несколько A records
api.example.com  IN  A  192.168.1.10
api.example.com  IN  A  192.168.1.20
api.example.com  IN  A  192.168.1.30

# Клиент 1 получает IP #1 → 192.168.1.10
# Клиент 2 получает IP #2 → 192.168.1.20
# Клиент 3 получает IP #3 → 192.168.1.30
# Клиент 4 получает IP #1 (круг замкнулся) → 192.168.1.10
Читать полностью ->
Хранится ли в стандарте REST состояние клиента
2.0 Middle🔥 29💬 1

Хранение состояния клиента в REST

Принцип Statelessness

Нет, REST не хранит состояние клиента на сервере. Это одна из фундаментальных характеристик REST архитектуры, определённая Roy Fielding в его диссертации. Принцип называется Statelessness (отсутствие состояния).

Что это значит

Statelessness означает, что:

  1. Сервер не помнит информацию о клиенте между запросами
  2. Каждый запрос содержит всю необходимую информацию для его обработки
  3. Сессии хранятся на клиенте, а не на сервере

Почему это важно

Масштабируемость:

# ❌ Stateful подход (плохо)
# Сервер 1 запомнил: user_id=123 logged_in=True
# Если запрос пойдёт на Сервер 2, он не знает о клиенте

# ✅ Stateless подход (хорошо)
# Клиент отправляет: Authorization: Bearer token_xyz
# Любой сервер может обработать запрос

При stateless можно использовать любой сервер из пула — не нужна привязка к одному серверу.

Надёжность:

Читать полностью ->
Сколько потоков и процессов используется в asyncio?
3.0 Senior🔥 29💬 1

Количество потоков и процессов в asyncio

Ответ: Ни один процесс, один поток

asyncio использует один поток выполнения и ни одного дополнительного процесса по умолчанию. Это критически важно понимать для правильного использования асинхронного программирования в Python.

Как это работает

asyncio работает на базе event loop — встроенного механизма планирования задач, который работает исключительно в одном потоке:

import asyncio

async def task1():
    print("Задача 1 начало")
    await asyncio.sleep(1)
    print("Задача 1 конец")

async def task2():
    print("Задача 2 начало")
    await asyncio.sleep(1)
    print("Задача 2 конец")

async def main():
    # Обе задачи работают в ОДНОМ потоке
    await asyncio.gather(task1(), task2())

asyncio.run(main())

Вывод:

Задача 1 начало
Задача 2 начало
Задача 1 конец
Задача 2 конец

Обе задачи выполняются в одном потоке, а не параллельно!

Почему это однопоточно?

Читать полностью ->
На чем делаешь валидацию входных данных в DRF
2.0 Middle🔥 29💬 1

Валидация входных данных в Django Rest Framework

DRF предоставляет мощный встроенный механизм валидации данных. Валидация — это критически важный процесс для обеспечения целостности и безопасности приложения.

1. Валидация на уровне полей (Field-level validation)

Это первый уровень валидации, который срабатывает автоматически для каждого поля в сериализаторе.

from rest_framework import serializers

class UserSerializer(serializers.Serializer):
    username = serializers.CharField(
        max_length=150,
        min_length=3,
        required=True
    )
    email = serializers.EmailField()
    age = serializers.IntegerField(
        min_value=0,
        max_value=150
    )
    phone = serializers.CharField(
        max_length=20,
        allow_blank=False
    )

DRF автоматически проверит формат email, диапазоны для целых чисел и строковые ограничения.

2. Методы-валидаторы на уровне полей (validate_field_name)

Читать полностью ->
Какой функцией создается движок SQLAlchemy?
2.0 Middle🔥 29💬 1

Функция create_engine() в SQLAlchemy

Движок SQLAlchemy создаётся функцией create_engine() из модуля sqlalchemy. Это основной компонент для работы с базами данных.

Базовое использование

from sqlalchemy import create_engine

# PostgreSQL
engine = create_engine("postgresql://user:password@localhost:5432/mydb")

# SQLite
engine = create_engine("sqlite:///database.db")

# MySQL
engine = create_engine("mysql+pymysql://user:password@localhost:3306/mydb")

Структура URL подключения

dialect+driver://username:password@host:port/database
  • dialect — тип БД (postgresql, mysql, sqlite и т.д.)
  • driver — драйвер для работы (psycopg2, pymysql, cx_Oracle)
  • username:password — учётные данные
  • host:port — адрес и порт сервера
  • database — имя БД

Параметры create_engine()

from sqlalchemy import create_engine
Читать полностью ->
Какой принцип действия у алгоритма двоичного поиска?
1.0 Junior🔥 29💬 1

Принцип действия алгоритма двоичного поиска

Двоичный поиск (binary search) — это фундаментальный алгоритм поиска элемента в отсортированном массиве за логарифмическое время. Это один из самых эффективных алгоритмов в информатике.

Основной принцип

Алгоритм работает по принципу "разделяй и властвуй" — на каждом шаге он делит диапазон поиска пополам и определяет, в какой половине находится искомый элемент.

Пошаговое описание

Шаг 1: Определение границ

left = 0           # Левая граница
right = len(arr) - 1  # Правая граница

Шаг 2: Вычисление середины

mid = (left + right) // 2  # Индекс середины

Шаг 3: Сравнение

if arr[mid] == target:
    return mid  # Элемент найден
elif arr[mid] < target:
    left = mid + 1  # Ищем в правой половине
else:
    right = mid - 1  # Ищем в левой половине

Шаг 4: Повторение

Процесс повторяется, пока не найдем элемент или диапазон не исчерпается.

Полная реализация

Читать полностью ->
Какие методы в http передают тело запроса?
1.3 Junior🔥 29💬 1

Какие методы HTTP передают тело запроса

В HTTP тело запроса (request body) может передаваться различными методами, но не все методы предназначены для этого. Разберу каждый.

1. POST

Основной метод для отправки данных.

Назначение: создание нового ресурса на сервере.

import requests

# JSON body
response = requests.post(
    "https://api.example.com/users",
    json={"name": "John", "email": "john@example.com"}
)

# Эквивалентно
response = requests.post(
    "https://api.example.com/users",
    headers={"Content-Type": "application/json"},
    data='{"name": "John", "email": "john@example.com"}'
)

# Form data
response = requests.post(
    "https://api.example.com/login",
    data={"username": "john", "password": "secret"}
)

# Файл
with open('file.pdf', 'rb') as f:
    response = requests.post(
        "https://api.example.com/upload",
        files={"file": f}
    )

Идемпотентность: ❌ НЕ идемпотентный (несколько вызовов = несколько ресурсов).

Читать полностью ->
Какую систему контролей версий используешь?
1.2 Junior🔥 29💬 1

Система контроля версий: Git

Мой основной выбор — Git, универсальная система распределённого контроля версий. Её использую для всех проектов: от личных экспериментов до enterprise-приложений.

Почему Git?

Распределённость — каждый разработчик имеет полную историю репозитория локально. Это означает, что можно работать автономно, без постоянного подключения к центральному серверу.

Производительность — все операции выполняются локально с минимальной задержкой:

  • Коммиты создаются за миллисекунды
  • Просмотр истории мгновенный
  • Переключение между ветками не требует сетевого доступа

Ветвление и слияние — система веток в Git настолько лёгкая, что позволяет создавать отдельные ветки для каждой задачи:

# Создание и переключение на новую ветку
$ git checkout -b feature/user-authentication
$ git checkout -b bugfix/memory-leak
$ git checkout -b docs/api-documentation
Читать полностью ->
Какие умеешь писать тесты?
1.0 Junior🔥 29💬 1

Какие тесты я умею писать

Тестирование — это искусство и наука. Я имею опыт написания различных видов тестов и понимаю когда и какой тип использовать. Давайте разберемся во всех видах тестов.

Пирамида тестов

           E2E (End-to-End)  - 5-10%
          /\
        /    \
      /        \      Integration Tests - 15-20%
    /            \
  /                \  Unit Tests - 70-80%

1. Unit Tests (Модульные тесты)

Цель: Тестировать отдельные функции/методы в изоляции

import pytest
from unittest.mock import Mock, patch
from myapp.services import UserService
from myapp.models import User
Читать полностью ->
Для чего используются параметры запроса (query-параметры) в Django, FastAPI и других фреймворках?
1.2 Junior🔥 29💬 1

Query-параметры в веб-фреймворках

Query-параметры (параметры строки запроса) — это переменные данные, передаваемые в URL адресе после символа ?. Они используются для фильтрации, сортировки и настройки поведения запроса.

Назначение query-параметров

Фильтрация данных:

  • GET /api/users?status=active — получить только активных пользователей
  • GET /api/posts?author_id=123 — посты конкретного автора
  • GET /api/products?min_price=100&max_price=500 — товары в диапазоне цен

Поиск:

  • GET /api/search?q=python&limit=10 — поиск по ключевому слову
  • GET /api/articles?tag=django — статьи с определённым тегом

Пагинация:

  • GET /api/posts?page=2&per_page=20 — вторая страница с 20 элементами
  • GET /api/comments?offset=100&limit=50 — с пропуском первых 100

Сортировка:

  • GET /api/products?sort=price&order=asc — сортировка по цене по возрастанию
  • GET /api/posts?sort=-created_at — новые посты первыми

Django: работа с query-параметрами

Читать полностью ->
Как добавляешь URL в Django?
1.3 Junior🔥 29💬 1

Как добавляешь URL в Django

Добавление URL в Django осуществляется через систему маршрутизации на основе файлов urls.py. Это мощный и гибкий механизм, который позволяет связать URL-пути с view функциями или классами.

Основные концепции

URL routing в Django работает через сопоставление URL-паттерна с view функцией или классом. Главный файл urls.py обычно находится в папке проекта (конфигурация Django), а отдельные приложения имеют свои файлы urls.py.

Структура проекта

project/
├── project/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py          ← Главный файл маршрутизации
│   └── wsgi.py
└── myapp/
    ├── migrations/
    ├── templates/
    ├── views.py
    ├── urls.py          ← URL для приложения
    └── models.py

1. Основной файл urls.py (проекта)

# project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
Читать полностью ->
В чем разница между локальной и глобальной переменной?
1.0 Junior🔥 29💬 1

Локальные vs Глобальные переменные

Это фундаментальная концепция scope (области видимости) в Python. Область видимости определяет, где можно использовать переменную.

Глобальная переменная

global_var = 100  # Доступна везде в модуле

def function1():
    print(global_var)  # 100 — может читать

def function2():
    print(global_var)  # 100 — может читать

function1()
function2()
print(global_var)  # 100 — доступна на уровне модуля

Характеристики глобальной переменной:

  • Объявлена на уровне модуля
  • Видна во всех функциях и классах модуля
  • Существует в течение всего выполнения программы
  • Изменение влияет на все, кто её использует

Локальная переменная

def function():
    local_var = 50  # Существует только внутри функции
    print(local_var)

function()  # 50
print(local_var)  # NameError: переменная не существует
Читать полностью ->
Почему хочешь сменить компанию?
1.0 Junior🔥 29💬 1

Почему хочешь сменить компанию?

Это хороший вопрос, который раскрывает не просто недовольство, а карьерные амбиции. Отвечу честно и конструктивно.

Текущая ситуация

В текущей компании я получил отличный опыт: работал с фреймворками на уровне продакшена (Django, FastAPI), оптимизировал критичные системы, участвовал в архитектурных решениях. Это был профессиональный рост.

Почему хочу двигаться дальше

1. Технологические вызовы

Большинство текущих проектов — поддержка legacy-кода. Хочу работать с современным стеком, где тестирование, CI/CD и документация — часть культуры с начала, не костыль в конце.

2. Масштаб и влияние

В текущей компании работаю над внутренними инструментами. Хочу создавать продукты, которые используют тысячи людей. Интересует опыт работы с системами, где производительность и надёжность — не опция.

Читать полностью ->
Что такое виртуальное окружение (virtual environment) и зачем оно нужно?
1.3 Junior🔥 29💬 1

Виртуальное окружение (Virtual Environment)

Виртуальное окружение (venv) — это изолированная копия Python интерпретатора с собственным набором установленных пакетов. Это позволяет разным проектам использовать разные версии одних и тех же библиотек без конфликтов.

Проблема без виртуального окружения

Все пакеты устанавливаются в глобальную директорию Python:

$ python -m pip install django==3.2
# Установлена глобально в /usr/local/lib/python3.11/site-packages/

$ python -m pip install django==4.2
# Перезаписана на новую версию

# Теперь старый проект, использующий Django 3.2, сломан!

Проблемы:

  • Конфликты версий между проектами
  • Невозможно работать над несколькими проектами параллельно
  • Сложно понять, какие пакеты нужны конкретному проекту
  • Рискованно обновлять пакеты (может сломать другие проекты)
  • На сервере сложно развертывать (неизвестны зависимости)

Решение: Виртуальное окружение

Каждый проект получает собственную копию Python и пакетов:

Читать полностью ->
Зачем нужен try except?
1.0 Junior🔥 29💬 1

Зачем нужен try except?

Try-except — это механизм обработки ошибок (исключений) в Python. Без него программа падает при любой ошибке.

Проблема без try-except

# БЕЗ try-except
def divide(a, b):
    return a / b

result = divide(10, 0)  # ZeroDivisionError: division by zero
print("Программа упала")  # Этот код НЕ выполнится

# Программа прерывается здесь!

Решение с try-except

# С try-except
def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        print("Нельзя делить на ноль!")
        return None

result = divide(10, 0)
print("Программа продолжает работу")  # Это выполнится!

Основные случаи использования

1. Обработка ошибок ввода

Читать полностью ->
Зачем нужна каждая структура данных?
1.0 Junior🔥 29💬 1

Зачем нужна каждая структура данных

Каждая структура данных оптимизирована для определённых операций и сценариев использования. Выбор правильной структуры напрямую влияет на производительность и читаемость кода.

List (Список)

Назначение: упорядоченная коллекция элементов с быстрым доступом по индексу.

# Быстрый доступ O(1)
arr = [1, 2, 3, 4, 5]
print(arr[2])  # 3

# Итерация O(n)
for item in arr:
    print(item)

# Медленный поиск O(n) и вставка O(n)
arr.insert(0, 0)  # вставка в начало — сдвиг всех элементов

Когда использовать: когда нужен произвольный доступ и итерация, данные меняются часто.

Tuple (Кортеж)

Назначение: неизменяемая последовательность, используется как ключ в словарях.

# Неизменяемость
coord = (10, 20)
coord[0] = 5  # TypeError

# Как ключ
locations = {
    (0, 0): "начало",
    (10, 20): "точка A",
    (5, 15): "точка B"
}

print(locations[(0, 0)])  # начало
Читать полностью ->
В чем разница между POST и PUT?
1.6 Junior🔥 29💬 1

Разница между POST и PUT

ПОСТ и PUT — это два разных HTTP метода, и хотя оба отправляют данные на сервер, они используются в совершенно разных сценариях.

POST — Создание нового ресурса

ПОСТ используется для отправки данных, которые изменяют состояние сервера. Обычно это создание нового ресурса.

import requests

# Создание новой записи
response = requests.post(
    "https://api.example.com/users",
    json={
        "name": "Ivan Petrov",
        "email": "ivan@example.com"
    }
)
# Response: 201 Created
# {
#   "id": 123,
#   "name": "Ivan Petrov",
#   "email": "ivan@example.com"
# }

Важные свойства POST:

  • Не идемпотентен: несколько одинаковых запросов создадут несколько ресурсов
  • Создает новый ресурс: каждый вызов добавляет что-то новое
  • Обычно возвращает 201 Created или 200 OK
  • URL обычно указывает коллекцию (/users), а не конкретный ресурс

PUT — Полное обновление существующего ресурса

Читать полностью ->
В чем разница между == и is?
1.0 Junior🔥 29💬 1

Разница между == и is

Это один из фундаментальных вопросов в Python, касающихся сравнения объектов. Оба оператора выполняют сравнение, но работают они совершенно по-разному.

Определение

Оператор == (равенство по значению)

== сравнивает значения объектов. Он проверяет, содержат ли объекты одинаковые данные.

a = [1, 2, 3]
b = [1, 2, 3]

print(a == b)  # True (значения одинаковые)
print(a is b)  # False (это разные объекты в памяти)

Оператор == вызывает магический метод __eq__ и зависит от его реализации в классе:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        if not isinstance(other, Person):
            return False
        return self.name == other.name and self.age == other.age

alice1 = Person('Alice', 30)
alice2 = Person('Alice', 30)
bob = Person('Bob', 25)
Читать полностью ->
Является ли использование Mixin правильным подходом наследования как концепции?
3.0 Senior🔥 28💬 1

Mixin как подход наследования

Mixin — это спорный, но полезный подход наследования в Python. Вопрос о его «правильности» зависит от контекста и того, как он используется.

Что такое Mixin?

Mixin — это класс, который предоставляет дополнительную функциональность, но не предназначен для самостоятельного использования. Он содержит методы и атрибуты, которые могут быть переиспользованы в других классах через множественное наследование.

Пример Mixin

class TimestampMixin:
    """Добавляет временные метки к объектам"""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
    
    def mark_updated(self):
        self.updated_at = datetime.now()

class JsonSerializableMixin:
    """Добавляет сериализацию в JSON"""
    def to_json(self):
        return json.dumps(self.__dict__, default=str)
Читать полностью ->
Что такое шифровальщик?
1.0 Junior🔥 28💬 1

Шифровальщик (Encryptor)

Шифровальщик — это механизм, алгоритм или программное средство, предназначенное для преобразования открытых (читаемых) данных в зашифрованный (нечитаемый без ключа) вид. В контексте Python и разработки, это может означать как криптографические функции для защиты информации, так и паттерны проектирования для преобразования данных.

Криптографическое шифрование

Криптографический шифровальщик использует математические алгоритмы для преобразования данных так, чтобы их можно было прочитать только с использованием специального ключа.

# Пример симметричного шифрования с использованием Fernet
from cryptography.fernet import Fernet

# Генерация ключа
key = Fernet.generate_key()
cipher = Fernet(key)

# Шифрование данных
plaintext = b"Secret message"
encrypted = cipher.encrypt(plaintext)
print(f"Зашифровано: {encrypted}")

# Расшифровка данных
decrypted = cipher.decrypt(encrypted)
print(f"Расшифровано: {decrypted.decode()}")
Читать полностью ->
Что такое изменяемый объект в Python?
2.7 Senior🔥 28💬 1

Что такое изменяемый объект в Python?

Изменяемый объект (Mutable Object) в Python — это объект, содержимое которого можно изменять после его создания, не создавая новый объект. Это фундаментальное понятие в Python, которое влияет на поведение переменных, функций и структур данных.

Определение и суть

Когда говорят об изменяемости, речь идёт о возможности модифицировать содержимое объекта в памяти. При изменении изменяемого объекта его идентификатор (id) остаётся прежним:

# Список (list) - изменяемый объект
my_list = [1, 2, 3]
original_id = id(my_list)

# Изменяем содержимое
my_list.append(4)
my_list[0] = 100

# id не изменился - это тот же объект
print(id(my_list) == original_id)  # True

print(my_list)  # [100, 2, 3, 4]

Изменяемые типы данных в Python

1. Список (list)

# Списки полностью изменяемы
my_list = [1, 2, 3]

# Изменение элемента
my_list[0] = 10

# Добавление элемента
my_list.append(4)

# Удаление элемента
my_list.pop(1)
Читать полностью ->
Что такое TCP и что такое IP?
1.2 Junior🔥 28💬 1

TCP и IP протоколы

TCP/IP — это набор протоколов, который позволяет компьютерам взаимодействовать в Интернете. IP отвечает за доставку пакетов между компьютерами, а TCP гарантирует, что данные доходят в правильном порядке и без потерь.

IP (Internet Protocol)

IP — это сетевой протокол, который отвечает за маршрутизацию пакетов данных от отправителя к получателю через сеть.

Основные характеристики:

  • Адресация — каждому компьютеру присвоен IP адрес (например, 192.168.1.1)
  • Маршрутизация — пакеты могут проходить через несколько сетевых узлов
  • Connectionless — не требует установления соединения
  • Ненадежный — не гарантирует доставку (пакеты могут теряться)

Версии IP:

IPv4: 192.168.1.1 (4 октета, 32 бита)
IPv6: 2001:0db8:85a3::8a2e:0370:7334 (128 бит, больше адресов)

Как работает IP:

Читать полностью ->
Что такое Global Interpreter Lock в Python?
2.0 Middle🔥 28💬 1

Что такое Global Interpreter Lock в Python

Global Interpreter Lock (GIL) — это мьютекс (взаимное исключение), который защищает доступ к объектам в CPython (стандартной реализации Python). GIL гарантирует, что только один поток может выполнять байт-код Python одновременно, даже на многоядерных системах.

Почему существует GIL

CPython управляет памятью через reference counting (подсчёт ссылок). Каждый объект имеет счётчик ссылок:

import sys

x = [1, 2, 3]
print(sys.getrefcount(x))  # Количество ссылок на объект

y = x  # Увеличивает счётчик
del x  # Уменьшает счётчик

# Когда счётчик = 0, объект удаляется из памяти

Если несколько потоков будут одновременно изменять счётчик, возникнет race condition. Вместо того чтобы использовать дорогой мьютекс для каждого объекта, CPython использует один глобальный мьютекс — GIL.

Последствия GIL

Многопоточность не даёт параллелизм для CPU-bound операций:

import threading
import time
Читать полностью ->
Что произойдет с другими корутинами, если корутина asyncio начнет читать большой файл через open('file').read()?
3.0 Senior🔥 28💬 1

asyncio блокировка при синхронном чтении файла

Это критическая проблема в asyncio приложениях. Синхронное чтение файла замораживает ВСЕ корутины.

Проблема: блокировка event loop

import asyncio

async def read_large_file():
    """❌ НЕПРАВИЛЬНО: блокирует весь event loop"""
    with open('huge_file.bin', 'rb') as f:
        data = f.read()  # БЛОКИРУЕТ! (может быть 1+ секунду)
    return len(data)

async def fast_operation():
    """Эта корутина ЗАВИСНЕТ, пока читаем файл"""
    print("Start")
    await asyncio.sleep(0.1)  # Хотим 0.1 сек
    print("Done")  # Но выведется ДО ТОГО как файл прочитается!

async def main():
    # Запускаем обе корутины параллельно
    results = await asyncio.gather(
        read_large_file(),
        fast_operation()
    )
    return results

# Время выполнения:
# read_large_file: 2 сек (читает файл)
# fast_operation: ЖДЕТ 2 сек (хотя нужно только 0.1)
# Итого: ~2 сек вместо параллельного выполнения

Что происходит в event loop

Читать полностью ->
Что нужно использовать, чтобы запустить проект в интернете на Python?
2.7 Senior🔥 28💬 1

Развертывание Python проекта в интернете

Это один из самых важных практических навыков. За 10+ лет работал с разными хостингами и подходами.

Стек технологий для production

Основные компоненты

Интернет
  ↓
HTTPS Load Balancer (nginx, AWS ALB)
  ↓
APP Server (Gunicorn, uWSGI, Uvicorn)
  ↓
Python приложение (Django, FastAPI)
  ↓
Database (PostgreSQL)
  ↓
Cache (Redis)

1. Веб-сервер приложения (WSGI/ASGI)

Gunicorn (для Django, Flask — WSGI)

# Установка
pip install gunicorn

# Запуск
gunicorn config.wsgi:application --workers 4 --threads 2 --bind 0.0.0.0:8000

# workers: количество process'ов (рекомендация: количество CPU cores)
# threads: потоки per worker
# bind: адрес и порт

# Systemd сервис

Файл /etc/systemd/system/myapp.service

[Unit]
Description=My Python App
After=network.target
Читать полностью ->
Что использовали для передачи статики клиенту на продакшн?
2.0 Middle🔥 28💬 1

Передача статики клиенту на продакшене

Вопрос о доставке статических файлов (CSS, JavaScript, изображений, шрифтов) — один из ключевых аспектов архитектуры веб-приложений. За 10+ лет я использовал разные подходы на разных этапах развития приложений.

Почему это важно

Проблема: Python/Django/FastAPI обработка статики — это напрасная трата ресурсов
- Django в продакшене НЕ должен обрабатывать статику
- Каждый запрос к изображению = блокирует воркер
- На 100 одновременных пользователей это заметно

Решение 1: Nginx/Apache (самое популярное)

# /etc/nginx/sites-available/myapp

server {
    listen 80;
    server_name myapp.com;
    client_max_body_size 50M;

# Статика: Nginx обрабатывает напрямую (очень быстро)
    location /static/ {
        alias /var/www/myapp/static/;
        expires 30d;  # Кэширование на клиенте
        add_header Cache-Control "public, immutable";
    }

location /media/ {
        alias /var/www/myapp/media/;
        expires 7d;
    }
Читать полностью ->