This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
GrooveShop Django API — a headless e-commerce API built with Django 6.0 and Django REST Framework. Supports both WSGI (Gunicorn) and ASGI (Daphne/Uvicorn) with WebSocket notifications via Django Channels. Uses PostgreSQL 17, Redis, Celery (RabbitMQ broker), and Meilisearch. Python 3.14.2, managed with uv.
# Install dependencies (uses uv, not pip)
uv sync --locked --all-extras --dev
# Run all tests (parallel by default via -n auto in addopts)
uv run pytest
# Run a single test file
uv run pytest tests/unit/path/to/test_file.py
# Run a single test function
uv run pytest tests/unit/path/to/test_file.py::test_function_name
# Run tests with coverage (must disable parallel with -n0)
uv run pytest --cov=. --cov-report=term --cov-report=html --cov-config=pyproject.toml -n0
# Lint and format
uv run ruff check --fix
uv run ruff format
# Run all pre-commit hooks
uv run pre-commit run --all-files
# Django management
uv run python manage.py makemigrations
uv run python manage.py migrate
uv run python manage.py runserver
uv run python manage.py seed_all
# Generate OpenAPI schema (used by Nuxt frontend for types)
uv run python manage.py spectacular --color --file schema.yml
# Meilisearch index management
uv run python manage.py meilisearch_sync_all_indexes
uv run python manage.py meilisearch_sync_index --model ProductTranslation
# Docker (full stack)
docker compose up -d --buildSingle settings.py at the project root (not inside any app). Uses SYSTEM_ENV env var (dev, production, ci) for environment-specific behavior. All config via .env file loaded with python-dotenv. The DJANGO_SETTINGS_MODULE is just "settings".
All apps live at the project root (flat structure, no src/ directory):
- core/ — Shared infrastructure: base views, serializers, permissions, middleware, filters, caching, Celery config, URL routing. The
core/urls.pyis the root URL conf that mounts all other apps underapi/v1/. - product/, order/, cart/, blog/, user/ — Main domain apps
- search/ — Meilisearch integration with federated search, Greeklish expansion, and analytics
- meili/ — Meilisearch model definitions and indexing via
IndexMixinwithMeiliMetaconfig - notification/ — Real-time notifications via WebSocket (Django Channels consumer)
- loyalty/ — Points, XP, tiers, and rewards system
- country/, region/, vat/, pay_way/, tag/, contact/ — Supporting domain apps
- admin/ — Custom admin app with django-unfold dashboard callback
- devtools/ —
CustomDjangoModelFactorybase class for all test factories
Abstract models (all in core/models.py):
TimeStampMixinModel—created_at/updated_atwith indexesUUIDModel— UUID4 field for external/guest accessSeoModel—seo_title,seo_description,seo_keywordsSortableModel—sort_orderwith atomicmove_up()/move_down()usingselect_for_update()PublishableModel—is_published/published_atwithPublishableManager.published()querysetMetaDataModel—metadata/private_metadataJSONFields with GinIndexSoftDeleteModel—is_deleted/deleted_atwithSoftDeleteManager(.all_with_deleted(),.deleted_only(),.restore(),.hard_delete())
Domain models compose multiple mixins, e.g. Product(SoftDeleteModel, TranslatableModel, TimeStampMixinModel, SeoModel, UUIDModel, MetaDataModel, TaggedModel).
Base ViewSet (core/api/views.py: BaseModelViewSet):
- Combines
RequestResponseSerializerMixin,TranslationsModelViewSet,PaginationModelViewSet - Uses
serializers_configdict mapping actions toActionConfigobjects (separate request/response serializers per action) - Supports three pagination strategies via
?pagination_type=query param:pageNumber(default),cursor,limitOffset - Handles multipart translation data via
TranslationsProcessingMixin - Atomic transactions on create/update
Optimized Managers (core/managers/):
OptimizedManager/OptimizedQuerySet— Overridefor_list()andfor_detail()for select_related/prefetch_relatedTranslatableOptimizedManager/TranslatableOptimizedQuerySet— Addswith_translations()for parlerTreeTranslatableManager/TreeTranslatableQuerySet— For MPTT + Parler models (categories)- ViewSets call
Model.objects.for_list()or.for_detail()based on action
Composable FilterSets (core/filters/):
- Mixin-based:
TimeStampFilterMixin,PublishableFilterMixin,SoftDeleteFilterMixin,MetaDataFilterMixin,UUIDFilterMixin,SortableFilterMixin - CamelCase variants:
CamelCaseFilterMixinauto-converts query param names - Pre-built:
BaseFullFilterSet(all mixins),CamelCasePublishableTimeStampFilterSet, etc.
Pagination (core/pagination/): PageNumberPaginator, CursorPaginator, LimitOffsetPaginator — all return consistent envelope: {links, count, total_pages, page_size, page_total_results, page, results}
Serializer utilities (core/utils/serializers.py):
ActionConfigdataclass — per-action request/response serializer + OpenAPI metadatacrud_config()— shorthand for standard CRUD serializer mappingcreate_schema_view_config()— auto-generates@extend_schemadecorators from model verbose names
Custom fields: ImageAndSvgField (images + SVG), MeasurementField (physical measurements with unit conversion)
Permissions (core/api/permissions.py): IsOwnerOrAdmin, IsOwnerOrAdminOrGuest — checks user, owner, or created_by fields. IsOwnerOrAdminOrGuest additionally handles guest orders: when obj.user is None, it verifies request.query_params.get("uuid") == str(obj.uuid).
- All API endpoints are under
api/v1/with DRF ViewSets - Request/response bodies use camelCase (auto-converted from snake_case via
djangorestframework-camel-case) - Authentication: Knox token auth (
Bearerprefix, 7-day TTL, auto-refresh after 1 day) + Django Allauth (account management, social providers: Google/Facebook/GitHub/Discord, MFA/WebAuthn/Passkeys) + Django session auth DEFAULT_PERMISSION_CLASSESisIsAuthenticatedOrReadOnly. Any endpoint that requires anonymous POST access (e.g. guest checkout) must explicitly setpermission_classes = [AllowAny].- Payments: Stripe via dj-stripe
- Default pagination: 12 items per page, max 100
- OpenAPI docs at
/api/v1/schema/swagger-uiand/api/v1/schema/redoc - Health check at
/api/v1/health(checks DB, Redis, Celery) - Serializer tiering: separate List/Detail/Write serializers per model
- Order / payment / shipping / notification system: see
docs/order-system.mdfor the full reference — state machines, creation paths, carrier integrations, email + WS dedup, critical invariants, common-task playbook. Read this first before changing anything inorder/,shipping_acs/,shipping_boxnow/, orpay_way/. - Translations: django-parler
TranslatableModelon Product, BlogPost, Category, LoyaltyTier, etc. Languages: el (default), en, de. Factories create translations for all languages. - Audit history: django-simple-history on models
- Tree structures: django-mptt
TreeForeignKeyfor ProductCategory, BlogCategory - Monetary fields: django-money
MoneyFieldfor prices, totals. Default currency: EUR - Stock management:
StockManagerwith atomicreserve_stock()/release_stock()usingselect_for_update()to prevent overselling under concurrent requests. The legacycreate_orderpath also performs stock checks inside an atomic block.StockReservationmodel with TTL (default 15 min).StockLogfor audit trail. - Computed properties: Models check
__dict__for annotation values before falling back to DB queries (e.g.likes_count,review_average) - Meilisearch indexing: Models inherit
IndexMixinwithMeiliMetaclass defining filterable/searchable/sortable fields, ranking rules, synonyms, typo tolerance.meili_filter()controls indexing eligibility.get_additional_meili_fields()adds computed fields.
ASGI routing in asgi/__init__.py with Channels ProtocolTypeRouter:
- HTTP: Django ASGI with CORS handler
- WebSocket:
ws/notifications/→NotificationConsumer - Auth via
TokenAuthMiddleware— only?access_token=<knox>in query params;session_tokenis not accepted - Groups:
user_{id}per-user,adminsfor staff
App configured in core/celery.py. Base task class: MonitoredTask (logs success/failure). All tasks use autoretry_for, retry_backoff, and retry_jitter with exponential backoff (max 5 retries).
Tasks are split by domain:
core/tasks.py— system health monitoring, DB backup, cache clearing, session cleanup, Meilisearch sync, abandoned cart cleanup, loyalty points expiration, inactive user notificationsproduct/tasks.py— price drop notificationssearch/tasks.py— search analyticsblog/tasks.py— comment liked notificationsloyalty/tasks.py— loyalty point award/expiry tasks (all useMonitoredTaskbase class)
All factories extend CustomDjangoModelFactory from devtools/factories.py. Key features:
auto_translationsflag for parler modelsunique_model_fieldslist for collision-free field generation@factory.post_generationfor related objects (images, reviews, translations, cart items)get_or_create_instance()helper incore/helpers/factory.pyfor dynamic model/factory resolutionUserNameGeneratorincore/generators.pygenerates{Adjective}{Noun}#{hash}usernames from email
Tests in tests/ with unit/, integration/, and utils/ subdirectories. Key conftest.py settings:
- MD5 password hasher (faster than default)
DISABLE_CACHE = True,MEILISEARCH["OFFLINE"] = TrueCELERY_TASK_ALWAYS_EAGER = True(synchronous execution)- Auto-fixtures: cache clearing, DB query reset, site cache clear, connection cleanup for xdist
requires_meilisearchskip marker for tests needing live Meilisearchcount_queriesfixture andQueryCountAssertionMixinfor N+1 detection- Coverage minimum: 50% (
fail_under = 50), timeout: 600s
infra.compose.yml— PostgreSQL 17, Redis, RabbitMQ, Meilisearch v1.42.1, pgAdmin, RedisInsightapp.compose.yml— backend-init (migrations), backend, celery_worker, celery_beat, celery_flowerdocker-compose.yml— Combines both ongrooveshop-backbonebridge networkDockerfile— Multi-stage Alpine build (uv → tailwind CSS → Python deps → production)dev.Dockerfile— Debian slim with full dev environment
GitHub Actions (.github/workflows/ci.yml): 3-stage pipeline:
- Quality — Ruff format check
- Testing — PostgreSQL 17 + Redis + Meilisearch services, migrations, pytest with coverage (15 min timeout)
- Release — python-semantic-release → TestPyPI → PyPI (main branch only)
- Line length: 80 characters
- Formatter/Linter: Ruff (targets Python 3.13)
- Max function args: 6 (pylint rule via ruff)
- Migrations are excluded from linting (
**/migrations/**) - Semantic release:
feat→ minor,fix/perf→ patch