From eeb1b069fced9b88763151f1355f2407e24c68e9 Mon Sep 17 00:00:00 2001 From: Harald Nezbeda Date: Mon, 14 Feb 2022 17:15:07 +0100 Subject: [PATCH] Make tranlatable context optional, extend readme for django admin case --- README.md | 47 ++++++++++++++++++++++++++------ drf_attachments/admin.py | 28 ++++++++++--------- drf_attachments/models/models.py | 9 ++++-- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c1ab591..9177c01 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # DRF Attachments -Django rest framework module to manage any model's file up-/downloads by relating an Attachment model to it. +Django module to manage any model's file up-/downloads by relating an Attachment model to it. +The module can be used for extending the Django Admin or for exposing the attachment via Django REST Framework. -## Installation +## Installation and Setup 1. Install using pip: @@ -20,7 +21,37 @@ INSTALLED_APPS = [ ] ``` -3. Add a helper/utils file to your project's source code (e.g. `attachments.py`) and prepare the methods `attachment_content_object_field`, `attachment_context_translatables`, `filter_viewable_content_types`, `filter_editable_content_types` and `filter_deletable_content_types` there. +## Usage with Django Admin + +1. Add minimal settings + +```python +# drf-attachment settings +ATTACHMENT_MAX_UPLOAD_SIZE = 1024 * 1024 * 25 +ATTACHMENT_CONTEXT_CONTEXT_1 = "CONTEXT-1" +ATTACHMENT_CONTEXT_CONTEXT_2 = "CONTEXT-2" +``` + +2. Add `AttachmentInlineAdmin` or `ReadOnlyAttachmentInlineAdmin` to each model that needs attachment + +```python +from django.contrib import admin +from drf_attachments.admin import AttachmentInlineAdmin + +@admin.register(MyModel) +class MyModelAdmin(admin.ModelAdmin): + inlines = [ + AttachmentInlineAdmin, + ] +``` + +`ReadOnlyAttachmentInlineAdmin` is useful when attachments should be provided only by REST API. You may consider +extending the classes in order to handle additional permission checks. + +## Usage with DRF (ToDo: API needs to be simplified) + + +1. Add a helper/utils file to your project's source code (e.g. `attachments.py`) and prepare the methods `attachment_content_object_field`, `attachment_context_translatables`, `filter_viewable_content_types`, `filter_editable_content_types` and `filter_deletable_content_types` there. ``` # within app/your_app_name/attachments.py @@ -62,7 +93,7 @@ def filter_deletable_content_types(queryset): ``` -4. Define the helper/utils methods' paths within your `settings.py` as `ATTACHMENT_CONTENT_OBJECT_FIELD_CALLABLE` and `ATTACHMENT_CONTEXT_TRANSLATABLES_CALLABLE`: +2. Define the helper/utils methods' paths within your `settings.py` as `ATTACHMENT_CONTENT_OBJECT_FIELD_CALLABLE` and `ATTACHMENT_CONTEXT_TRANSLATABLES_CALLABLE`: ``` # within settings.py @@ -70,14 +101,14 @@ ATTACHMENT_CONTENT_OBJECT_FIELD_CALLABLE = "your_app_name.attachments.attachment ATTACHMENT_CONTEXT_TRANSLATABLES_CALLABLE = "your_app_name.attachments.attachment_context_translatables" ``` -5. Add the `ATTACHMENT_MAX_UPLOAD_SIZE` to your `settings.py`: +3. Add the `ATTACHMENT_MAX_UPLOAD_SIZE` to your `settings.py`: ``` # within settings.py ATTACHMENT_MAX_UPLOAD_SIZE = env.int("ATTACHMENT_MAX_UPLOAD_SIZE", default=1024 * 1024 * 25) ``` -6. Define any context choices your attachment files might represent (e.g. "driver's license", "offer", "contract", ...) as `ATTACHMENT_CONTEXT_CHOICES` in the `settings.py`. We recommend defining each context type as constant before adding them to the "choices" dict, e.g.: +4. Define any context choices your attachment files might represent (e.g. "driver's license", "offer", "contract", ...) as `ATTACHMENT_CONTEXT_CHOICES` in the `settings.py`. We recommend defining each context type as constant before adding them to the "choices" dict, e.g.: ``` # within settings.py @@ -87,14 +118,14 @@ ATTACHMENT_CONTEXT_CONTRACT = 'CONTRACT' ATTACHMENT_CONTEXT_OTHER = 'OTHER' ``` -7. Optionally define a default context in the `settings.py` that will be set automatically each time you save an attachment without explicitly defining a context yourself, e.g.: +5. Optionally define a default context in the `settings.py` that will be set automatically each time you save an attachment without explicitly defining a context yourself, e.g.: ``` # within settings.py ... ATTACHMENT_DEFAULT_CONTEXT = 'ATTACHMENT' ``` -8. Add all possible context choices (and the default value, if defined) to the `attachment_context_translatables` method to make them translatable and detectable via the `makemessages` command, e.g.: +6. Add all possible context choices (and the default value, if defined) to the `attachment_context_translatables` method to make them translatable and detectable via the `makemessages` command, e.g.: ``` # within app/your_app_name/attachments.py diff --git a/drf_attachments/admin.py b/drf_attachments/admin.py index 72cadaa..5aa11b1 100644 --- a/drf_attachments/admin.py +++ b/drf_attachments/admin.py @@ -1,10 +1,8 @@ from django.contrib import admin from django.contrib.contenttypes.admin import GenericTabularInline from django.forms import ChoiceField, ModelForm -from django.forms.utils import ErrorList -from django.utils.translation import ugettext_lazy as _ -from drf_attachments.models.models import Attachment, attachment_context_choices, attachment_context_translatable +from drf_attachments.models.models import Attachment, attachment_context_choices __all__ = [ "AttachmentInlineAdmin", @@ -51,13 +49,11 @@ class AttachmentAdmin(admin.ModelAdmin, AttachmentAdminMixin): ) -class AttachmentInlineAdmin(GenericTabularInline, AttachmentAdminMixin): +class BaseAttachmentInlineAdmin(GenericTabularInline, AttachmentAdminMixin): model = Attachment extra = 0 fields = ( - "context_label", "name", - "download_url", "file", "size", "mime_type", @@ -65,19 +61,25 @@ class AttachmentInlineAdmin(GenericTabularInline, AttachmentAdminMixin): "creation_date", ) readonly_fields = ( - "context_label", - "name", - "download_url", - "file", "size", "mime_type", "extension", "creation_date", ) - show_change_link = False + class Media: + css = {"": ("attachments/attachment_admin.css",)} + + +class AttachmentInlineAdmin(BaseAttachmentInlineAdmin): + def has_change_permission(self, request, obj=None): + return False + + show_change_link = True + + +class ReadOnlyAttachmentInlineAdmin(AttachmentInlineAdmin): def has_add_permission(self, request, obj=None): return False - class Media: - css = {"": ("attachments/attachment_admin.css",)} + show_change_link = False diff --git a/drf_attachments/models/models.py b/drf_attachments/models/models.py index 7faaaa0..34c3498 100644 --- a/drf_attachments/models/models.py +++ b/drf_attachments/models/models.py @@ -33,9 +33,12 @@ def __init__(self): def get_context_translatables(): - module_name, callable_name = settings.ATTACHMENT_CONTEXT_TRANSLATABLES_CALLABLE.rsplit('.', maxsplit=1) - backend_module = importlib.import_module(module_name) - return getattr(backend_module, callable_name)() + context_translatables_callable = getattr(settings, "ATTACHMENT_CONTEXT_TRANSLATABLES_CALLABLE", None) + if context_translatables_callable: + module_name, callable_name = settings.ATTACHMENT_CONTEXT_TRANSLATABLES_CALLABLE.rsplit('.', maxsplit=1) + backend_module = importlib.import_module(module_name) + return getattr(backend_module, callable_name)() + return {} def attachment_upload_path(attachment, filename):