Repository navigation
py-v1.14.0 - migration-risk: 407 criticals on six big Django codebases, 2 real
·
16 commits
to main
since this release
Fixed
-
migration-risk: 407 criticals on six big Django codebases, 2 real.
Run over NetBox, PostHog, authentik, Plane, paperless-ngx and Healthchecks,
it reported 407 critical findings, and 2 of them were real. The rules now
follow what Django actually does to existing rows:ManyToManyField,TaggableManagerandGenericRelationadd no column,
so they are never a NOT NULL risk.blank=Trueon a text field, including project subclasses such as
ColorField(max_length=6), gets"".auto_now/auto_now_addget the
current time, anddb_defaultandGeneratedFieldfill themselves.- A table created earlier in the same migration is empty. A squashed
migration (replaces = [...]) only ever runs on a fresh database. unique=Truewithnull=Trueand no default, ordefault=None, fills
rows with NULL, which never collides.- A field removed and then re-added, renamed onto the same name, or a model
deleted and re-created, in that migration or a later one, is no longer
"still referenced". - The migration graph collapses squashed migrations the way Django's loader
does, so a squash and the migrations it replaces are not reported as
conflicting leaves. - A NOT NULL column added after a
RunPython, or after aRunSQLthat
deletes, updates or truncates rows, is a warning instead of a certain
critical, because that step may have emptied the table.
-
nplusonereported writes and test code as N+1 reads. Checked against
NetBox, PostHog, authentik, Plane, paperless-ngx and Healthchecks:post.tags.set(...),.add(),.remove(),.clear(),.create()and
.get_or_create()on a relation were caught by the attribute-chain
fallback and given a prefetch, which cannot serve a write. They are not
reported now. Loading a single-valued relation before writing to it
(profile.user.delete()) is still an N+1, and its fix is
select_related. QuerySet writes such asupdate()anddelete()on a
related manager stay findings, as before.- One field is never suggested in both
select_relatedand
prefetch_related..select_related("user").prefetch_related("user")
came from a schema-less guess onx.user.delete(). - Test modules (
test_*.py,*_test.py,tests.py,conftest.py, and
anything undertests/ortest/) are skipped, because a query per row
in a test is not a production N+1. Pass--include-teststo scan them.
This also halves the scan time on large repositories (PostHog: 80 s to
39 s).