theriverman / django-minio-backend

Minio Backend for Django
https://pypi.org/project/django-minio-backend/
MIT License
111 stars 22 forks source link

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

Closed ravisarath closed 3 years ago

ravisarath commented 3 years ago

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'
theriverman commented 3 years ago

Sorry for the late reply, @ravisarath I have missed this issue report.

If you look into the contents of models.py, you can observe that it takes an InMemoryUploadedFile: https://github.com/theriverman/django-minio-backend/blob/811abbed821edc0c87d613b78bc3255c03843a88/django_minio_backend/models.py#L69

(Edit: Removed to avoid misunderstandings. It was a non-working code snippet by mistake)

theriverman commented 3 years ago

Ignore my previous reply, @ravisarath . I had been testing various ways yesterday, and I've provided a non-working example, sorry for that!

I've modified the implementation to automagically guess the content_type based on the provided file's name (using the Python standard library's mimetypes module).

Please update to django-minio-backend==2.3.0.

In case your image is a base64 string, refer to the below code snippet to get it properly saved by django-minio-backend:

import base64
from django.core.files.base import ContentFile
from Attachments.models import Image

base64_img_blob = "data:image/png;base64,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"
base64_img = base64_img_blob.split(";base64,")[1]
base64_img_bytes = base64_img.encode('utf-8')
decoded_image_data = base64.decodebytes(base64_img_bytes)

cf = ContentFile(content=decoded_image_data, name='an-amazing-image.png')  # You should provide a filename with proper extension

img = Image.objects.first()
img.image.save(name='an-amazing-image.png', content=cf)

Above snippet implies that your base64 string starts with a metadata (i.e.: data:image/png;base64,). Otherwise you can skip the part which extracts the actual base64 image string. It is also implied that you're working with models.ImageField but mapping it to models.FileField is pretty simple.