ellmetha / django-machina

A Django forum engine for building powerful community driven websites.
https://django-machina.readthedocs.org
BSD 3-Clause "New" or "Revised" License
589 stars 126 forks source link

Preview while creating a new post with a large attachment results into "AttributeError: '_io.BytesIO' object has no attribute 'name'" #283

Open barsch opened 1 year ago

barsch commented 1 year ago

Steps to reproduce:

  1. Go into any forum
  2. Create a new topic
  3. add subject and message
  4. add a attachment file which needs to be larger than FILE_UPLOAD_MAX_MEMORY_SIZE (default 2.5 MB) so that django.core.files.uploadhandler.TemporaryFileUploadHandler is used
  5. push the Preview button
  6. push the Submit button

Results into:

  ...
  File "D:\Python\virtualenv\test\lib\site-packages\machina\apps\forum_conversation\forum_attachments\forms.py", line 39, in save
    super().save(commit)
  File "D:\Python\virtualenv\test\lib\site-packages\django\forms\models.py", line 681, in save
    return self.save_existing_objects(commit) + self.save_new_objects(commit)
  File "D:\Python\virtualenv\test\lib\site-packages\django\forms\models.py", line 819, in save_new_objects
    self.new_objects.append(self.save_new(form, commit=commit))
  File "D:\Python\virtualenv\test\lib\site-packages\django\forms\models.py", line 658, in save_new
    return form.save(commit=commit)
  File "D:\Python\virtualenv\test\lib\site-packages\django\forms\models.py", line 468, in save
    self.instance.save()
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\base.py", line 739, in save
    self.save_base(using=using, force_insert=force_insert,
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\base.py", line 776, in save_base
    updated = self._save_table(
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\base.py", line 881, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\base.py", line 919, in _do_insert
    return manager._insert(
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\query.py", line 1270, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\sql\compiler.py", line 1415, in execute_sql
    for sql, params in self.as_sql():
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\sql\compiler.py", line 1358, in as_sql
    value_rows = [
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\sql\compiler.py", line 1359, in <listcomp>
    [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\sql\compiler.py", line 1359, in <listcomp>
    [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\sql\compiler.py", line 1310, in pre_save_val
    return field.pre_save(obj, add=True)
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\fields\files.py", line 302, in pre_save
    file.save(file.name, file.file, save=False)
  File "D:\Python\virtualenv\test\lib\site-packages\django\db\models\fields\files.py", line 89, in save
    self.name = self.storage.save(name, content, max_length=self.field.max_length)
  File "D:\Python\virtualenv\test\lib\site-packages\django\core\files\storage.py", line 54, in save
    name = self._save(name, content)
  File "D:\Python\virtualenv\test\lib\site-packages\django\core\files\storage.py", line 274, in _save
    file_move_safe(content.temporary_file_path(), full_path)
  File "D:\Python\virtualenv\test\lib\site-packages\django\core\files\uploadedfile.py", line 69, in temporary_file_path
    return self.file.name
AttributeError: '_io.BytesIO' object has no attribute 'name'

One can track it down to the forum attachments cache where the TemporaryUploadedFile.file attribute (usually a django.core.files.temp.TemporaryFile instance) gets directly replaced with a BytesIO instance at https://github.com/ellmetha/django-machina/blob/main/machina/apps/forum_conversation/forum_attachments/cache.py#L104

Later on self.file.name gets called which fails for BytesIO with the exception above.

Replacing the line upload.file = f with upload.file.write(f.getbuffer()) fixes it for me.

This issue is probably the same as issue #64.