如何在 Django 中使用仿函数而不是函数?
How to use functors instead of functions in django?
我有一个名为 Story 的模型:
from django.db import models
from api import settings
from core.functions import UploadImage
from .settings import STORY_UPLOAD_PATH
get_story_upload_path = UploadImage(STORY_UPLOAD_PATH)
class Story(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(upload_to=get_story_upload_path)
caption = models.TextField()
expiration_date = models.DateTimeField()
import datetime
import uuid
import os
上传照片的行为对于多个模型是相同的,因此我决定制作一个仿函数来保存上传路径并将其传递给具有共同行为的模型。
仿函数:
import datetime
import uuid
import os
class UploadImage:
def __init__(self, upload_path):
self.upload_path = upload_path
def __call__(self, instance, filename):
filename = f'{datetime.datetime.now()}_{uuid.uuid4()}_{filename}'
return os.path.join(self.upload_path, instance.username, filename)
序列化器class:
from rest_framework import serializers
from models import Story, ViewedStory
class StorySerializer(serializers.ModelSerializer):
class Meta:
model = Story
fields = ('id', 'user', 'image', 'caption')
read_only_fields = ('id', )
当我尝试进行迁移时,出现此错误:
Migrations for 'stories':
stories/migrations/0001_initial.py
- Create model Story
- Create model ViewedStory
Traceback (most recent call last):
File "manage.py", line 22, in <module>
main()
File "manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.8/dist-packages/django/core/management/__init__.py", line 413, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/base.py", line 354, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/base.py", line 398, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/base.py", line 89, in wrapped
res = handle_func(*args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/commands/makemigrations.py", line 190, in handle
self.write_migration_files(changes)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/commands/makemigrations.py", line 227, in write_migration_files
migration_string = writer.as_string()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 141, in as_string
operation_string, operation_imports = OperationWriter(operation).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 99, in serialize
_write(arg_name, arg_value)
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 51, in _write
arg_string, arg_imports = MigrationWriter.serialize(item)
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 271, in serialize
return serializer_factory(value).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 39, in serialize
item_string, item_imports = serializer_factory(item).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 201, in serialize
return self.serialize_deconstructed(path, args, kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 88, in serialize_deconstructed
arg_string, arg_imports = serializer_factory(arg).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 353, in serializer_factory
raise ValueError(
ValueError: Cannot serialize: <core.functions.UploadImage object at 0x7f369fbb27f0>
There are some values Django cannot serialize into migration files.
For more, see https://docs.djangoproject.com/en/3.2/topics/migrations/#migration-serializing
为什么会发生这种情况,我该如何解决?
根据 documentation:
Migrations are Python files containing the old definitions of your
models - thus, to write them, Django must take the current state of
your models and serialize them out into a file.
这意味着 Django 需要序列化模型中使用的所有东西。由于get_story_upload_path
是UploadImage
的一个实例,你自己弄的一个classDjango显然不知道如何序列化它。
Add a deconstruct() method的文档中也提到了解决方法。如果 class 的构造函数的所有参数本身都是可序列化的,我们可以简单地使用 deconstructible
装饰器。此外,我们还需要添加一个 __eq__
方法,以便迁移系统可以知道何时需要进行新的迁移。因此,您可以按如下方式更改 class UploadImage
以便它可以序列化:
from django.utils.deconstruct import deconstructible
@deconstructible
class UploadImage:
def __init__(self, upload_path):
self.upload_path = upload_path
def __call__(self, instance, filename):
filename = f'{datetime.datetime.now()}_{uuid.uuid4()}_{filename}'
return os.path.join(self.upload_path, instance.username, filename)
def __eq__(self, other):
return isinstance(other, self.__class__) and self.upload_path == other.upload_path
我有一个名为 Story 的模型:
from django.db import models
from api import settings
from core.functions import UploadImage
from .settings import STORY_UPLOAD_PATH
get_story_upload_path = UploadImage(STORY_UPLOAD_PATH)
class Story(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(upload_to=get_story_upload_path)
caption = models.TextField()
expiration_date = models.DateTimeField()
import datetime
import uuid
import os
上传照片的行为对于多个模型是相同的,因此我决定制作一个仿函数来保存上传路径并将其传递给具有共同行为的模型。
仿函数:
import datetime
import uuid
import os
class UploadImage:
def __init__(self, upload_path):
self.upload_path = upload_path
def __call__(self, instance, filename):
filename = f'{datetime.datetime.now()}_{uuid.uuid4()}_{filename}'
return os.path.join(self.upload_path, instance.username, filename)
序列化器class:
from rest_framework import serializers
from models import Story, ViewedStory
class StorySerializer(serializers.ModelSerializer):
class Meta:
model = Story
fields = ('id', 'user', 'image', 'caption')
read_only_fields = ('id', )
当我尝试进行迁移时,出现此错误:
Migrations for 'stories':
stories/migrations/0001_initial.py
- Create model Story
- Create model ViewedStory
Traceback (most recent call last):
File "manage.py", line 22, in <module>
main()
File "manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.8/dist-packages/django/core/management/__init__.py", line 413, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/base.py", line 354, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/base.py", line 398, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/base.py", line 89, in wrapped
res = handle_func(*args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/commands/makemigrations.py", line 190, in handle
self.write_migration_files(changes)
File "/usr/local/lib/python3.8/dist-packages/django/core/management/commands/makemigrations.py", line 227, in write_migration_files
migration_string = writer.as_string()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 141, in as_string
operation_string, operation_imports = OperationWriter(operation).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 99, in serialize
_write(arg_name, arg_value)
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 51, in _write
arg_string, arg_imports = MigrationWriter.serialize(item)
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/writer.py", line 271, in serialize
return serializer_factory(value).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 39, in serialize
item_string, item_imports = serializer_factory(item).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 201, in serialize
return self.serialize_deconstructed(path, args, kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 88, in serialize_deconstructed
arg_string, arg_imports = serializer_factory(arg).serialize()
File "/usr/local/lib/python3.8/dist-packages/django/db/migrations/serializer.py", line 353, in serializer_factory
raise ValueError(
ValueError: Cannot serialize: <core.functions.UploadImage object at 0x7f369fbb27f0>
There are some values Django cannot serialize into migration files.
For more, see https://docs.djangoproject.com/en/3.2/topics/migrations/#migration-serializing
为什么会发生这种情况,我该如何解决?
根据 documentation:
Migrations are Python files containing the old definitions of your models - thus, to write them, Django must take the current state of your models and serialize them out into a file.
这意味着 Django 需要序列化模型中使用的所有东西。由于get_story_upload_path
是UploadImage
的一个实例,你自己弄的一个classDjango显然不知道如何序列化它。
Add a deconstruct() method的文档中也提到了解决方法。如果 class 的构造函数的所有参数本身都是可序列化的,我们可以简单地使用 deconstructible
装饰器。此外,我们还需要添加一个 __eq__
方法,以便迁移系统可以知道何时需要进行新的迁移。因此,您可以按如下方式更改 class UploadImage
以便它可以序列化:
from django.utils.deconstruct import deconstructible
@deconstructible
class UploadImage:
def __init__(self, upload_path):
self.upload_path = upload_path
def __call__(self, instance, filename):
filename = f'{datetime.datetime.now()}_{uuid.uuid4()}_{filename}'
return os.path.join(self.upload_path, instance.username, filename)
def __eq__(self, other):
return isinstance(other, self.__class__) and self.upload_path == other.upload_path