Browse Source

fix: display original filename in links and serve files under runserver

- MetadataFieldFile.__str__: return original_filename from metadata so
  template links show the user-supplied name instead of the UUID path
- MetadataFileField.pre_save: capture original_filename from
  file_before.name (FieldFile attribute set to the upload name by
  FileDescriptor) instead of file_before.file.name (TemporaryUploadedFile
  temp-file path), which was producing wrong filenames
- serve_file: add settings.DEBUG branch that streams bytes via
  FileResponse so the dev server works; production path unchanged
  (X-Accel-Redirect)
- serve_image: same DEBUG fallback via FileResponse
- norma/normajuridica_detail.html: replace hardcoded /media/{{field}}
  href with {{p.anexo_arquivo.url}} so annexed-file links work now
  that /media/ is nginx-internal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
file-metafields
Edward Ribeiro 2 weeks ago
parent
commit
200ec9c9e5
  1. 19
      sapl/base/fields.py
  2. 30
      sapl/base/views.py
  3. 4
      sapl/templates/norma/normajuridica_detail.html

19
sapl/base/fields.py

@ -20,6 +20,18 @@ class MetadataFieldFile(FieldFile):
must use (see RFC §10).
"""
def __str__(self):
"""Return the original filename for display (not the UUID storage path)."""
if not self:
return ''
meta_attr = f'{self.field.name}_metadata'
meta = getattr(self.instance, meta_attr, None)
if meta and meta.original_filename:
return meta.original_filename
# Fallback: basename of storage path (may be UUID for newly uploaded files
# whose metadata row hasn't been committed yet).
return Path(self.name).name if self.name else ''
@property
def url(self):
if not self:
@ -161,8 +173,11 @@ class MetadataFileField(models.FileField):
is_clearing = not file_before and meta_before is not None
# Capture browser-supplied filename before storage renames it to the UUID path.
if has_new_upload and hasattr(file_before, 'file'):
original_filename = Path(file_before.file.name).name
# file_before.name is set to the original upload name by FileDescriptor.__get__
# when it wraps the UploadedFile — more reliable than file_before.file.name
# which for TemporaryUploadedFile is the NamedTemporaryFile path.
if has_new_upload:
original_filename = Path(file_before.name).name if file_before.name else ''
else:
original_filename = ''

30
sapl/base/views.py

@ -1575,9 +1575,11 @@ def pesquisa_textual(request):
# File-serving views (RFC §6.4, §9)
# ---------------------------------------------------------------------------
import os as _os # noqa: E402
from urllib.parse import quote # noqa: E402 — kept near usage site
from django.http import HttpResponse # noqa: E402
from django.http import FileResponse, HttpResponse # noqa: E402
SERVE_FILE_FIELDS = frozenset({
('materia', 'materialegislativa', 'texto_original'),
@ -1623,7 +1625,18 @@ def serve_file(request, file_uuid):
# When DocumentoAdministrativo.restrito / nivel_restricao is wired,
# insert the per-file check here (RFC §6.4).
# Build the nginx internal redirect path.
display_name = meta.original_filename or Path(meta.storage_name).name
if settings.DEBUG:
# runserver has no nginx: serve the bytes directly from the filesystem.
file_path = _os.path.join(settings.MEDIA_ROOT, meta.storage_name)
try:
fh = open(file_path, 'rb')
except OSError:
raise Http404
return FileResponse(fh, as_attachment=False, filename=display_name)
# Production: delegate byte transfer to nginx via X-Accel-Redirect.
# storage_name is relative to MEDIA_ROOT (e.g. "sapl/public/norma/…/file.pdf").
internal_path = f'/media/{meta.storage_name}'
@ -1631,8 +1644,8 @@ def serve_file(request, file_uuid):
response['X-Accel-Redirect'] = internal_path
# RFC 6266 — dual filename parameter: ASCII fallback + UTF-8 encoded.
filename_ascii = meta.original_filename.encode('ascii', 'replace').decode()
filename_encoded = quote(meta.original_filename, safe='')
filename_ascii = display_name.encode('ascii', 'replace').decode()
filename_encoded = quote(display_name, safe='')
response['Content-Disposition'] = (
f'inline; filename="{filename_ascii}"'
f"; filename*=UTF-8''{filename_encoded}"
@ -1699,6 +1712,15 @@ def serve_image(request, app_label, model_name, pk, field_name):
if not field_file:
raise Http404
if settings.DEBUG:
import os as _os
file_path = _os.path.join(settings.MEDIA_ROOT, field_file.name)
try:
fh = open(file_path, 'rb')
except OSError:
raise Http404
return FileResponse(fh)
response = HttpResponse()
response['X-Accel-Redirect'] = f'/media/{field_file.name}'
return response

4
sapl/templates/norma/normajuridica_detail.html

@ -78,8 +78,8 @@
{% if object.get_anexos_norma_juridica|length > 0 %}
{% for p in object.get_anexos_norma_juridica %}
<div class="form-control-static">
<a href="/media/{{p.anexo_arquivo}}">
{{ p.anexo_arquivo | to_str | split:"/" | get_last_item_from_list:-1 }}
<a href="{{ p.anexo_arquivo.url }}">
{{ p.anexo_arquivo }}
</a>
</div>
{% endfor %}

Loading…
Cancel
Save