Add multi-image compositions, docker setup, tests, and docs
- Add CompositionImage model with multi-image upload and main image flag - Add composition endpoints: add-images, set-main-image, delete image, by-created-at filter - Make contact-us by_category public - Add 24 API tests covering all endpoints - Add Dockerfile, docker-compose, entrypoint and docker docs - Add Persian API docs and update README and Postman collection Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
326
README.md
326
README.md
@@ -1,135 +1,261 @@
|
||||
# Zoneco ORG Backend API
|
||||
|
||||
A professional Django REST Framework backend for Zoneco ORG with PostgreSQL database.
|
||||
A Django REST Framework backend for Zoneco ORG with PostgreSQL database.
|
||||
|
||||
---
|
||||
|
||||
## Test Status
|
||||
|
||||
```
|
||||
Ran 24 tests in ~12s — OK (0 failures)
|
||||
```
|
||||
|
||||
| Group | Tests | Status |
|
||||
|-------|-------|--------|
|
||||
| Contact Us | 6 | ✅ All pass |
|
||||
| Compositions | 11 | ✅ All pass |
|
||||
| Campaigns | 7 | ✅ All pass |
|
||||
|
||||
Run tests at any time:
|
||||
```bash
|
||||
cd backend
|
||||
python manage.py test api -v 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Contact Us API**: Handle contact form submissions with name, email/phone, description, and category
|
||||
- **Composition API**: Manage compositions with name, description, and image
|
||||
- **Campaign API**: Manage campaigns with name, description, start/end times, and image
|
||||
- **RESTful API**: Full CRUD operations for all models
|
||||
- **CORS Enabled**: Configured for frontend integration
|
||||
- **Image Upload**: Support for image uploads with proper media handling
|
||||
- **Admin Panel**: Django admin interface for managing data
|
||||
- **Contact Us API** — Contact form submissions with category filter
|
||||
- **Composition API** — Multi-image upload, main image flag, date-range filter
|
||||
- **Campaign API** — Campaigns with active / upcoming / ended filters
|
||||
- **Admin Panel** — Full Django admin at `/admin/`
|
||||
- **Image Upload** — JPEG, PNG, WebP, GIF, max 5 MB, stored in `media/`
|
||||
- **CORS Enabled** — Configured for frontend integration
|
||||
- **Pagination** — 20 items per page
|
||||
|
||||
## Database Configuration
|
||||
---
|
||||
|
||||
- **Database Name**: Zoneco_ORG
|
||||
- **User**: postgres
|
||||
- **Password**: postgres
|
||||
- **Host**: localhost
|
||||
- **Port**: 5432
|
||||
## Quick Start
|
||||
|
||||
## Installation
|
||||
### 1. Install dependencies
|
||||
|
||||
1. **Install Python dependencies:**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Create PostgreSQL database:**
|
||||
```sql
|
||||
CREATE DATABASE "Zoneco_ORG";
|
||||
```
|
||||
### 2. Create PostgreSQL database
|
||||
|
||||
3. **Run migrations:**
|
||||
```bash
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
```
|
||||
```sql
|
||||
CREATE DATABASE "Zoneco_ORG";
|
||||
```
|
||||
|
||||
4. **Create superuser (optional):**
|
||||
```bash
|
||||
python manage.py createsuperuser
|
||||
```
|
||||
### 3. Apply migrations
|
||||
|
||||
5. **Run development server:**
|
||||
```bash
|
||||
python manage.py runserver
|
||||
```
|
||||
```bash
|
||||
python manage.py migrate
|
||||
```
|
||||
|
||||
The API will be available at `http://localhost:8000/api/`
|
||||
### 4. Run development server
|
||||
|
||||
```bash
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
API available at `{{base_url}}/api/`
|
||||
|
||||
---
|
||||
|
||||
## Admin Credentials
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| URL | `{{base_url}}/admin/` |
|
||||
| Username | `Zoneco_@1405` |
|
||||
| Password | `Fun_@_zone2026` |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Contact Us
|
||||
- `GET /api/contact-us/` - List all contacts
|
||||
- `POST /api/contact-us/` - Create new contact (public)
|
||||
- `GET /api/contact-us/{id}/` - Get specific contact
|
||||
- `PUT /api/contact-us/{id}/` - Update contact (authenticated)
|
||||
- `PATCH /api/contact-us/{id}/` - Partial update (authenticated)
|
||||
- `DELETE /api/contact-us/{id}/` - Delete contact (authenticated)
|
||||
- `GET /api/contact-us/by_category/?category={category}` - Filter by category
|
||||
### Authentication
|
||||
|
||||
### Compositions
|
||||
- `GET /api/compositions/` - List all compositions
|
||||
- `POST /api/compositions/` - Create new composition (authenticated)
|
||||
- `GET /api/compositions/{id}/` - Get specific composition
|
||||
- `PUT /api/compositions/{id}/` - Update composition (authenticated)
|
||||
- `PATCH /api/compositions/{id}/` - Partial update (authenticated)
|
||||
- `DELETE /api/compositions/{id}/` - Delete composition (authenticated)
|
||||
| Action | Access |
|
||||
|--------|--------|
|
||||
| GET (list, retrieve, custom filters) | Public — no login required |
|
||||
| POST (create) | Public — no login required |
|
||||
| PUT / PATCH / DELETE | Admin only — Basic Auth required |
|
||||
|
||||
### Campaigns
|
||||
- `GET /api/campaigns/` - List all campaigns
|
||||
- `POST /api/campaigns/` - Create new campaign (authenticated)
|
||||
- `GET /api/campaigns/{id}/` - Get specific campaign
|
||||
- `PUT /api/campaigns/{id}/` - Update campaign (authenticated)
|
||||
- `PATCH /api/campaigns/{id}/` - Partial update (authenticated)
|
||||
- `DELETE /api/campaigns/{id}/` - Delete campaign (authenticated)
|
||||
- `GET /api/campaigns/active/` - Get active campaigns
|
||||
- `GET /api/campaigns/upcoming/` - Get upcoming campaigns
|
||||
- `GET /api/campaigns/ended/` - Get ended campaigns
|
||||
---
|
||||
|
||||
### Contact Us — `/api/contact-us/`
|
||||
|
||||
| Method | Endpoint | Access | Description |
|
||||
|--------|----------|--------|-------------|
|
||||
| GET | `/api/contact-us/` | Public | List all submissions |
|
||||
| POST | `/api/contact-us/` | Public | Submit a contact form |
|
||||
| GET | `/api/contact-us/{id}/` | Public | Get one submission |
|
||||
| GET | `/api/contact-us/by_category/?category=...` | Public | Filter by category |
|
||||
| PATCH | `/api/contact-us/{id}/` | Admin | Partial update |
|
||||
| PUT | `/api/contact-us/{id}/` | Admin | Full update |
|
||||
| DELETE | `/api/contact-us/{id}/` | Admin | Delete |
|
||||
|
||||
**Category choices:** `همکاری` · `فروش` · `پشتیبانی` · `درخواست مشاور` · `سایر`
|
||||
|
||||
**Fields:**
|
||||
|
||||
| Field | Type | Required |
|
||||
|-------|------|----------|
|
||||
| `name` | string | Yes |
|
||||
| `email_or_phone` | string | Yes |
|
||||
| `description` | string | Yes |
|
||||
| `category` | string | Yes |
|
||||
|
||||
---
|
||||
|
||||
### Compositions — `/api/compositions/`
|
||||
|
||||
Each composition can have **multiple images**. One image can be flagged as the **main image** (`is_main: true`).
|
||||
|
||||
| Method | Endpoint | Access | Description |
|
||||
|--------|----------|--------|-------------|
|
||||
| GET | `/api/compositions/` | Public | List all compositions |
|
||||
| POST | `/api/compositions/` | Public | Create composition (with optional images) |
|
||||
| GET | `/api/compositions/{id}/` | Public | Get one composition |
|
||||
| GET | `/api/compositions/by-created-at/?from=...&to=...` | Public | Filter by creation date range |
|
||||
| POST | `/api/compositions/{id}/add-images/` | Admin | Add images to existing composition |
|
||||
| POST | `/api/compositions/{id}/set-main-image/` | Admin | Set main image |
|
||||
| DELETE | `/api/compositions/{id}/images/{image_id}/` | Admin | Delete a single image |
|
||||
| PATCH | `/api/compositions/{id}/` | Admin | Partial update |
|
||||
| PUT | `/api/compositions/{id}/` | Admin | Full update |
|
||||
| DELETE | `/api/compositions/{id}/` | Admin | Delete composition |
|
||||
|
||||
**Fields:**
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `name` | string | Yes | |
|
||||
| `description` | string | Yes | |
|
||||
| `uploaded_images` | file[] | No | One or more image files (multipart) |
|
||||
| `main_image_index` | integer | No | 0-based index of main image in `uploaded_images`. Defaults to 0. |
|
||||
|
||||
**Image constraints:** JPEG, PNG, WebP, GIF — max **5 MB** each — stored at `media/compositions/`
|
||||
|
||||
**`by-created-at` query params:**
|
||||
|
||||
| Param | Required | Format |
|
||||
|-------|----------|--------|
|
||||
| `from` | At least one | ISO 8601 e.g. `2026-06-01T00:00:00Z` |
|
||||
| `to` | At least one | ISO 8601 e.g. `2026-06-30T23:59:59Z` |
|
||||
|
||||
---
|
||||
|
||||
### Campaigns — `/api/campaigns/`
|
||||
|
||||
| Method | Endpoint | Access | Description |
|
||||
|--------|----------|--------|-------------|
|
||||
| GET | `/api/campaigns/` | Public | List all campaigns |
|
||||
| POST | `/api/campaigns/` | Public | Create campaign |
|
||||
| GET | `/api/campaigns/{id}/` | Public | Get one campaign |
|
||||
| GET | `/api/campaigns/active/` | Public | Currently active campaigns |
|
||||
| GET | `/api/campaigns/upcoming/` | Public | Not started yet |
|
||||
| GET | `/api/campaigns/ended/` | Public | Already finished |
|
||||
| PATCH | `/api/campaigns/{id}/` | Admin | Partial update |
|
||||
| PUT | `/api/campaigns/{id}/` | Admin | Full update |
|
||||
| DELETE | `/api/campaigns/{id}/` | Admin | Delete |
|
||||
|
||||
**Fields:**
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `name` | string | Yes | |
|
||||
| `description` | string | Yes | |
|
||||
| `start_time` | datetime | Yes | ISO 8601 |
|
||||
| `end_time` | datetime | Yes | Must be after `start_time` |
|
||||
| `image` | file | No | Campaign image (multipart) |
|
||||
|
||||
**Read-only computed fields:** `is_active`, `is_upcoming`, `is_ended`
|
||||
|
||||
---
|
||||
|
||||
## Models
|
||||
|
||||
### ContactUs
|
||||
- `name` (CharField): Name of the contact
|
||||
- `email_or_phone` (CharField): Email or phone number
|
||||
- `description` (TextField): Message description
|
||||
- `category` (CharField): Category (همکاری, فروش, پشتیبانی, درخواست مشاور, سایر)
|
||||
- `created_at` (DateTimeField): Creation timestamp
|
||||
- `updated_at` (DateTimeField): Last update timestamp
|
||||
|
||||
| Field | Type |
|
||||
|-------|------|
|
||||
| `name` | CharField |
|
||||
| `email_or_phone` | CharField |
|
||||
| `description` | TextField |
|
||||
| `category` | CharField (choices) |
|
||||
| `created_at` | DateTimeField (auto) |
|
||||
| `updated_at` | DateTimeField (auto) |
|
||||
|
||||
### Composition
|
||||
- `name` (CharField): Name of the composition
|
||||
- `description` (TextField): Description
|
||||
- `image` (ImageField): Composition image
|
||||
- `created_at` (DateTimeField): Creation timestamp
|
||||
- `updated_at` (DateTimeField): Last update timestamp
|
||||
|
||||
| Field | Type |
|
||||
|-------|------|
|
||||
| `name` | CharField |
|
||||
| `description` | TextField |
|
||||
| `image` | ImageField (legacy) |
|
||||
| `created_at` | DateTimeField (auto) |
|
||||
| `updated_at` | DateTimeField (auto) |
|
||||
|
||||
### CompositionImage
|
||||
|
||||
| Field | Type |
|
||||
|-------|------|
|
||||
| `composition` | ForeignKey → Composition |
|
||||
| `image` | ImageField |
|
||||
| `is_main` | BooleanField |
|
||||
| `created_at` | DateTimeField (auto) |
|
||||
|
||||
DB constraint: only one `is_main=True` per composition.
|
||||
|
||||
### Campaign
|
||||
- `name` (CharField): Campaign name
|
||||
- `description` (TextField): Campaign description
|
||||
- `start_time` (DateTimeField): Campaign start time
|
||||
- `end_time` (DateTimeField): Campaign end time
|
||||
- `image` (ImageField): Campaign image
|
||||
- `created_at` (DateTimeField): Creation timestamp
|
||||
- `updated_at` (DateTimeField): Last update timestamp
|
||||
|
||||
## Admin Panel
|
||||
| Field | Type |
|
||||
|-------|------|
|
||||
| `name` | CharField |
|
||||
| `description` | TextField |
|
||||
| `start_time` | DateTimeField |
|
||||
| `end_time` | DateTimeField |
|
||||
| `image` | ImageField |
|
||||
| `created_at` | DateTimeField (auto) |
|
||||
| `updated_at` | DateTimeField (auto) |
|
||||
|
||||
Access the Django admin panel at `http://localhost:8000/admin/` after creating a superuser.
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
CORS is configured to allow requests from:
|
||||
- `http://localhost:5173` (Vite default)
|
||||
- `http://localhost:3000`
|
||||
- `http://127.0.0.1:5173`
|
||||
- `http://127.0.0.1:3000`
|
||||
---
|
||||
|
||||
## Media Files
|
||||
|
||||
Uploaded images are stored in the `media/` directory:
|
||||
- Composition images: `media/compositions/`
|
||||
- Campaign images: `media/campaigns/`
|
||||
| Content | Path |
|
||||
|---------|------|
|
||||
| Composition images | `media/compositions/` |
|
||||
| Campaign images | `media/campaigns/` |
|
||||
|
||||
## Development Notes
|
||||
---
|
||||
|
||||
- The backend uses Django REST Framework for API endpoints
|
||||
- PostgreSQL is used as the database
|
||||
- Image uploads are handled via multipart/form-data
|
||||
- All models include proper indexing for performance
|
||||
- Serializers include validation and proper error handling
|
||||
- Viewsets provide both public read access and authenticated write access
|
||||
## Postman Collection
|
||||
|
||||
Import `backend/Zoneco_ORG_API.postman_collection.json` into Postman.
|
||||
|
||||
Set the `base_url` collection variable to your server address.
|
||||
|
||||
---
|
||||
|
||||
## Persian Documentation
|
||||
|
||||
Full Persian API documentation: `backend/API_DOCS_FA.md`
|
||||
|
||||
---
|
||||
|
||||
## HTTP Status Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| 200 | OK |
|
||||
| 201 | Created |
|
||||
| 204 | Deleted (no body) |
|
||||
| 400 | Invalid data |
|
||||
| 403 | Forbidden (login required) |
|
||||
| 404 | Not found |
|
||||
|
||||
Reference in New Issue
Block a user