Freelancing for Pale Blue

Looking for flexible work opportunities that fit your schedule?


Download uploaded files only from Django Admin

Django Jul 25, 2022

You can relatively easily upload a file to Django and save it into a location in your filesystem using a Model. And it's quite standard to set up serving those uploaded files when users need to view them.

An exceptionally specific, yet I think common case is for those files to be only viewable by the admins. For instance, if the user needs to upload a verification document for the admins to review before granting the user full access.

In those cases, the standard process of setting up static file deployment might not be enough. Those uploaded media files should only be downloadable from the admin interface. Let's see how this can be accomplished using a custom admin view.

Prerequisites

Firstly, you would need to set up a custom admin site as described here.

Secondly, your Model should contain a FileField :

class CustomUserProfile(models.Model):
    # [...] Other fields
    id_proof = models.FileField(upload_to='id_proof')
models.py

View

You will need a view that will take as a parameter the primary key of the object and will return the bytes of the file. An HttpResponse with the file contents along with some additional headers will do the trick.

class IdProofView(View):

    def get(self, request, pk):
        user_profile = CustomUserProfile.objects.get(pk=pk)
        filename = os.path.basename(user_profile.id_proof.name)
        response = HttpResponse(user_profile.id_proof)
        response['Content-Disposition'] = \
        			f'attachment; filename={filename}'
        return response
views.py

Then wire it up in your custom admin's URL dispatcher (see this for how to set up a custom admin site). Notice that you are wrapping the call to your view with admin_view(). This will protect it from unauthorized access. Only staff members will be able to download the file which is the behavior we want.

class CustomAdminSite(AdminSite):

    def get_urls(self):
        urls = super().get_urls()
        my_urls = [
            # [...] Other views for your custom admin
            path('id_proof/<int:pk>', \
            self.admin_view(IdProofView.as_view()), name='id_proof'),
           
        ]
        return my_urls + urls
admin.py

ModelAdmin

Finally, we need to show the "secure" link to the file in the admin interface. We need a ModelAdmin that generates the link, if it exists, and shows it as a read-only property. Optionally, you can hide the default field in the ModelAdmin that will not work anyway, if you haven't set up media files serving.

class CustomUserProfileModelAdmin(admin.ModelAdmin):
    readonly_fields = ('id_proof_url',)
    exclude = ('id_proof',)

    def id_proof_url(self, instance):
        if instance.id_proof is None:
            return '-'
        return format_html(
            f'<a href="{reverse("admin:id_proof", \
            kwargs={"pk": instance.pk})}"target="_blank">Download</a>',
        )
        
admin.register(CustomUser, CustomUserModelAdmin)
admin.py

Hopefully, you got an idea of how to securely serve files defined in models to staff users via the admin interface.

Happy coding!

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.