1

更新我现在认为有一个定义的理由get_prep_value(),这样做可以提高 Django 对该字段的使用。我也能够摆脱包装类。最后,所有这些都使我能够__getattribute__ implementation with the data model, which was annoying. So, apart from Django calling超级频繁地消除 to_python()`,就我所见,我现在很好。/更新

一天早上,您醒来发现自己在 Python 2.6.8 上使用 Django 1.4.2 和 DjangoRESTFramework 2.1.2。嘿,事情肯定会更糟。这个 Django 管理魔法为您提供了易于指定的关系数据模型的表单,使维护数据库的编辑部分成为一种乐趣。RESTful URL 背后的业务逻辑会根据需要访问编辑数据和特定的数据库表,甚至这些都显示在 Django 管理员中,部分原因是它很容易完成并且很好用,部分原因是一些自动生成的记录需要一个迷你工作流程.

可是等等。您还没有将这些二进制字段实现为 BINARY。他们是 VARCHARS。你已经把它放在你的待办事项列表中以备后用。后来就是现在。

好的,有些表大小很小的一次写入多次读取的情况下,优化不一定会付出代价。但在另一种情况下,由于表中频繁的 INSERT 和 DELETE 会变大,您会浪费存储空间和性能。

那么你想拥有什么?DB 和 Django 之间的清晰映射,其中 DB 存储 BINARY,而 Django 处理两倍长度的十六进制字符串。不可能那么难实现,不是吗?

您在 Web 上搜索并找到想要 CHAR 而不是 VARCHAR 的人,还有其他想要 BLOB 的人,而且每个人的做法似乎都有些不同。最后,您将在正式处理 VARCHAR -> CHAR 案例的地方编写自定义模型字段。因此,您决定使用此信息。

__init__(),db_type()和开始to_python(),您会注意到它to_python()很少被调用,并且__metaclass__ = models.SubfieldBase仅添加到 Django 现在调用的数字,to_python()即使它之前已经这样做过。页面上的其他建议突然开始对您更有意义,因此您要将数据包装在一个类中,这样您就可以保护它免受重复调用to_python(). 你也按照建议去Put a __str__() or __unicode__() method on the class you're wrapping up as a field实施get_prep_value()

虽然生成的代码没有达到您的预期,但您注意到的一件事是到目前为止get_prep_value()从未被调用,因此您现在将其删除。您所做的事情是,Django 似乎始终str从 DB 和unicode从 admin获得 a ,这很酷,并最终得到类似的东西(归结为要领,真的)。

class MyHexWrappeer(object):
    def __init__(self, hexstr):
        self.hexstr = hexstr
    def __len__(self):
        return len(self.hexstr)
    def __str__(self):
        return self.hexstr

class MyHexField(models.CharField):
    __metaclass__ = models.SubfieldBase
    def __init__(self, max_length, *args, **kwargs):
        assert(max_length % 2 == 0)
        self.max_length = max_length
        super(MyHexField, self).__init__(max_length=max_length, *args, **kwargs)
    def db_type(self, connection):
        return 'binary(%s)' % (self.max_length // 2)
    def to_python(self, data):
        if isinstance(data, MyHexWrapper): # protect object
            return data
        if isinstance(data, str): # binary string from DB side
            return MyHexWrapper(binascii.b2a_hex(data))
        if isinstance(data, unicode): # unicode hex string from admin
            return MyHexWrapper(data)

而且……这行不通。当然,原因是虽然您找到了一种可靠的方法来MyHexWrapper从包括 Django 本身在内的所有来源创建对象,但显然缺少向后的路径。从上面的评论中,您认为 Django 调用str()unicode()为 admin 并get_prep_value()在 DB 的方向。但是,如果您在get_prep_value()上面添加,它将永远不会被调用,并且您会被卡住。

那不可能,对吧?所以你不愿意轻易放弃。突然你有了这个讨厌的想法,你正在做一个测试,它起作用了。你不知道该笑还是该哭。

所以现在你试试这个修改,不管你信不信,它确实有效。

class MyHexWrapper(object):
    def __init__(self, hexstr):
        self.hexstr = hexstr
    def __len__(self):
        return len(self.hexstr)
    def __str__(self): # called on its way to the DB
        return binascii.a2b_hex(self.hexstr)
    def __unicode__(self): # called on its way to the admin
        return self.hexstr

它只是工作?好吧,如果你在代码中使用这样的字段,比如 RESTful URL,那么你必须确保你有正确的字符串类型;这是纪律问题。

但是,它仍然只在大多数时候有效。因为当您将这样的字段设为主键时,Django 会调用quote(getattr()),而我发现一个声称getattr()“现在”将使用的消息来源unicode()我无法确认。但是,一旦你走到这一步,这并不是一个严重的障碍,是吗?

class MyModel((models.Model):
    myhex = MyHexField(max_length=32,primary_key=True,editable=False)
    # other fields
    def __getattribute__(self, name):
        if (name == 'myhex'):
            return unicode(super(MyModel, self).__getattribute__(name))
        return super(MyModel, self).__getattribute__(name)

奇迹般有效。但是,现在您向后倾斜并从整体上查看您的解决方案。而且您不禁想到这是对您提到的文档的转移,它使用了您不打算使用的未记录或内部行为特征,并且容易出错并且对开发人员显示出较差的可用性由于您必须实施和遵守的内容的某种分散性质。

那么如何以更清洁的方式实现目标呢?在 Django 中是否有另一个级别的钩子和魔法应该位于这个映射的位置?

感谢您的时间。

4

0 回答 0