Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ALEJ4NDRO2025/urban-store/llms.txt

Use this file to discover all available pages before exploring further.

Urban Store’s product catalog is the public-facing heart of the store. Every product is stored as a MongoDB document managed by MongoEngine, with support for multiple sizes, multiple colors, and fine-grained inventory tracking at the individual size-and-color variant level. The catalog is entirely read-only for shoppers — no authentication is required to browse or filter products — while creation, editing, and deletion are reserved for administrators.

Product Data Model

Each product maps to a document in the products MongoDB collection. The fields below come directly from products/models.py:
FieldMongoEngine TypeNotes
nameStringField(required=True, max_length=200)Display name of the product
slugStringField(required=True, unique=True)URL-safe identifier, must be unique
descriptionStringField()Long-form product description
priceDecimalField(required=True, precision=2)Retail price, 2 decimal places
categoryStringField(required=True)Product category (e.g. "hoodies")
stockIntField(default=0)General fallback stock count
sizesListField(StringField())Available sizes, e.g. ["S", "M", "L", "XL"]
colorsListField(StringField())Available colors, e.g. ["negro", "blanco", "rojo"]
stock_by_variantDictField(default=dict)Per-variant inventory (see below)
imagesListField(StringField())Cloudinary image URLs
is_activeBooleanField(default=True)Soft visibility toggle
created_atDateTimeField(default=datetime.utcnow)Creation timestamp (UTC)

Per-Variant Inventory

The stock_by_variant field is a DictField that tracks stock independently for every combination of size and color. The key format is "size|color" — a pipe-separated string. This allows the catalog to report exact availability for each variant without needing a separate collection.
{
  "stock_by_variant": {
    "M|negro": 5,
    "M|blanco": 2,
    "L|negro": 0,
    "L|blanco": 8,
    "S|rojo": 3
  }
}
When a customer selects size M and color negro, the store checks stock_by_variant["M|negro"] to determine if that variant is in stock. If a variant key is not present in the dict, the backend falls back to the top-level stock field.
A value of 0 means that specific variant is sold out, even if other variants of the same product are available. The frontend should use stock_by_variant to disable out-of-stock size/color combinations in the product selector.

Filtering

GET /api/products/ supports four optional query parameters for filtering the active product list:

category

Filter by exact category string. ?category=hoodies

min_price

Return products priced at or above this value. ?min_price=50000

max_price

Return products priced at or below this value. ?max_price=200000

size

Return products that include this size in their sizes list. ?size=M
Parameters can be combined freely. Only products with is_active=True are ever returned regardless of filters.
GET /api/products/?category=hoodies&min_price=80000&size=L

Cloudinary Image Storage

Product images are not uploaded to or served from Django. Instead, images are uploaded to Cloudinary externally (via the admin panel or a separate upload flow), and the resulting Cloudinary URLs are stored in the images ListField. The images array may contain multiple URLs for a product gallery.
{
  "images": [
    "https://res.cloudinary.com/demo/image/upload/v1/urban-store/hoodie-negro-front.jpg",
    "https://res.cloudinary.com/demo/image/upload/v1/urban-store/hoodie-negro-back.jpg"
  ]
}

Admin vs. Public Access

Access control is enforced by the custom IsAdminMongo permission class, which decodes the incoming JWT and checks the is_admin flag in the payload — completely independent of DRF’s built-in authentication system.
MethodEndpointAuth Required
GET/api/products/None (public)
GET/api/products/<slug>/None (public)
POST/api/products/Admin JWT
PUT/api/products/<slug>/Admin JWT
DELETE/api/products/<slug>/Admin JWT
authentication_classes = [] is set on both views so DRF does not attempt to validate the token with SimpleJWT. The IsAdminMongo permission class handles token verification manually using PyJWT.

Example Product Response

GET /api/products/hoodie-urbano-negro/
{
  "id": "665f1a2b3c4d5e6f7a8b9c0d",
  "name": "Hoodie Urbano Negro",
  "slug": "hoodie-urbano-negro",
  "description": "Hoodie de algodón premium con capucha ajustable y bolsillo canguro.",
  "price": "149900.00",
  "category": "hoodies",
  "stock": 20,
  "sizes": ["S", "M", "L", "XL"],
  "colors": ["negro", "blanco"],
  "images": [
    "https://res.cloudinary.com/demo/image/upload/v1/urban-store/hoodie-negro.jpg"
  ],
  "stock_by_variant": {
    "S|negro": 3,
    "M|negro": 5,
    "L|negro": 0,
    "XL|negro": 2,
    "S|blanco": 4,
    "M|blanco": 6,
    "L|blanco": 1,
    "XL|blanco": 0
  }
}

Build docs developers (and LLMs) love