Django REST Framework backend for a Korean cosmetics e-commerce platform.
- Python 3.11, Django 4.x, Django REST Framework
- PostgreSQL (SQLite for local dev)
- SimpleJWT — authentication
- drf-spectacular — OpenAPI/Swagger docs
- Elasticsearch — faceted catalog search
- Redis — caching
- pytest — tests
# 1. Clone & create virtualenv
python -m venv venv && source venv/bin/activate
# 2. Install dependencies
pip install -r requirements.txt
# 3. Copy env file and fill in variables
cp .env.example .env
# 4. Apply migrations
python manage.py migrate
# 5. Run dev server
python manage.py runserver| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
insecure key | Django secret key |
DEBUG |
True |
Debug mode |
USE_SQLITE |
False |
Use SQLite instead of PostgreSQL |
DATABASE_URL |
— | PostgreSQL connection string |
ALLOWED_HOSTS |
* |
Comma-separated allowed hosts |
SENTRY_DSN |
— | Sentry DSN (optional) |
After running the server:
- Swagger UI:
http://localhost:8000/api/schema/swagger-ui/ - ReDoc:
http://localhost:8000/api/schema/redoc/
POST /api/auth/register/
Content-Type: application/json
{
"email": "user@example.com",
"password": "StrongPass1",
"password_confirm": "StrongPass1",
"first_name": "John",
"last_name": "Doe"
}
Response:
{
"user": { "id": 1, "email": "user@example.com", ... },
"tokens": { "access": "...", "refresh": "..." }
}POST /api/auth/login/
{ "email": "user@example.com", "password": "StrongPass1" }
Response:
{ "access": "...", "refresh": "..." }Authorization: Bearer <access_token>
POST /api/auth/token/refresh/
{ "refresh": "..." }
POST /api/auth/logout/
Authorization: Bearer <access_token>
{ "refresh": "..." }
POST /api/auth/google/
{ "id_token": "..." } # mobile
{ "access_token": "..." } # web
GET /api/auth/me/
Authorization: Bearer <access_token>
PATCH /api/auth/update/{id}/
Authorization: Bearer <access_token>
Only the owner or admin can update a profile.
GET /api/v1/products/
| Field | Type | Description |
|---|---|---|
id |
int | Product ID |
name |
string | Translated name |
slug |
string | URL slug |
price |
decimal | Current price |
old_price |
decimal | null | Price before discount |
image |
string | null | Main image URL |
is_sale |
bool | old_price > price |
is_new |
bool | Created within last 7 days |
rating |
decimal | Average rating |
review_count |
int | Number of reviews |
stock_status |
string | in_stock / low_stock / out_of_stock |
tags |
array | List of tag names |
| Condition | Value |
|---|---|
stock == 0 |
out_of_stock |
stock < 5 |
low_stock |
stock >= 5 |
in_stock |
| Parameter | Type | Example | Description |
|---|---|---|---|
sale_only |
bool | true |
Only products with discount |
in_stock_only |
bool | true |
Only products in stock |
rating_min |
float | 4.0 |
Minimum rating |
tags |
string | beauty,popular |
Filter by tags (comma-separated) |
category |
string | skincare |
Category slug |
price_min |
int | 10000 |
Minimum price |
price_max |
int | 50000 |
Maximum price |
search |
string | крем |
Full-text search |
brand |
string | laneige |
Brand slug |
lang |
string | ru |
Translation language (ru/en/kg) |
Example:
GET /api/v1/products/?sale_only=true&rating_min=4&category=skincare
{
"count": 120,
"next": "...?limit=40&offset=40",
"previous": null,
"results": [...]
}GET /api/v1/products/{slug}/
Returns same fields as list + variants, images, created_at, updated_at.
GET /api/v1/categories/
Returns hierarchical category tree.
GET /api/v1/brands/
All favorites endpoints require authentication (Authorization: Bearer <token>).
GET /api/v1/favorites/
Response:
{
"count": 1,
"results": [
{
"id": 1,
"product": {
"id": 10,
"name": "Product A",
"price": 23000,
"image": "...",
"stock_status": "in_stock"
}
}
]
}POST /api/v1/favorites/
{ "product_id": 10 }
- Returns
201on success - Returns
400if product already in favorites - Returns
404if product not found
DELETE /api/v1/favorites/{product_id}/
- Returns
204on success - Returns
404if not in favorites
pytest tests/ -vTest coverage:
- Auth: register, login, protected endpoints, token refresh
- Catalog: list, detail, all filters, stock_status, is_sale
- Favorites: add, delete, duplicate protection, data isolation
GitHub Actions pipeline runs on every push to main:
- Install dependencies
- Lint (ruff + isort)
- Run tests