A backend for creating accounts and booking taxi rides, with token-based auth and a real-time, distance-based fare calculator. Every endpoint is self-documented through an auto-generated OpenAPI schema.
Python, Django, Django REST Framework, SQLite, drf-spectacular for schema generation. drf-spectacular docs: https://drf-spectacular.readthedocs.io/en/latest/
git clone https://github.com/bobur-yusupov/booking-api.git
cd booking-api
docker compose up --buildThe container runs migrations and starts the dev server automatically. Once it's up:
- API root: http://localhost:8000/
- Interactive docs (Swagger UI): http://localhost:8000/api/docs/
Fallback: run without Docker
pip install -r requirements.txt
python manage.py makemigrations
python manage.py migrate
python manage.py runserverflowchart TD
Client[Client] -->|HTTP| App
subgraph Compose["docker compose"]
App["app container\nDjango + DRF"]
DB[("db container\nPostgres 15")]
end
App -->|"/accounts/*"| Accounts[accounts app\ncustom User + token auth]
App -->|"/taxi/order/*"| Taxi[taxi app\nOrderTaxi viewset]
App -->|"/api/schema, /api/docs"| Docs[drf-spectacular]
Taxi -->|geopy| Nominatim[("Nominatim\nOpenStreetMap geocoding")]
App -.->|"defined, not wired\nsee issue #6"| DB
App --> SQLite[("SQLite file")]
A client signs up and exchanges credentials for a DRF token at /accounts/token/, then sends that token on every subsequent request. TaxiOrderViewSet requires the token, scopes its queryset to request.user, and on read computes each order's price by calling out to geopy, which geocodes both locations against the public Nominatim service and derives a flat-rate fare from the straight-line distance. drf-spectacular introspects the same viewsets and serializers to generate the OpenAPI schema served at /api/docs/. Everything runs in one app container; compose also defines a db Postgres container, but the active settings hardcode SQLite, so that database isn't actually wired into the app yet (tracked in issue #6 below).
Custom User model built on AbstractBaseUser + PermissionsMixin, not AbstractUser. It replaces username with email as USERNAME_FIELD and uses a UUID primary key instead of Django's default auto-incrementing integer. The obvious alternative was subclassing AbstractUser and adding the extra fields on top, keeping Django's default username-based login and integer IDs.
TODO: why email/UUID over extending AbstractUser, and what did the custom manager (create_user/create_superuser) cost you versus the built-in one?
Role flag on the user is set but never actually stored. UserManager.create_user runs user.is_user = True, but User has no is_user field anywhere in the model or migrations — it's a plain Python attribute that disappears the moment the instance is reloaded from the database. There's also no is_taxi flag or driver profile at all, despite the taxi app depending on a rider (user FK) to place orders. Today there is exactly one flat account type. The obvious alternative is a real BooleanField/CharField(choices=...) on the model, or a separate Driver profile linked one-to-one to User.
TODO: why did the role flag stop at an in-memory attribute, and is rider/driver differentiation still coming, or was that dropped?
Fare calculation geocodes on every read, against a public service, with no caching. OrderTaxiSerializer exposes price as a property that calls taxi/functions/measure_length.py's Distance class, which geocodes both from_location and to_location through geopy's Nominatim (OpenStreetMap) on every access, then charges a flat rate off the resulting straight-line distance. The obvious alternative is geocoding once at order creation and storing the coordinates/distance, or using a paid geocoding provider with an SLA instead of the free, rate-limited public endpoint.
TODO: why compute live on every read instead of caching at write time, and has this been tested against Nominatim's usage/rate limits under real traffic?
Tests exist for accounts and taxi, run with:
python manage.py testCoverage: user creation (incl. email normalization and superuser creation), the signup/token/profile API endpoints, and Django admin list/edit/add pages for accounts; taxi order creation and its string representation for taxi. Coverage is uneven — taxi/test/test_taxi_booking_api.py::test_orders_by_user is a stub with no assertions yet, and the per-user queryset filtering it names isn't actually tested.
- Wire the
dbPostgres container into the actual Django settings, or drop it fromdocker-compose.yml— right now it starts up and does nothing (issue #6). - Persist the role flag properly (or remove it) instead of leaving
is_useras a non-persisted attribute that a same-request test can't catch. - Move fare calculation off the request path — geocode once at creation time instead of on every serialization.
MIT — see LICENSE.