【问题标题】:AttributeError when attempting to save a file downloaded from requests尝试保存从请求中下载的文件时出现 AttributeError
【发布时间】:2021-09-03 10:25:15
【问题描述】:

我正在尝试保存 MP3 媒体文件,因为它们打包在对本地存储的请求响应中。我已经定义了一个模型MP3,以及它对应的经理MP3Manager。在管理器中,我们有一个用于获取数据的类方法,理想情况下是从本地数据库中获取数据,但如果不存在则从外部媒体服务器获取。此检索发生在名为get_mp3() 的类方法中。

get_mp3() 内部,我们还有save_mp3(),它的意思是从response.content 中获取字节字符串,将其保存到文件系统,并创建一个数据库条目。但是,似乎存在某种格式问题,因为在执行save_mp3() return 语句时出现以下错误:

AttributeError: '_io.BufferedWriter' object has no attribute '_committed'

我该如何解决这个问题?整个 mp3.py 文件在这里:

import os
import requests

from django.db import models
from django.utils.translation import gettext_lazy as _

from rest_framework import status
from rest_framework.exceptions import NotFound

from api.exceptions import InternalServerError
from api.models import TimestampedModel


class MP3Manager(models.Manager):
    """
    Class defining utility methods for downloading MP3 pronunciation audio files from the Merriam-Webster media servers.
    """
    @classmethod
    def get_mp3(cls, id, url):
        """
        Obtains an MP3 file from local storage if a database entry exists, and downloads from the Merriam-Webster database on a cache miss.
        """
        mp3 = None

        try:
            # Check the local database.
            mp3 = cls.get(id=id)
        except MP3.DoesNotExist:
            # Not in store. Download and save file.
            response = requests.get(url)

            if response.status_code == status.HTTP_404_NOT_FOUND:
                raise NotFound(_('The specified ID was invalid.'))
            elif response.status_code != status.HTTP_200_OK:
                raise InternalServerError(
                    _(
                        'The file could not retrieved. Please contact support at support@example.com.'
                    ))

            filepath = f'/tmp/{id}.mp3'

            def save_mp3():
                """
                Save the downloaded file to the filesystem and local database.
                """
                with open(filepath, 'xb') as f:
                    content = response.content
                    for i in range(0, len(content), MP3.block_size):
                        upper = min(i + MP3.block_size, len(content))
                        f.write(content[i:upper])

                    return cls.create(id=id, data=f)

            try:
                mp3 = save_mp3()
            except FileExistsError:
                os.remove(filepath)
                mp3 = save_mp3()

        return mp3


class MP3(TimestampedModel):
    """
    Timestamped model for MP3 pronunciation audio files downloaded from the Merriam-Webster media servers.
    """
    # Static Variables
    objects = MP3Manager()
    relative_path = 'mp3'
    block_size = 2**16

    # Attributes
    id = models.CharField(primary_key=True, max_length=64)
    data = models.FileField(_('MP3'), upload_to=relative_path)
    _hash = models.BinaryField(
        _('MD5 hash'), editable=False, null=True, default=None, max_length=16)

在这里追溯:

Traceback (most recent call last):
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/models/mp3.py", line 51, in get_mp3
    mp3 = MP3.objects.get(id=id)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 435, in get
    raise self.model.DoesNotExist(
api.dictionary.models.mp3.MP3.DoesNotExist: MP3 matching query does not exist.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/models/mp3.py", line 85, in get_mp3
    mp3 = save_mp3()
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/models/mp3.py", line 76, in save_mp3
    with open(filepath, 'xb') as f:
FileExistsError: [Errno 17] File exists: '/tmp/apple001.mp3'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/tests/test_mp3.py", line 42, in test_success_miss
    response = self.client.get(self.url_path, format='json')
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/test.py", line 288, in get
    response = super().get(path, data=data, **extra)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/test.py", line 205, in get
    return self.generic('GET', path, **r)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/test.py", line 233, in generic
    return super().generic(
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/test/client.py", line 473, in generic
    return self.request(**r)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/test.py", line 285, in request
    return super().request(**kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/test.py", line 237, in request
    request = super().request(**kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/test/client.py", line 719, in request
    self.check_exception(response)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/test/client.py", line 580, in check_exception
    raise exc_value
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/views/mp3_views.py", line 28, in get
    mp3 = MP3.objects.get_mp3(id)
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/models/mp3.py", line 88, in get_mp3
    mp3 = save_mp3()
  File "/home/matt/Repositories/Iconopedia/back-end/api/dictionary/models/mp3.py", line 82, in save_mp3
    return MP3.objects.create(id=id, data=f)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 453, in create
    obj.save(force_insert=True, using=self.db)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 726, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 763, in save_base
    updated = self._save_table(
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 868, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/base.py", line 906, in _do_insert
    return manager._insert(
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/query.py", line 1270, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1415, in execute_sql
    for sql, params in self.as_sql():
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1358, in as_sql
    value_rows = [
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/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 "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/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 "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1310, in pre_save_val
    return field.pre_save(obj, add=True)
  File "/home/matt/Repositories/Iconopedia/back-end/.venv/lib/python3.9/site-packages/django/db/models/fields/files.py", line 300, in pre_save
    if file and not file._committed:
AttributeError: '_io.BufferedWriter' object has no attribute '_committed'

【问题讨论】:

    标签: python django file


    【解决方案1】:

    内置的python文件对象不能直接传递给Django的FileField,我们需要这样包装:

    from django.core.files import File
    
    content = File(f)
    mp3.id = id
    mp3.data.save("your_file_name.mp3", content, save=False) 
    mp3.save()
    

    参考:Django Shell image upload _io.BufferedReader no attribute size

    https://docs.djangoproject.com/en/3.2/ref/files/file/#django.core.files.File

    【讨论】:

    • 谢谢。我最终使用 ContentFile 代替,因为我的原始来源是字节字符串。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-09
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多