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: def processed_file_url(self, instance: Attachment) -> str:
if instance.processed_file: if instance.processed_file:
return format_html( return format_html(
'{0} <a class="copy-button" data-to-copy="{1}" href="#">&#128203;</a>', '{0} <a class="copy-button" data-to-copy="{0}" href="#">&#128203;</a>',
instance.processed_file.url, instance.processed_file_url,
instance.processed_file.url,
) )
return "" return ""
def original_file_url(self, instance: Attachment) -> str: def original_file_url(self, instance: Attachment) -> str:
if instance.original_file: if instance.original_file:
return format_html( return format_html(
'{0} <a class="copy-button" data-to-copy="{1}" href="#">&#128203;</a>', '{0} <a class="copy-button" data-to-copy="{0}" href="#">&#128203;</a>',
instance.original_file.url, instance.original_file_url,
instance.original_file.url,
) )
return "" 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.core.handlers.wsgi import WSGIRequest
from django.db import models from django.db import models
from django.db.models.fields.files import FieldFile from django.db.models.fields.files import FieldFile
from django.urls import reverse
from PIL import Image from PIL import Image
from articles.utils import build_full_absolute_url from articles.utils import build_full_absolute_url
@ -35,6 +36,7 @@ class Attachment(models.Model):
original_file = AbsoluteUrlFileField() original_file = AbsoluteUrlFileField()
processed_file = AbsoluteUrlFileField(blank=True, null=True) processed_file = AbsoluteUrlFileField(blank=True, null=True)
open_graph_image = models.BooleanField(blank=True, default=False) open_graph_image = models.BooleanField(blank=True, default=False)
updated_at = models.DateTimeField(auto_now=True)
objects = AttachmentManager() objects = AttachmentManager()
@ -48,6 +50,16 @@ class Attachment(models.Model):
self.processed_file = None # type: ignore self.processed_file = None # type: ignore
self.save() 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: def save(self, *args: Any, **kwargs: Any) -> None:
super().save(*args, **kwargs) 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("admin/", admin.site.urls),
path("", include("articles.urls")), path("", include("articles.urls")),
path("attachments/", include("attachments.urls")),
] ]
if settings.DEBUG: if settings.DEBUG: