RepuestosOriente
Accounts tied to tax IDs, view- and object-level permissions, orders split per seller with frozen prices, and infrastructure that costs $0/month in real production, with the cold start made visible instead of hidden.
The infrastructure behind this marketplace costs $0 a month. The price is paid in another currency: the server goes to sleep.
Auto parts with three roles, a checkout that produces one order per seller and a catalog with photos. Four free tiers, a product in real production, and the cold start made visible instead of hidden.
A request travels from the SPA to Postgres through two permission questions
Click the steps or let them advance. Each step lights up the piece it talks about.
company_name and rif: the marketplace is built for businesses buying from each other, and the catalog stays open to the public without login.A repair shop buys from two distributors in a single checkout
A made-up case with small numbers, marked as an example, to see what happens at each stage.
product.seller. Order 1 for Distribuidora Oriente ($36 + $12), order 2 for Repuestos Maturín ($20). Each OrderItem stores the price, original price and discount at that moment. Stock goes down and the cart is emptied.Five times the obvious solution was wrong
What anyone would do first, why it does not work, what was done, and the number that proves it. The technical detail stays folded.
Every free tier charges in something other than money: Render in wake-up latency, Supabase in a stateless pooler, R2 in URLs that look nothing like the local ones. The work was paying those tolls in the code, not on the invoice.
One filter: does this add a monthly bill or take time away from the product?
If it added a bill, the equivalent free tier was found. If it took time, whatever comes already solved was chosen.
Go deeper
The Django admin is the back office from day one: loading the catalog, verifying sellers, reviewing orders. Writing it by hand would have been half the project.
None instead of raising, so the authentication chain is not cut short.Go deeper
Tokens are stored in localStorage and a hook refreshes the access token five minutes before it expires, with a lock so two refreshes never run at once.
BaseModel sets deleted_at and reimplements Django's cascade by introspecting _meta.related_objects: CASCADE deletes down the chain, SET_NULL nulls the field, ManyToMany is cleared, and restore() undoes the cascade.Go deeper
A _deletion_context with the keys already visited avoids A→B→A cycles. Two managers per model: objects filters out deleted rows, all_objects does not. It is the most sophisticated code in the project and also the most fragile: any unique constraint can collide with a "deleted" row.
Go deeper
The split: Firebase Hosting serves the SPA from a CDN with a rewrite to index.html; Render builds and deploys the API on every push; Supabase provides Postgres with a pooler; R2 stores the photos with an S3 API and no egress fees; Whitenoise serves Django's static files without one more bucket.
The minimal version of the production data connection, as prod.py reads it: