Git Product home page Git Product logo

django-minio-backend's Introduction

django-app-tests publish-py-dist-to-pypi PYPI

django-minio-backend

The django-minio-backend provides a wrapper around the MinIO Python SDK. See minio/minio-py for the source.

Integration

  1. Get and install the package:
pip install django-minio-backend
  1. Add django_minio_backend to INSTALLED_APPS:
INSTALLED_APPS = [
    # '...'
    'django_minio_backend',  # https://github.com/theriverman/django-minio-backend
]

If you would like to enable on-start consistency check, install via DjangoMinioBackendConfig:

INSTALLED_APPS = [
    # '...'
    'django_minio_backend.apps.DjangoMinioBackendConfig',  # https://github.com/theriverman/django-minio-backend
]

Then add the following parameter to your settings file:

MINIO_CONSISTENCY_CHECK_ON_START = True

Note: The on-start consistency check equals to manually calling python manage.py initialize_buckets.
It is recommended to turn off this feature during development by setting MINIO_CONSISTENCY_CHECK_ON_START to False, because this operation can noticeably slow down Django's boot time when many buckets are configured.

  1. Add the following parameters to your settings.py:
from datetime import timedelta
from typing import List, Tuple

MINIO_ENDPOINT = 'minio.your-company.co.uk'
MINIO_EXTERNAL_ENDPOINT = "external-minio.your-company.co.uk"  # Default is same as MINIO_ENDPOINT
MINIO_EXTERNAL_ENDPOINT_USE_HTTPS = True  # Default is same as MINIO_USE_HTTPS
MINIO_REGION = 'us-east-1'  # Default is set to None
MINIO_ACCESS_KEY = 'yourMinioAccessKey'
MINIO_SECRET_KEY = 'yourVeryS3cr3tP4ssw0rd'
MINIO_USE_HTTPS = True
MINIO_URL_EXPIRY_HOURS = timedelta(days=1)  # Default is 7 days (longest) if not defined
MINIO_CONSISTENCY_CHECK_ON_START = True
MINIO_PRIVATE_BUCKETS = [
    'django-backend-dev-private',
]
MINIO_PUBLIC_BUCKETS = [
    'django-backend-dev-public',
]
MINIO_POLICY_HOOKS: List[Tuple[str, dict]] = []
# MINIO_MEDIA_FILES_BUCKET = 'my-media-files-bucket'  # replacement for MEDIA_ROOT
# MINIO_STATIC_FILES_BUCKET = 'my-static-files-bucket'  # replacement for STATIC_ROOT
MINIO_BUCKET_CHECK_ON_SAVE = True  # Default: True // Creates bucket if missing, then save

# Custom HTTP Client (OPTIONAL)
import os
import certifi
import urllib3
timeout = timedelta(minutes=5).seconds
ca_certs = os.environ.get('SSL_CERT_FILE') or certifi.where()
MINIO_HTTP_CLIENT: urllib3.poolmanager.PoolManager = urllib3.PoolManager(
    timeout=urllib3.util.Timeout(connect=timeout, read=timeout),
    maxsize=10,
    cert_reqs='CERT_REQUIRED',
    ca_certs=ca_certs,
    retries=urllib3.Retry(
        total=5,
        backoff_factor=0.2,
        status_forcelist=[500, 502, 503, 504]
    )
)
  1. Implement your own Attachment handler and integrate django-minio-backend:
from django.db import models
from django_minio_backend import MinioBackend, iso_date_prefix

class PrivateAttachment(models.Model):   
    file = models.FileField(verbose_name="Object Upload",
                            storage=MinioBackend(bucket_name='django-backend-dev-private'),
                            upload_to=iso_date_prefix)
  1. Initialize the buckets & set their public policy (OPTIONAL):
    This django-admin command creates both the private and public buckets in case one of them does not exists, and sets the public bucket's privacy policy from private(default) to public.
python manage.py initialize_buckets

Code reference: initialize_buckets.py.

Static Files Support

django-minio-backend allows serving static files from MinIO. To learn more about Django static files, see Managing static files, and STATICFILES_STORAGE.

To enable static files support, update your settings.py:

STATICFILES_STORAGE = 'django_minio_backend.models.MinioBackendStatic'
MINIO_STATIC_FILES_BUCKET = 'my-static-files-bucket'  # replacement for STATIC_ROOT
# Add the value of MINIO_STATIC_FILES_BUCKET to one of the pre-configured bucket lists. eg.:
# MINIO_PRIVATE_BUCKETS.append(MINIO_STATIC_FILES_BUCKET)
# MINIO_PUBLIC_BUCKETS.append(MINIO_STATIC_FILES_BUCKET)

The value of STATIC_URL is ignored, but it must be defined otherwise Django will throw an error.

IMPORTANT
The value set in MINIO_STATIC_FILES_BUCKET must be added either to MINIO_PRIVATE_BUCKETS or MINIO_PUBLIC_BUCKETS, otherwise django-minio-backend will raise an exception. This setting determines the privacy of generated file URLs which can be unsigned public or signed private.

Note: If MINIO_STATIC_FILES_BUCKET is not set, the default value (auto-generated-bucket-static-files) will be used. Policy setting for default buckets is private.

Default File Storage Support

django-minio-backend can be configured as a default file storage. To learn more, see DEFAULT_FILE_STORAGE.

To configure django-minio-backend as the default file storage, update your settings.py:

DEFAULT_FILE_STORAGE = 'django_minio_backend.models.MinioBackend'
MINIO_MEDIA_FILES_BUCKET = 'my-media-files-bucket'  # replacement for MEDIA_ROOT
# Add the value of MINIO_STATIC_FILES_BUCKET to one of the pre-configured bucket lists. eg.:
# MINIO_PRIVATE_BUCKETS.append(MINIO_STATIC_FILES_BUCKET)
# MINIO_PUBLIC_BUCKETS.append(MINIO_STATIC_FILES_BUCKET)

The value of MEDIA_URL is ignored, but it must be defined otherwise Django will throw an error.

IMPORTANT
The value set in MINIO_MEDIA_FILES_BUCKET must be added either to MINIO_PRIVATE_BUCKETS or MINIO_PUBLIC_BUCKETS, otherwise django-minio-backend will raise an exception. This setting determines the privacy of generated file URLs which can be unsigned public or signed private.

Note: If MINIO_MEDIA_FILES_BUCKET is not set, the default value (auto-generated-bucket-media-files) will be used. Policy setting for default buckets is private.

Health Check

To check the connection link between Django and MinIO, use the provided MinioBackend.is_minio_available() method.
It returns a MinioServerStatus instance which can be quickly evaluated as boolean.

Example:

from django_minio_backend import MinioBackend

minio_available = MinioBackend().is_minio_available()  # An empty string is fine this time
if minio_available:
    print("OK")
else:
    print("NOK")
    print(minio_available.details)

Policy Hooks

You can configure django-minio-backend to automatically execute a set of pre-defined policy hooks.
Policy hooks can be defined in settings.py by adding MINIO_POLICY_HOOKS which must be a list of tuples.
Policy hooks are automatically picked up by the initialize_buckets management command.

For an exemplary policy, see the implementation of def set_bucket_to_public(self) in django_minio_backend/models.py or the contents of examples/policy_hook.example.py.

Consistency Check On Start

When enabled, the initialize_buckets management command gets called automatically when Django starts.
This command connects to the configured minIO server and checks if all buckets defined in settings.py.
In case a bucket is missing or its configuration differs, it gets created and corrected.

Reference Implementation

For a reference implementation, see Examples.

Behaviour

The following list summarises the key characteristics of django-minio-backend:

  • Bucket existence is not checked on a save by default. To enable this guard, set MINIO_BUCKET_CHECK_ON_SAVE = True in your settings.py.
  • Bucket existences are not checked on Django start by default. To enable this guard, set MINIO_CONSISTENCY_CHECK_ON_START = True in your settings.py.
  • Many configuration errors are validated through AppConfig but not every error can be captured there.
  • Files with the same name in the same bucket are not replaced on save by default. Django will store the newer file with an altered file name To allow replacing existing files, pass the replace_existing=True kwarg to MinioBackend. For example: image = models.ImageField(storage=MinioBackend(bucket_name='images-public', replace_existing=True))
  • Depending on your configuration, django-minio-backend may communicate over two kind of interfaces: internal and external. If your settings.py defines a different value for MINIO_ENDPOINT and MINIO_EXTERNAL_ENDPOINT, then the former will be used for internal communication between Django and MinIO, and the latter for generating URLs for users. This behaviour optimises the network communication. See Networking below for a thorough explanation
  • The uploaded object's content-type is guessed during save. If mimetypes.guess_type fails to determine the correct content-type, then it falls back to application/octet-stream.

Networking and Docker

If your Django application is running on a shared host with your MinIO instance, you should consider using the MINIO_EXTERNAL_ENDPOINT and MINIO_EXTERNAL_ENDPOINT_USE_HTTPS parameters. This way most traffic will happen internally between Django and MinIO. The external endpoint parameters are required for external pre-signed URL generation.

If your Django application and MinIO instance are running on different hosts, you can omit the MINIO_EXTERNAL_ENDPOINT and MINIO_EXTERNAL_ENDPOINT_USE_HTTPS parameters, and django-minio-backend will default to the value of MINIO_ENDPOINT.

Setting up and configuring custom networks in Docker is not in the scope of this document.
To learn more about Docker networking, see Networking overview and Networking in Compose.

See README.Docker.md for a real-life Docker Compose demonstration.

Compatibility

  • Django 3.2 or later
  • Python 3.8.0 or later
  • MinIO SDK 7.0.2 or later

Contribution

Please find the details in CONTRIBUTE.md

Copyright

  • theriverman/django-minio-backend licensed under the MIT License
  • minio/minio-py is licensed under the Apache License 2.0

django-minio-backend's People

Contributors

abhi1693 avatar dependabot[bot] avatar disonds avatar marcelfox avatar patvdleer avatar subho007 avatar theriverman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

django-minio-backend's Issues

For public files, click on their link does not start download immediately

Hello,

In Django admin, for private files clicking on the file link immediately starts to download the file. But for public files clicking on the link transfer user to the Minio dashboard. The user has to click on the download button to download the file.

I want links for public files to be the same as private files. (immediately start download after clicking on them)
How can I achieve this?

Thanks.

Download url not working

Hi,
If the bucket is public then the download URL from the Django admin is working well, but if the bucket is private then the URL is not working.
image

here is the URL pattern I received when I click on admin items

https://yourdomain.com/dmc/2023-2-7/dddddd.tif?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxx/20230207/us-east-1/s3/aws4_request&X-Amz-Date=20230207T085042Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host&X-Amz-Signature=d42c4c9980ba06393192e1b1bdc2b3ab2011be447424f3c064e51d5a48e37898

it is also strange that the download URL passes the credential as it is :(

Does anyone try the download option for the private bucket?

Getting ConnectionError in tox environment from call to check_bucket_existence when using static files storage

Note: I'm only running into this issue in my CI/CD pipeline, because I am using tox to create an isolated python 3.8 environment to run all my django/python unit tests.

In my settings.py I set STATIC_FILES_STORAGE to use MinioBackendStatic to serve all my static files from my minio server, because of this I get a ConnectionError when the tox environment starts up, this library makes a call to check the buckets exist at django_minio_backend/apps.py#L32.

        # Validate static storage and default storage configurations
        staticfiles_storage: str = get_setting('STATICFILES_STORAGE')
        if staticfiles_storage.endswith(MinioBackendStatic.__name__):
            mbs = MinioBackendStatic()
            mbs.check_bucket_existence()

Screenshot of stacktrace on startup

Screen Shot 2022-09-20 at 10 24 50 AM

Because this is an isolated python environment specifically used for testing, it can't really reach out to ensure any buckets exist. By looking at the source code, it doesn't seem like there's a way to prevent the call to mbs.check_bucket_existence() unless I don't set STATICFILES_STORAGE = "django_minio_backend.models.MinioBackendStatic" in my settings.py file whenever I'm running this tox environment, which is not an elegant solution IMO.

I'm basically looking for any guidance on how to get around this a bit more elegantly. I'm also open to opening up a pull request to introduce an additional (or reuse MINIO_CONSISTENCY_CHECK_ON_START) configuration setting in that line of code to not check for bucket existence e.g.

There are 2 places where this needs to be done, in apps.py:

        consistency_check_on_start = get_setting('MINIO_CONSISTENCY_CHECK_ON_START', False)

        # other code goes here...

        # Validate static storage and default storage configurations
        staticfiles_storage: str = get_setting('STATICFILES_STORAGE')
        if staticfiles_storage.endswith(MinioBackendStatic.__name__):
            mbs = MinioBackendStatic()
            # This is my proposed change - this entire line could also be removed entirely
            # since the init method of MinioBackendStatic already checks for bucket existence
            if consistency_check_on_start:
                    mbs.check_bucket_existence()

and in models.py

@deconstructible
class MinioBackendStatic(MinioBackend):
    """
    MinIO-compatible Django custom storage system for Django static files.
    The used bucket can be configured in settings.py through `MINIO_STATIC_FILES_BUCKET`
    :arg *args: Should not be used for static files. It's here for compatibility only
    :arg **kwargs: Should not be used for static files. It's here for compatibility only
    """
    def __init__(self, *args, **kwargs):
        super().__init__(self.MINIO_STATIC_FILES_BUCKET, *args, **kwargs)

        consistency_check_on_start = get_setting('MINIO_CONSISTENCY_CHECK_ON_START', False)
        if consistency_check_on_start: # This is my proposed change
                self.check_bucket_existence()  # make sure the `MINIO_STATIC_FILES_BUCKET` exists
        self.set_bucket_to_public()  # the static files bucket must be publicly available

initialize_buckets command is stuck for several hours

Is there something I can do to troubleshoot this issue? I tried enabling verbose but nothing gets displayed on the console. I have both public and private buckets. My private bucket has 4.67 MB of data in it.

I have also enabled consistency check

Import timedelta in models.py

Hey I got this error:

File "/usr/local/lib/python3.8/site-packages/django_minio_backend/models.py", line 177, in url     

  expires=get_setting("MINIO_URL_EXPIRY_HOURS", timedelta(days=7))  # Default is 7 days

NameError: name 'timedelta' is not defined

An import like this in models.py should fix it..?

from datetime import timedelta

OSError at

anyone have an idea about this error ?
OSError at /APP/
stream having not enough data;expected: 1999, got: 0 bytes
Request Method: | POST

Exception Location: | /venv/lib/python3.7/site-packages/minio/api.py, line 1745, in put_object

thank yo u

external endpoint are broken

Hi,
I've upgrade my project to version 3.4.0 of django_minio_backend and now external URL seems to be broken.
Correct me if I'm wrong but now MINIO_EXTERNAL_ENDPOINT seems to not get used to calculate external url.

Am I right?

TypeError: can't compare offset-naive and offset-aware datetimes when running collectstatic

I think this might be user error but I'm having hard time figuring out how to debug this.

I have latest django-minio (3.2.1) running on Django 3.2 and connected to minio (version 2021-11-09T03:21:45Z) that is running behind Traefik proxy.

The connection, bucket creation and initial collectstatic works ok, but when I run collectstatic again I get following error:


Traceback (most recent call last):
  File "/project/manage.py", line 25, in <module>
    main()
  File "/project/manage.py", line 22, in main
    execute_from_command_line(sys.argv)
  File "/project/venv39/lib/python3.9/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/project/venv39/lib/python3.9/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/project/venv39/lib/python3.9/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/project/venv39/lib/python3.9/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/project/venv39/lib/python3.9/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 187, in handle
    collected = self.collect()
  File "/project/venv39/lib/python3.9/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 114, in collect
    handler(path, prefixed_path, storage)
  File "/project/venv39/lib/python3.9/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 338, in copy_file
    if not self.delete_file(path, prefixed_path, source_storage):
  File "/project/venv39/lib/python3.9/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 278, in delete_file
    target_last_modified.replace(microsecond=0) >=
TypeError: can't compare offset-naive and offset-aware datetimes

Using following relevant settings:

MINIO_PUBLIC_BUCKETS = [
    "my-dev-staticfiles",
]
MINIO_STATIC_FILES_BUCKET = "my-dev-staticfiles"
STATICFILES_STORAGE = "django_minio_backend.models.MinioBackendStatic"

When I delete the uploaded staticfiles + the bucket and re-run collectatic the command works again. (But only for the first time.)

How to have multiple MINIO_MEDIA_FILES_BUCKET?

Based on my needs, I divided media files into 2 separate buckets. One is private and another is public.
the problem is that, the django only can serve one of the buckets at a time.
Here is my settings:

MINIO_PRIVATE_BUCKET = "private-bk"
MINIO_PUBLIC_BUCKET = "public-bk"
DEFAULT_FILE_STORAGE = "django_minio_backend.models.MinioBackend"
MINIO_MEDIA_FILES_BUCKET = MINIO_PUBLIC_BUCKET  # replacement for MEDIA_ROOT
MINIO_PRIVATE_BUCKETS = [MINIO_PRIVATE_BUCKET]
MINIO_PUBLIC_BUCKETS = [MINIO_PUBLIC_BUCKET]

Now in the production, links for the public bucket files are generated (static links) and links work. but links for private bucket files don't work (they are permanent links)
Is it possible to have multiple buckets as MINIO_MEDIA_FILES_BUCKET?

ModuleNotFoundError for latest Minio version (>6.0)

File "/home/patrick/PycharmProjects/fpo-api/fpo/core/models/game.py", line 8, in <module>
    from django_minio_backend import MinioBackend
  File "/home/patrick/PycharmProjects/fpo-api/venv/lib/python3.7/site-packages/django_minio_backend/__init__.py", line 2, in <module>
    from .models import *
  File "/home/patrick/PycharmProjects/fpo-api/venv/lib/python3.7/site-packages/django_minio_backend/models.py", line 12, in <module>
    import minio.definitions
ModuleNotFoundError: No module named 'minio.definitions'

Storage does not follow Django docs recommendations

Hi!

First of all, thanks for your package. It looks great! However, I don't understand the reason why MinioBackend requires a bucket name as argument to instantiate. This, for instance, makes it impossible to set up the storage as the default storage for either statics or media files.

The Django docs are quite explicit about this: https://docs.djangoproject.com/en/3.2/howto/custom-file-storage/, a storage should not have init arguments.

This basically causes two issues:

  1. For media files, you need to specify the storage on every file field you use
  2. As far as I know it is impossible to use MinIO to store the statics collected by collectstatic

django-minio-storage solved this by allowing to set two settings: MINIO_STORAGE_MEDIA_BUCKET_NAME and MINIO_STORAGE_STATICS_BUCKET_NAME, so the respective buckets are used accordingly.

I wonder if it would be a good idea to do something similar for this project.

Minio 7.0.2 causes error messages in installation

It looks like the latest version of minio client causes errors on installation, I will investigate further.

$ pip3 install django-minio-backend
Collecting django-minio-backend
  Downloading django_minio_backend-3.3.2-py3-none-any.whl (25 kB)
Requirement already satisfied: Django>=2.2.2 in ./venv/lib/python3.8/site-packages (from django-minio-backend) (4.1.1)
Collecting minio>=7.0.2
  Downloading minio-7.1.11-py3-none-any.whl (76 kB)
     |████████████████████████████████| 76 kB 9.5 MB/s 
Requirement already satisfied: backports.zoneinfo; python_version < "3.9" in ./venv/lib/python3.8/site-packages (from Django>=2.2.2->django-minio-backend) (0.2.1)
Requirement already satisfied: sqlparse>=0.2.2 in ./venv/lib/python3.8/site-packages (from Django>=2.2.2->django-minio-backend) (0.4.2)
Requirement already satisfied: asgiref<4,>=3.5.2 in ./venv/lib/python3.8/site-packages (from Django>=2.2.2->django-minio-backend) (3.5.2)
Requirement already satisfied: urllib3 in ./venv/lib/python3.8/site-packages (from minio>=7.0.2->django-minio-backend) (1.26.12)
Requirement already satisfied: certifi in ./venv/lib/python3.8/site-packages (from minio>=7.0.2->django-minio-backend) (2022.9.14)
ERROR: django-minio-storage 0.3.10 has requirement minio<7,>=4.0.21, but you'll have minio 7.1.11 which is incompatible.
Installing collected packages: minio, django-minio-backend
  Attempting uninstall: minio
    Found existing installation: minio 6.0.2
    Uninstalling minio-6.0.2:
      Successfully uninstalled minio-6.0.2
Successfully installed django-minio-backend-3.3.2 minio-7.1.11

Inconsistent use of `MINIO_USE_HTTPS`

@theriverman When I had proposed changes in #31, I had done so thinking USE_HTTPS variable means that the user wants https protocol meaning it is False by default. I had seen a similar assumption from you at

self.__MINIO_USE_HTTPS: bool = get_setting("MINIO_USE_HTTPS", False)

But by checking it for a value of None, you are also enforcing on the user to set this value even though it should be a default False when not provided as mentioned above.

IMHO, you should leave MINIO_USE_HTTPS out of validate_settings unless the user wants an HTTPS connection as, without it, you are anyway using http as a fallback.

self.__BASE_URL = ("https://" if self.__MINIO_USE_HTTPS else "http://") + self.__MINIO_ENDPOINT

Bucket name is wrong when read data from minio

environment

  • system: ubuntu19.10
  • Python: 3.7.5
  • django-minio-backend: 1.1.2
  • django: 3.0.2
  • minio: 5.0.7 (also tried 4.0.9)

code

All settings are just the same as readme, and I ensure I can connect to my minio server (by the way, MinioBackend.is_minio_available() in readme is not work as is_minio_available is not a static function).
The main model is defined as follows.

class Image(models.Model):
    """
    This is just for uploaded image
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    image = models.ImageField(upload_to=get_image_path, storage=MinioBackend(is_public=True))

error

When I run Image.objects.first() after I create an object successfully, I meet errors:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/query.py", line 664, in first
    for obj in (self if self.ordered else self.order_by('pk'))[:1]:
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/query.py", line 276, in __iter__
    self._fetch_all()
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/query.py", line 1261, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/query.py", line 75, in __iter__
    obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/base.py", line 512, in from_db
    new = cls(*values)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/base.py", line 502, in __init__
    post_init.send(sender=cls, instance=self)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/dispatch/dispatcher.py", line 175, in send
    for receiver in self._live_receivers(sender)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/dispatch/dispatcher.py", line 175, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/fields/files.py", line 449, in update_dimension_fields
    width = file.width
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/core/files/images.py", line 19, in width
    return self._get_image_dimensions()[0]
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/core/files/images.py", line 28, in _get_image_dimensions
    self.open()
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/db/models/fields/files.py", line 74, in open
    self.file = self.storage.open(self.name, mode)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django/core/files/storage.py", line 36, in open
    return self._open(name, mode)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/django_minio_backend/models.py", line 101, in _open
    return self.client.get_object(bucket_name, object_name, request_headers, sse)
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/minio/api.py", line 727, in get_object
    response = self._url_open('PUT',
  File "/home/holder/repo/dyf_api/venv/lib/python3.7/site-packages/minio/helpers.py", line 391, in is_valid_bucket_name
    if not isinstance(policy, string_type):
minio.error.InvalidBucketError: InvalidBucketError: message: Bucket name does not follow S3 standards. Bucket: 2020-2-20/bug_9Ag81us.jpg

conjecture

It seems that when self.open(name, mode) is called by ImageFiled, it will call Storage.open(name, mode), which calls MinioBackend._open(bucket_name, object_name). Here, unfortunately, backend will regard name as bucket_name, mode as object_name, which leads to the error that bucket name is "2020-2-20/bug_9Ag81us.jpg".
I'm not sure if I do something in wrong given that you must have tested such case. Looking forward to your early reply!

AttributeError: 'ContentFile' object has no attribute 'content_type'

I am trying to save base64 image from on FileField which is generating the following error

AttributeError: 'ContentFile' object has no attribute 'content_type'
the model code is


   data = models.FileField(upload_to=iso_date_prefix, storage=MinioBackend(
        bucket_name='test-private'))

what is the possible cause of the issue and the solution for the same. Thanks in advance for your valuable time
I have also attached the stack trace for the same . If I have under emphasised anything let me know in the comments

Traceback (most recent call last):
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/nithin/PIX-DAR/SIB_FRONT/SIB-VIDEOKYC/VIDEO_KYC/vkyc-sib-backend/pixl/users/views.py", line 1951, in upload_image
    x.save()
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/base.py", line 741, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/base.py", line 779, in save_base
    force_update, using, update_fields,
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/base.py", line 870, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/base.py", line 908, in _do_insert
    using=using, raw=raw)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/query.py", line 1186, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1367, in execute_sql
    for sql, params in self.as_sql():
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1311, in as_sql
    for obj in self.query.objs
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1311, in <listcomp>
    for obj in self.query.objs
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1310, in <listcomp>
    [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1261, in pre_save_val
    return field.pre_save(obj, add=True)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/fields/files.py", line 288, in pre_save
    file.save(file.name, file.file, save=False)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/db/models/fields/files.py", line 87, in save
    self.name = self.storage.save(name, content, max_length=self.field.max_length)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django/core/files/storage.py", line 52, in save
    return self._save(name, content)
  File "/home/nithin/miniconda3/lib/python3.7/site-packages/django_minio_backend/models.py", line 86, in _save
    content_type=content.content_type,
AttributeError: 'ContentFile' object has no attribute 'content_type'

Exception ignored in: <function Minio.__del__ at 0x7f5cb166feb0>

This warning occurs when django server reloads when am using minio as a static files storage.

my static files storage setting is

STATICFILES_STORAGE = 'django_minio_backend.models.MinioBackendStatic'

ERROR

Exception ignored in: <function Minio.__del__ at 0x7f5cb166feb0> Traceback (most recent call last): File "/yyy/yyyy/yyyy/yyyy/env/lib/python3.10/site-packages/minio/api.py", line 155, in __del__ File "/yyy/yyyy/yyyy/yyyy/env/lib/python3.10/site-packages/urllib3/poolmanager.py", line 223, in clear File "/yyy/yyyy/yyyy/yyyy/env/lib/python3.10/site-packages/urllib3/_collections.py", line 95, in clear TypeError: 'NoneType' object is not callable INFO 2022-10-20 22:23:00,643 autoreload 87782 140634100062016 Watching for file changes with StatReloader

but when using S3 [minio alternative static files settings], the error dissapears:

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

change bucket_name in model

Hello, dears
Imagine I've got the below code in the model e.x bucket_name is sample

class Image(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    file = models.FileField(verbose_name="Object Upload",
                            storage=MinioBackend(bucket_name='sample'),
                            upload_to=iso_date_prefix)

when changing the model like below e.x bucket_name is test and wanna make migration and migration

class Image(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    file = models.FileField(verbose_name="Object Upload",
                            storage=MinioBackend(bucket_name='test'),
                            upload_to=iso_date_prefix)

I see this error because the sample exists in the last migration and I force added sample in the public or private bucket in settings to prevent error how to fix it?
must be declared either in MINIO_PRIVATE_BUCKETS or MINIO_PUBLIC_BUCKETS') django_minio_backend.utils.configuration error: The configured bucket (punto) must be declared either in MINIO_PRIVATE_BUCKETS or MINIO_PUBLIC_BUCKETS

ImportError: cannot import name 'utc' from 'django.utils.timezone'

Django 5.0 release notes:

The django.utils.timezone.utc alias to datetime.timezone.utc is removed.

logs:

...
File "/usr/local/lib/python3.12/site-packages/django_minio_backend/init.py", line 1, in
from .apps import *
File "/usr/local/lib/python3.12/site-packages/django_minio_backend/apps.py", line 3, in
from .models import MinioBackend, MinioBackendStatic
File "/usr/local/lib/python3.12/site-packages/django_minio_backend/models.py", line 30, in
from django.utils.timezone import utc
ImportError: cannot import name 'utc' from 'django.utils.timezone' (/usr/local/lib/python3.12/site-packages/django/utils/timezone.py)

Call super() in models.Model.delete() method

def delete(self, using=None, keep_parents=False):
"""
Delete must be overridden because the inherited delete method does not call `self.file.delete()`.
"""
using = using or router.db_for_write(self.__class__, instance=self)
assert self.pk is not None, (
"%s object can't be deleted because its %s attribute is set to None." %
(self._meta.object_name, self._meta.pk.attname)
)
collector = Collector(using=using)
collector.collect([self], keep_parents=keep_parents)
self.file.delete()
return collector.delete()

Instead of rewriting the whole method, call super():

    def delete(self, *args, **kwargs):
        self.file.delete()
        super(PublicAttachment, self).delete(*args, **kwargs)

How can I create private link for files?

Hello there,

I need to create a private link for my files for a specific duration. For example, if my model has an image field I need to something like this:

obj.image.create_presignedurl()

and this link must be valid for a specific time, How I can achieve this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.