Adds the foundational infrastructure for the file-serving redesign described
in docs/rfc/files-metafields.md.
What changed:
sapl/base/models.py
- Add FileMetadata model (db_table=base_file_metadata): stable uuid,
storage_name, original_filename, file_size_bytes, content_hash, version,
backfilled_at. The uuid is the stable public identifier that survives
file replacements; storage_name is the storage-backend key (relative path
or S3 object key).
sapl/base/fields.py (new)
- MetadataFileField: FileField subclass that injects a companion FK
(<fieldname>_metadata → base.FileMetadata) via contribute_to_class and
manages the FileMetadata row lifecycle in pre_save (create on first upload,
update-in-place on replacement keeping uuid stable, nullify+delete on
clear, no-op on re-save).
sapl/utils.py
- fabrica_validador_de_tipos_de_arquivo: also set __qualname__ = nome so
Django 2.2's migration serializer (which checks __qualname__ for '<locals>')
can resolve the function as sapl.utils.<name>.
14 models across 5 apps swapped FileField → MetadataFileField:
norma: NormaJuridica.texto_integral, AnexoNormaJuridica.anexo_arquivo
materia: MateriaLegislativa.texto_original, DocumentoAcessorio.arquivo,
Proposicao.texto_original
sessao: SessaoPlenaria.upload_{pauta,ata,anexo}, AbstractOrador.upload_anexo
(→ Orador, OradorExpediente, OradorOrdemDia), JustificativaAusencia
comissoes: Reuniao.upload_{pauta,ata,anexo}, DocumentoAcessorio.arquivo
audiencia: AudienciaPublica.upload_{pauta,ata,anexo},
AnexoAudienciaPublica.arquivo
protocoloadm: DocumentoAdministrativo.texto_integral,
DocumentoAcessorioAdministrativo.arquivo
6 migrations adding <fieldname>_metadata FK columns (AddField, pure SQL;
no existing rows touched).
sapl/api/views_base.py
- Remove FileMetadata from the auto-built API set after build_class to
prevent UUID enumeration and storage_name leakage (RFC §11.3).
Not yet implemented (follow-up commits):
- serve_file / serve_model_file views (RFC §6.4, §9)
- MetadataFileField.url() returning semantic aliases (RFC §9)
- MetadataOverwriteStorage with UUID-based paths (RFC §6.3)
- backfill_file_metadata management command (RFC §8)
- SaplSerializerMixin integration for API canonical URLs (RFC §10)
- nginx /media/ internal + ConditionalGetMiddleware (RFC §6.4)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>