Last active
December 2, 2023 17:01
-
-
Save kristinriebe/33f5f0419ef210fad2c47ab94c63ac5a to your computer and use it in GitHub Desktop.
Wagtail - use a custom url (document slug) to serve documents via a custom serve_view
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I prefer to serve documents using a document slug (derived from the title of the document) | |
# instead of using the filename, especially since filenames can become quite ugly (appended random strings). | |
# E.g. a file named 'campus_map.pdf' with titel 'Campus Map' may be uploaded as 'campus_map_asRfsgt.pdf' | |
# by Wagtail and is then served as e.g. '/documents/24/campus_map_asRfsgt.pdf' (if WAGTAILDOCS_SERVE_METHOD = 'serve_view'). | |
# However, it would look much nicer, if it was served as '/documents/24/campus_map/', which could | |
# also be a more persistent document url, i.e. it would not change if the document object was changed by | |
# replacing the file with a new version (e.g. 'campus_map_v2.pdf'). | |
# I could also shorten the URL to '/documents/campus_map/', omitting the id. However, lookups by id are | |
# much faster and we would need to ensure that these urls are unique. | |
# In this example, I am using 'mydocuments' as the app name, adjust it to the app where you store your new | |
# views.py and urls.py for the documents. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# In your settings file (could also be named differently), define the custom document | |
# model and the serve method | |
# ... | |
WAGTAILDOCS_DOCUMENT_MODEL = 'mydocuments.CustomDocument' | |
WAGTAILDOCS_SERVE_METHOD = 'serve_view' | |
# ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# In your global urls.py include the wagtail_serve urls | |
from django.conf.urls import url | |
from django.urls import include | |
# from wagtail.documents import urls as wagtaildocs_urls | |
from mydocuments import urls as docs_urls | |
# ... | |
urlpatterns = [ | |
# ... | |
# url(r'^documents/', include(wagtaildocs_urls)), | |
url(r'^documents/', include(docs_urls)), | |
# ... | |
] | |
# ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# We need a custom document model so that we can redefine its url method | |
class CustomDocument(AbstractDocument): | |
# put your custom properties here; | |
# the slug could also be added as a property, put into admin fields and indexed, | |
# so it does not have to be calculated again each time. | |
# ... | |
# redefine the url method of AbstractDocument, so it uses a slugified title | |
# instead of the file name; if you change this, make sure to change the serve-view | |
# and the urls.py above as well | |
@property | |
def url(self): | |
if getattr(settings, 'WAGTAILDOCS_SERVE_METHOD', None) == 'direct': | |
try: | |
return self.file.url | |
except NotImplementedError: | |
# backend does not provide a url, so fall back on the serve view | |
pass | |
slug = slugify(self.title) | |
return reverse('wagtaildocs_serve', args=[self.id, slug]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Change the wagtaildocs_serve urls to suit our needs | |
from django.urls import path | |
from mydocuments import views | |
urlpatterns = [ | |
path('<int:document_id>/<slug:document_slug>/', views.serve, name='wagtaildocs_serve'), | |
# keep the entry below as in the original version in wagtail/documents/urls.py | |
path( | |
'authenticate_with_password/<int:restriction_id>/', | |
serve.authenticate_with_password, | |
name='wagtaildocs_authenticate_with_password'), | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The following lines are copied from wagtail/documents/views/serve.py, v. 2.11.2 | |
# Use in document_etag and in the serve-view the new document_slug instead of the filename. | |
from wsgiref.util import FileWrapper | |
from django.conf import settings | |
from django.http import Http404, HttpResponse, StreamingHttpResponse | |
from django.shortcuts import get_object_or_404, redirect | |
from django.utils.text import slugify | |
from django.views.decorators.cache import cache_control | |
from django.views.decorators.http import etag | |
from wagtail.core import hooks | |
from wagtail.documents import get_document_model | |
from wagtail.documents.models import document_served | |
from wagtail.utils import sendfile_streaming_backend | |
from wagtail.utils.sendfile import sendfile | |
def document_etag(request, document_id, document_slug): | |
Document = get_document_model() | |
if hasattr(Document, 'file_hash'): | |
return Document.objects.filter(id=document_id).values_list('file_hash', flat=True).first() | |
@etag(document_etag) | |
@cache_control(max_age=3600, public=True) | |
def serve(request, document_id, document_slug): | |
Document = get_document_model() | |
doc = get_object_or_404(Document, id=document_id) | |
# We want to ensure that the document filename provided in the URL matches the one associated with the considered | |
# document_id. If not we can't be sure that the document the user wants to access is the one corresponding to the | |
# <document_id, document_slug> pair. | |
if slugify(doc.title) != document_slug: | |
raise Http404('This document does not match the given file slug.') | |
for fn in hooks.get_hooks('before_serve_document'): | |
# ... | |
# continue with the remaining code in this function; use the up-to-date code from your Wagtail version | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment