Codebase guide
The master map for building a feature end to end on this Django 5.2 / DRF 3.16 + Next.js stack — the model to serializer to ViewSet to urls/router to tests pipeline, every step tenant-scoped by the entreprise FK and gated by {resource}_{action} RBAC. Use when starting a new feature, adding an endpoint or app, onboarding to the conventions, asking "how is a feature built here" or "where does this go", or reviewing that a change follows house rules. Points to specialist skills for depth. Not for the deep mechanics of one layer — see multi-tenancy, drf-api, rbac-permissions, migrations, or write-tests for that.From its SKILL.md
npx -y skills add Deadlymind/nanolama --skill codebase-guideAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 27 days oldThe repository was created 27 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.1 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it
Codebase guide (how a feature is built end to end)
When to use
Starting any new feature, endpoint, or app, or checking that a change follows the house conventions. This is the map; the specialist skills are the terrain. Read it first, then dive into the layer you are touching.
Pattern
Every business feature flows through the same five files, in order:
models.py (tenant FK) → serializers.py (field shape) → views.py/ViewSet
(scope + RBAC) → urls.py/router (route name) → tests.py (isolation test).
Two invariants ride along every step and are non-negotiable on a new endpoint:
- Tenant scope — the row is filtered/stamped by the current
entreprise(fail-closed), never trusted from the client (seemulti-tenancy). - RBAC gate — access is gated by a
{resource}_{action}permission (seerbac-permissions).
A worked example, one file per step (migration follows the model — see migrations):
# invoices/models.py
class Invoice(models.Model):
entreprise = models.ForeignKey( # rename to your tenant FK
"tenants.Entreprise", on_delete=models.CASCADE,
null=False, related_name="invoices")
number = models.CharField(max_length=32)
class Meta:
indexes = [models.Index(fields=["entreprise"])] # every filter hits it
# invoices/serializers.py (see drf-api)
class InvoiceSerializer(serializers.ModelSerializer):
class Meta:
model = Invoice
fields = ["id", "number", "entreprise"]
read_only_fields = ["entreprise"] # server stamps it, not the client
# invoices/views.py — both invariants enforced here
class InvoiceViewSet(TenantScopedViewSet): # get_queryset filters by tenant
serializer_class = InvoiceSerializer
queryset = Invoice.objects.all()
permission_classes = [HasResourcePermission] # gates by invoice_{action}
perm_base = "invoice" # matches rbac-permissions -> invoice_read/create/...
# invoices/urls.py — basename gives reversible route names; mount under /api/
router = DefaultRouter()
router.register("invoices", InvoiceViewSet, basename="invoice")
# invoices/tests.py — the mandatory tenant-isolation test (see write-tests)
def test_tenant_isolation(self):
self.client.cookies = self.user_a_cookies # user A must NOT see B's invoice
resp = self.client.get(f"/api/invoices/{self.invoice_b.id}/")
assert resp.status_code == 404 # scoped queryset -> 404, not 403
Conventions on every change:
- New endpoint ⇒ tenant filter and RBAC permission, both. A filter with no gate leaks within the tenant; a gate with no filter leaks across tenants.
- One app per bounded resource; keep
models / serializers / views / urls / testssplit as above rather than one mega-file. - Server owns the tenant:
read_only_fieldson the FK, stamped inperform_create. Stamp the audit trail in the same override —perform_createsetscreated_by(andupdated_by) alongside the tenant,perform_updatesetsupdated_by— from the request user, never the client. Newcomers wire the tenant and forget the who. - Tests include the isolation case for every scoped resource — it is the one test reviewers block a PR for missing.
Adapt to your repo
Rename Entreprise/entreprise and the accessor (user.entreprise_id) to your
tenant model. Adjust the resource string and permission class to your RBAC helper's
names. Confirm your project urls.py mounts app routers under a stable prefix
(/api/). Frontend features mirror this on the Next.js side — see nextjs-module.
Gotchas
- Skipping the router basename breaks
reverse()and DRF'sHyperlinkedIdentityField. Model.objects.get(pk=...)inside a view bypasses tenant scoping — always go through the scopedget_queryset()/get_object().- Custom
@actionmethods do not inherit the RBAC check automatically — gate each one. - A migration that adds a
null=Falsetenant FK to a populated table needs a default or a data migration (seemigrations). - Business rows are soft-deleted, not hard-
DELETEd. Overridedestroy()to set a flag (archived_at/is_deleted) instead of removing the row, and exclude flagged rows from the defaultget_queryset()so they drop out of lists and 404 on detail while staying recoverable. A realDELETEloses the audit trail and breaks FKs.
See also
multi-tenancydrf-apirbac-permissionsmigrationswrite-tests
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.