尝试创建模型实例时出现类型错误

时间:2021-07-02 16:23:33

标签: python django

我有一个项目,我需要在其中创建一个 Django 命令来一次上传多个图像。我有一个定义文件的基类 Image,以及将单词和描述与图标相关联的子类 Icon

我的问题是在调用 Icon.objects.create() 时出现错误。它适用于单个上传上下文,但不适用于批量上传器。我该怎么办?

models.py

class Image(TimestampedModel):
    """
    Abstract model containing an image, a hash of that image, and operation methods.
    """
    class Meta:
        """
        The metaclass defining its parent as abstract.
        """
        abstract = True

    # Static variables
    RELATIVE_PATH = 'img'
    BLOCK_SIZE = 2**16

    # Attributes
    image = models.ImageField(
        blank=True, null=True, default=None, upload_to=RELATIVE_PATH)
    _hash = models.BinaryField(
        _('MD5 hash'), editable=False, null=True, default=None, max_length=16)


class Icon(Image):
    """
    Image file associated with a word, a descriptor, and (for verbs) tense.
    """
    # Static variables
    TENSE_CHOICES = [
        (None, 0),
        ('1', 1),  # present
        ('c', 2),  # present participle
        ('p', 3),  # past
        ('pp', 4),  # past participle
    ]
    BLOCK_SIZE = 2**12

    # Attributes
    word = models.CharField(max_length=32)
    descriptor = models.CharField(blank=True, null=True, max_length=32)
    tense = models.PositiveSmallIntegerField(
        blank=True, null=True, default=None, choices=TENSE_CHOICES)
    is_approved = models.BooleanField(default=False)

bulk_uploader.py

class BulkUploader:
    """
    Class defining a utility method for uploading multiple icons.
    """
    @classmethod
    def save(cls, filepath, word, descriptor, tense=None):
        filename = filepath.split('/')[-1]
        with open(filepath, 'rb') as f:
            bytes_str = b''
            for buffer in iter(partial(f.read, Icon.BLOCK_SIZE), b''):
                bytes_str += buffer

            breakpoint()
            icon = Icon.objects.create(
                word=word,
                descriptor=descriptor,
                tense=tense)
            icon.image.save(filename, ContentFile(bytes_str), save=False)
            icon.save()

调试输出

> /home/matt/Repositories/my_project/utils/bulk_uploader.py(28)save()
-> icon = Icon.objects.create(
(Pdb) n
> /home/matt/Repositories/my_project/utils/bulk_uploader.py(29)save()
-> word=word,
(Pdb) 
> /home/matt/Repositories/my_project/utils/bulk_uploader.py(30)save()
-> descriptor=descriptor,
(Pdb) 
> /home/matt/Repositories/my_project/utils/bulk_uploader.py(31)save()
-> tense=tense)
(Pdb) 
> /home/matt/Repositories/my_project/utils/bulk_uploader.py(28)save()
-> icon = Icon.objects.create(
(Pdb) 
TypeError: join() argument must be str, bytes, or os.PathLike object, not 'NoneType'
> /home/matt/Repositories/my_project/utils/bulk_uploader.py(28)save()
-> icon = Icon.objects.create(
(Pdb) 

编辑

upload.py

from django.core.management.base import BaseCommand, CommandError
from api.dictionary.utils.bulk_uploader import BulkUploader


class Command(BaseCommand):
    help = 'Uploads icons in a given directory based on its part of speech format.'

    def add_arguments(self, parser):
        parser.add_argument('part_speech', type=str)
        parser.add_argument('directory', type=str)

    def handle(self, *args, **options):
        try:
            BulkUploader.upload(options['part_speech'], options['directory'])
        except Exception as exc:
            raise CommandError(exc)

        self.stdout.write(self.style.SUCCESS('Upload successful.'))

3 个答案:

答案 0 :(得分:0)

你在使用管理命令吗?你连接到数据库了吗?。如果你在 Django 之外运行它,请使用 django.setup()。尝试将脚本更改为管理命令并使用 python manage.py。

答案 1 :(得分:0)

在您的模型上,您有以下字段:

_hash = models.BinaryField(
    _('MD5 hash'), editable=False, null=True, default=None, max_length=16
)

这里需要注意的一点是 BinaryField 是一个 基于字符串的 字段,documentation 明确建议不要在基于字符串的字段上使用 null=True .此外,您设置的 default=None 确实是多余的(如果您不指定它,Django 会将 None 作为默认值传递)并且实际上是破坏您的代码的原因。可能在内部调用了一些代码,该代码试图在此字段值上调用 join,因此给您一个错误。通常,该字段的默认值是一个空字节 b'',但您已将其设置为 None。事实上,您还在其他字段上设置了 default=None,我建议您将其删除,因为它没有用。

此外,您已经设置了 editable=False(这也是 BinaryField 这个 kwarg 的默认值,所以这是多余的)这一事实让我感到困惑,因为您没有为此字段设置值在创建实例时,这意味着您以后将无法设置它的值,否则会使该字段变得无用。

考虑到所有这些,您应该将字段定义更改为:

_hash = models.BinaryField(_('MD5 hash'), editable=True, max_length=16)

答案 2 :(得分:0)

我发现了这个问题。我有一个 post_save 信号接收器在这条线上被触发:

icon = Icon.objects.create(word=word, descriptor=descriptor, tense=tense)

所以我的解决方案是断开连接然后重新连接到信号。

post_save.disconnect(Image.post_save, sender=Icon, dispatch_uid='0')

icon = Icon.objects.create(word=word, descriptor=descriptor, tense=tense)
icon.image.save(filename, ContentFile(bytes_str), save=False)

post_save.connect(Image.post_save, sender=Icon, dispatch_uid='0')

icon.save()