0

我已经在 Django 中实现了一个自定义模型字段。它是一个图像字段,除了直接分配文件外,还允许分配 URL 字符串以从中加载图像。

import uuid
import urllib.request

from django.core.files.base import ContentFile
from django.db import models
from django.db.models.fields.files import ImageFileDescriptor


class UrlImageFileDescriptor(ImageFileDescriptor):
    def __set__(self, instance, value):
        # If a string is used for assignment, it is used as URL
        # to fetch an image from and store it on the server.
        if isinstance(value, str):
            try:
                response = urllib.request.urlopen(value)
                image = response.read()
                name = str(uuid.uuid4()) + '.png'
                value = ContentFile(image, name)
            except:
                print('Error fetching', value)
                pass
        super().__set__(instance, value)


class UrlImageField(models.ImageField):
    descriptor_class = UrlImageFileDescriptor

一般来说,该领​​域有效。但出于某种原因,Django 本身在内部为其分配了字符串值。每次使用该字段的模型查询集被过滤时,__set__都会使用字符串调用,以便触发 except 子句中的 print 语句Error fetching upload/to/50e170bf-61b6-4670-90d1-0369a8f9bdb4.png

我可以缩小对django/db/models/query.pyDjango 1.7c1 的调用范围。

def get(self, *args, **kwargs):
    """
    Performs the query and returns a single object matching the given
    keyword arguments.
    """
    clone = self.filter(*args, **kwargs)
    if self.query.can_filter():
        clone = clone.order_by()
    clone = clone[:MAX_GET_RESULTS + 1]
    num = len(clone) # This line causes setting of my field
    if num == 1:
        return clone._result_cache[0]
    # ...

为什么导致我的字段__set__被执行的行?我可以验证输入值是一个有效的 URL 来解决这个问题,但我想先知道原因。

4

1 回答 1

4

故事就在你的追溯中。要获取查询的长度:

File "C:\repository\virtualenv\lib\site-packages\django\db\models\query.py" in get
  350.         num = len(clone)

它将所有查询结果提取到一个列表中:

File "C:\repository\virtualenv\lib\site-packages\django\db\models\query.py" in __len__
  122.         self._fetch_all()
File "C:\repository\virtualenv\lib\site-packages\django\db\models\query.py" in _fetch_all
  966.             self._result_cache = list(self.iterator())

对于每个查询结果,它使用数据库中的数据创建一个模型对象:

File "C:\repository\virtualenv\lib\site-packages\django\db\models\query.py" in iterator
  275.                     obj = model(*row_data)

要创建模型对象,它会设置模型的每个字段:

File "C:\repository\virtualenv\lib\site-packages\django\db\models\base.py" in __init__
  383.                 setattr(self, field.attname, val)

最终调用__set__您的自定义模型字段:

File "C:\repository\invoicepad\apps\customer\fields.py" in __set__
  18.                           response = urllib.request.urlopen(value)

这背后的更大规模的原因很难说更多,因为我对 Django 了解不多,也因为我不知道你的 db 结构是什么样的。但是,从本质上讲,它看起来无论数据库字段填充了您UriImageField在其中的数据,这些数据对于您实现描述符的方式实际上都是无效的。(例如,从您的错误来看,数据库有'upload/to/50e170bf-61b6-4670-90d1-0369a8f9bdb4.png'但实际上没有这样的文件。)

于 2014-07-27T01:16:12.150 回答