Add cache to attachment urls

This commit is contained in:
Gabriel Augendre 2022-10-01 09:16:26 +02:00
parent 871d28f864
commit d7a7e65805
9 changed files with 174 additions and 6 deletions

View file

@ -0,0 +1,59 @@
# Generated by Django 4.1.1 on 2022-10-01 06:55
from django.apps.registry import Apps
from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.urls import reverse
def replace_with_wrapper_url(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
Attachment = apps.get_model("attachments", "Attachment")
Article = apps.get_model("articles", "Article")
db_alias = schema_editor.connection.alias
attachments = Attachment.objects.using(db_alias).all()
modified = []
for article in Article.objects.using(db_alias).all():
for attachment in attachments:
article.content = article.content.replace(
attachment.original_file.url,
reverse("attachments:original", kwargs={"pk": attachment.pk}),
)
if attachment.processed_file:
article.content = article.content.replace(
attachment.processed_file.url,
reverse("attachments:processed", kwargs={"pk": attachment.pk}),
)
modified.append(article)
Article.objects.using(db_alias).bulk_update(modified, ["content"])
def replace_with_file_url(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None:
Attachment = apps.get_model("attachments", "Attachment")
Article = apps.get_model("articles", "Article")
db_alias = schema_editor.connection.alias
attachments = Attachment.objects.using(db_alias).all()
modified = []
for article in Article.objects.using(db_alias).all():
for attachment in attachments:
article.content = article.content.replace(
reverse("attachments:original", kwargs={"pk": attachment.pk}),
attachment.original_file.url,
)
if attachment.processed_file:
article.content = article.content.replace(
reverse("attachments:processed", kwargs={"pk": attachment.pk}),
attachment.processed_file.url,
)
modified.append(article)
Article.objects.using(db_alias).bulk_update(modified, ["content"])
class Migration(migrations.Migration):
dependencies = [
("articles", "0033_alter_article_options"),
("attachments", "0008_attachment_updated_at"),
]
operations = [migrations.RunPython(replace_with_wrapper_url, replace_with_file_url)]

View file

@ -43,18 +43,16 @@ class AttachmentAdmin(admin.ModelAdmin):
def processed_file_url(self, instance: Attachment) -> str:
if instance.processed_file:
return format_html(
'{0} <a class="copy-button" data-to-copy="{1}" href="#">&#128203;</a>',
instance.processed_file.url,
instance.processed_file.url,
'{0} <a class="copy-button" data-to-copy="{0}" href="#">&#128203;</a>',
instance.processed_file_url,
)
return ""
def original_file_url(self, instance: Attachment) -> str:
if instance.original_file:
return format_html(
'{0} <a class="copy-button" data-to-copy="{1}" href="#">&#128203;</a>',
instance.original_file.url,
instance.original_file.url,
'{0} <a class="copy-button" data-to-copy="{0}" href="#">&#128203;</a>',
instance.original_file_url,
)
return ""

View file

@ -0,0 +1,18 @@
# Generated by Django 4.1.1 on 2022-10-01 06:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("attachments", "0007_auto_20201201_1917"),
]
operations = [
migrations.AddField(
model_name="attachment",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
]

View file

@ -11,6 +11,7 @@ from django.core.files import File
from django.core.handlers.wsgi import WSGIRequest
from django.db import models
from django.db.models.fields.files import FieldFile
from django.urls import reverse
from PIL import Image
from articles.utils import build_full_absolute_url
@ -35,6 +36,7 @@ class Attachment(models.Model):
original_file = AbsoluteUrlFileField()
processed_file = AbsoluteUrlFileField(blank=True, null=True)
open_graph_image = models.BooleanField(blank=True, default=False)
updated_at = models.DateTimeField(auto_now=True)
objects = AttachmentManager()
@ -48,6 +50,16 @@ class Attachment(models.Model):
self.processed_file = None # type: ignore
self.save()
@property
def original_file_url(self) -> str:
return reverse("attachments:original", kwargs={"pk": self.pk})
@property
def processed_file_url(self) -> str | None:
if self.processed_file:
return reverse("attachments:processed", kwargs={"pk": self.pk})
return None
def save(self, *args: Any, **kwargs: Any) -> None:
super().save(*args, **kwargs)

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,45 @@
from pathlib import Path
import pytest
from django.core.files import File
from django.test.client import Client
from django.urls import reverse
from attachments.models import Attachment
@pytest.mark.block_network()
@pytest.fixture()
def attachment(db: None) -> Attachment:
# This path manipulation is required to make the test run from this directory
# or from upper in the hierarchy (e.g.: settings.BASE_DIR)
original_path = Path(__file__).parent / "resources" / "image.png"
original_path = original_path.relative_to(Path.cwd())
processed_path = Path(__file__).parent / "resources" / "docker-logo.png"
processed_path = processed_path.relative_to(Path.cwd())
with original_path.open("rb") as of, processed_path.open("rb") as pf:
original_file = File(of)
processed_file = File(pf)
attachment = Attachment.objects.create(
description="Docker logo",
original_file=original_file,
processed_file=processed_file,
)
attachment.save()
return attachment
def test_view_original(attachment: Attachment, client: Client) -> None:
url = reverse("attachments:original", kwargs={"pk": attachment.pk})
res = client.get(url)
assert res.status_code == 302
assert res.url == attachment.original_file.url
assert "Last-Modified" in res.headers
def test_view_processed(attachment: Attachment, client: Client) -> None:
url = reverse("attachments:processed", kwargs={"pk": attachment.pk})
res = client.get(url)
assert res.status_code == 302
assert res.url == attachment.processed_file.url
assert "Last-Modified" in res.headers

10
src/attachments/urls.py Normal file
View file

@ -0,0 +1,10 @@
from django.urls import path
from attachments import views
app_name = "attachments"
urlpatterns = [
path("<int:pk>/original/", views.get_original, name="original"),
path("<int:pk>/processed/", views.get_processed, name="processed"),
]

25
src/attachments/views.py Normal file
View file

@ -0,0 +1,25 @@
import datetime
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.decorators.http import last_modified
from attachments.models import Attachment
def get_updated_at(request: WSGIRequest, pk: int) -> datetime.datetime:
attachment = get_object_or_404(Attachment, pk=pk)
return attachment.updated_at
@last_modified(get_updated_at)
def get_original(request: WSGIRequest, pk: int) -> HttpResponse:
attachment = get_object_or_404(Attachment, pk=pk)
return HttpResponseRedirect(attachment.original_file.url)
@last_modified(get_updated_at)
def get_processed(request: WSGIRequest, pk: int) -> HttpResponse:
attachment = get_object_or_404(Attachment, pk=pk)
return HttpResponseRedirect(attachment.processed_file.url)

View file

@ -32,6 +32,7 @@ urlpatterns = [
),
path("admin/", admin.site.urls),
path("", include("articles.urls")),
path("attachments/", include("attachments.urls")),
]
if settings.DEBUG: