1---2name: python-django3description: Write Python and Django code for Saleor — Django ORM patterns, signals, Celery tasks, type hints, migrations, management commands, and async views. Use when writing Python/Django in Saleor projects.4---56# Python & Django for Saleor78## Before writing code910**Fetch live docs**:111. Web-search `site:docs.djangoproject.com topics models querysets` for current Django ORM documentation122. Web-search `site:docs.djangoproject.com topics signals` for Django signals reference133. Web-search `site:docs.celeryq.dev userguide tasks` for Celery task patterns and configuration144. Web-search `site:docs.saleor.io developer` for Saleor-specific Django conventions155. Fetch `https://docs.python.org/3/library/typing.html` for Python type hints reference1617## Django ORM Patterns for Saleor1819### QuerySet Operations2021| Operation | Method | Example Use |22|-----------|--------|-------------|23| **Filter** | `.filter(**kwargs)` | Filter products by type |24| **Exclude** | `.exclude(**kwargs)` | Exclude draft orders |25| **Annotate** | `.annotate(expr)` | Add computed fields (totals, counts) |26| **Aggregate** | `.aggregate(expr)` | Compute sum, avg across queryset |27| **Select related** | `.select_related("fk")` | Join foreign keys (avoid N+1) |28| **Prefetch related** | `.prefetch_related("m2m")` | Batch-load many-to-many (avoid N+1) |29| **Values** | `.values("field")` | Return dictionaries instead of objects |30| **Order by** | `.order_by("field")` | Sort results |3132### F and Q Objects3334| Object | Purpose | Example Use |35|--------|---------|-------------|36| `F("field")` | Reference model field in expressions | Update stock: `F("quantity") - 1` |37| `Q(condition)` | Complex lookups with OR/AND/NOT | `Q(status="active") | Q(featured=True)` |3839- Use `F()` for atomic field updates without race conditions40- Combine `Q()` objects with `|` (OR), `&` (AND), `~` (NOT) for complex filters4142## Model Relationships in Saleor4344| Relationship | Field Type | Example |45|-------------|-----------|---------|46| **One-to-Many** | `ForeignKey` | Order -> User, OrderLine -> Order |47| **Many-to-Many** | `ManyToManyField` | Product -> Category (via ProductCategory) |48| **One-to-One** | `OneToOneField` | User -> UserProfile |49| **Generic** | `GenericForeignKey` | Attribute values on multiple entity types |5051- Always set `on_delete` explicitly (`CASCADE`, `PROTECT`, `SET_NULL`)52- Use `related_name` for reverse lookups53- Index foreign keys used in frequent queries5455## Django Signals5657| Signal | Fires When | Common Use |58|--------|-----------|------------|59| `pre_save` | Before `model.save()` | Validate or transform data |60| `post_save` | After `model.save()` | Trigger side effects, send notifications |61| `pre_delete` | Before `model.delete()` | Clean up related resources |62| `post_delete` | After `model.delete()` | Remove cached data |63| `m2m_changed` | Many-to-many field modified | Update denormalized counters |6465- Saleor uses webhooks as the primary event mechanism, not Django signals66- Use signals sparingly for internal side effects only67- Never perform expensive I/O in signal handlers (use Celery tasks instead)68- Connect signals in `AppConfig.ready()` to avoid import issues6970## Celery Task Patterns7172### Defining Tasks7374| Pattern | Decorator | Use Case |75|---------|-----------|----------|76| **Shared task** | `@shared_task` | Framework-agnostic, recommended |77| **App task** | `@app.task` | Tied to specific Celery app |78| **Bound task** | `@shared_task(bind=True)` | Access `self` for retries |7980### Retry and Error Handling8182| Parameter | Description | Example |83|-----------|-------------|---------|84| `max_retries` | Maximum retry attempts | `3` |85| `default_retry_delay` | Seconds between retries | `60` |86| `retry_backoff` | Enable exponential backoff | `True` |87| `autoretry_for` | Exception classes to auto-retry | `(ConnectionError,)` |88| `acks_late` | Acknowledge after execution | `True` (prevents task loss) |8990- Pass serializable arguments (IDs, not model instances)91- Keep tasks idempotent — safe to retry without side effects92- Set reasonable timeouts with `soft_time_limit` and `time_limit`9394## Python Type Hints9596| Type | Import | Use |97|------|--------|-----|98| `Optional[T]` | `typing` | Value may be None |99| `list[T]` | Built-in (3.9+) | Typed list |100| `dict[K, V]` | Built-in (3.9+) | Typed dictionary |101| `Union[A, B]` | `typing` | Either type A or B |102| `Protocol` | `typing` | Structural subtyping (duck typing) |103| `TypedDict` | `typing` | Typed dictionary with fixed keys |104| `Literal["a", "b"]` | `typing` | Restrict to specific values |105106- Use `from __future__ import annotations` for postponed evaluation107- Type all function parameters, return values, and class attributes108- Use `mypy` or `pyright` for static type checking109110## Database Migrations111112| Command | Purpose |113|---------|---------|114| `python manage.py makemigrations` | Generate migration files from model changes |115| `python manage.py migrate` | Apply pending migrations to database |116| `python manage.py showmigrations` | List all migrations and their status |117| `python manage.py sqlmigrate app_name 0001` | Show SQL for a specific migration |118| `python manage.py squashmigrations app_name 0001 0010` | Combine multiple migrations |119120- Review generated migrations before applying to production121- Use `RunPython` for data migrations (separate from schema migrations)122- Test migrations on a copy of production data before deploying123124## Management Commands125126| Aspect | Convention |127|--------|-----------|128| **Location** | `app_name/management/commands/command_name.py` |129| **Class** | Subclass `BaseCommand` |130| **Entry point** | `handle(self, *args, **options)` method |131| **Arguments** | Use `add_arguments(self, parser)` with `argparse` |132| **Output** | Use `self.stdout.write()` and `self.style` |133134- Use management commands for one-off scripts, data fixes, and admin tasks135- Always add `help` text to commands for documentation136137## Async Django Views (ASGI)138139| Feature | Sync | Async |140|---------|------|-------|141| **View type** | `def view(request)` | `async def view(request)` |142| **Server** | WSGI (Gunicorn) | ASGI (Uvicorn, Daphne) |143| **ORM access** | Direct | Wrap in `sync_to_async` |144| **HTTP calls** | `requests` | `httpx` (async) |145146- Saleor runs via ASGI with Uvicorn workers under Gunicorn147- Django ORM is synchronous — use `sync_to_async` or `QuerySet.aiterator()`148- Use `httpx.AsyncClient` for non-blocking HTTP requests149150## Django Settings and Virtual Environments151152| Pattern | Description |153|---------|-------------|154| **Environment variables** | Load with `os.environ` or `django-environ` |155| **Split settings** | `settings/base.py`, `settings/dev.py`, `settings/prod.py` |156| **Twelve-Factor** | All configuration via environment variables |157158| Tool | Command | Notes |159|------|---------|-------|160| **venv** | `python -m venv .venv` | Built-in, standard |161| **poetry** | `poetry install` | Dependency resolution, lock file |162| **uv** | `uv venv && uv pip install -r requirements.txt` | Fast Rust-based installer |163164## Best Practices165166- Use `select_related` and `prefetch_related` to avoid N+1 query problems167- Keep Django signals lightweight — offload heavy work to Celery tasks168- Type all function signatures and use `mypy` for static analysis169- Make Celery tasks idempotent and pass only serializable arguments170- Review migration SQL before applying to production databases171- Use `F()` expressions for atomic field updates instead of read-modify-write172- Follow Saleor's existing code style when contributing to the core173- Use async views and `httpx` for I/O-heavy endpoints174- Store all configuration in environment variables following twelve-factor methodology175176Fetch the Python and Django documentation for current ORM patterns, Celery task configuration, and async view setup before implementing.