CartItemView was registered on both /cart/items/ and
/cart/items/{item_uuid}/, so swagger listed POST/PATCH/DELETE on both
paths even though 3 of those 6 combinations raised a TypeError at
runtime (missing/unexpected item_uuid). Split into CartItemView (POST,
list path) and CartItemDetailView (PATCH/DELETE, detail path).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
django-filter was already installed and set as DEFAULT_FILTER_BACKENDS
but unused everywhere. Replace hand-rolled get_queryset filtering with
FilterSet classes (stores/catalog/locations/reviews) and
filterset_fields (orders status). drf-spectacular auto-documents these
for swagger, so the manual OpenApiParameter declarations for the
migrated fields are removed (stores keeps lat/lng manual since
geo-distance isn't a plain filter).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
phone_number was only exposed on the store detail serializer; add it
to the list too. Also surface the distance annotation (already
computed when lat/lng are passed) as distance_km on both, instead of
using it only for ordering.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Neighborhood filtering was UUID-only; add name-based search on both
GET /api/v1/cities/ and GET /api/v1/neighborhoods/ (the latter also
matching by city name), and document all params for drf-spectacular.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same gap as the stores endpoint: store/category/search filters on
GET /api/v1/products/ already worked but weren't declared to
drf-spectacular.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Category/neighborhood/search/lat/lng filters on GET /api/v1/stores/
already worked but weren't declared to drf-spectacular, so they never
showed up in swagger.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- seed_demo_data: every seeded Store.logo/cover_image and Product.image
now points at a shared placeholder MinIO object_key
(uploads/c1508b9e-c01f-4892-b24c-c1a949b5b5f5.png) instead of null, so
demo data exercises the image_url/logo_url/etc. fields end-to-end.
Note: <field>_url will 404 until that object is actually uploaded to
the bucket.
- .env.example: drop the redundant MINIO_EXTERNAL_ENDPOINT* lines —
they already default to MINIO_ENDPOINT/MINIO_USE_HTTPS, and aren't
read anywhere in this app's actual presign/read path.
- Add MEDIA_INTEGRATION.md: reference doc for the MinIO/presigned-media
work on this branch (architecture, settings, API shape, existing-data
caveats, what was verified).
Swagger inferred these SerializerMethodFields as untyped strings and
warned on generation; annotate each with @extend_schema_field so the
generated OpenAPI schema declares them as nullable URLs.
The prior commit wired MinIO as Django's default storage backend but
kept plain ImageField multipart uploads through the Django backend and
public-bucket direct URLs — not how campaign/advertising/promotions do
it. Rework to match: image fields (ProductCategory.icon, Product.image,
StoreCategory.icon, Store.logo, Store.cover_image) now store an opaque
MinIO object_key (CharField) instead of a Django-managed file.
- utils/clients/minio_client.py — raw Minio SDK client, same shape as
the other services.
- apps/core/views/media.py — POST /api/media/presign/ mints a 30-minute
presigned PUT URL + object_key; the client uploads bytes directly to
MinIO, then submits the object_key on the owning resource.
- apps/core/media.py — presigned_media_url() helper; every serializer
with an image field now also exposes a `<field>_url` computed from a
fresh 1-hour presigned GET, rather than trusting a stored/direct URL.
- FRONTEND_GUIDE.md updated to document the new upload flow and field
shapes (object_key vs `_url`), replacing the stale "no upload
endpoint yet" note.
Verified against a live local MinIO container: presign → direct PUT
upload → object_key stored on the model → serializer mints a working
presigned GET URL → URL fetches the uploaded bytes. Also exercised the
actual DRF view (POST /api/media/presign/) end-to-end, including the
401 on an unauthenticated request.
Django 6 + DRF resource server against the Gooyal accounts OAuth2 service,
matching the Winsoo ecosystem's conventions. Covers locations, stores,
catalog, cart, checkout/orders (with the multi-store-cart split and the
status stepper), reviews, and notifications, plus a demo-data seed command.
Payment integration (wallet debits, online gateway, seller payouts) is
intentionally left out here — see feature/payment.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>