允许Django自定义字段在父模型上触发save()

时间:2015-08-20 02:46:47

标签: python django django-models django-orm

问题:

我有一个自定义字段类型,偶尔需要更新其属性并将其自身重新保存到数据库。我正在尝试确定是否可以在字段层强制执行此操作而不在模型层实现。

动机:

我查看了django.contrib.auth handles workfactor upgrades的方法,并将此任务降级为拥有加密字段的模型。

我更喜欢在字段级别实现此功能,以便可以在各种应用程序的许多模型上实现自定义字段类型。

实施例

我有一个自定义字段类型SecretField,它将类型Secret存储为模型属性。通常,Secret包含加密数据,密钥是用户提供的密码的PBKDF2哈希。根据需要进行解密。

我们假设这是通过以下概述实现的:

from django.utils.six import with_metaclass
from django.db import models

class BuriedTreasure(models.Model):
    location = SecretField()

class SecretField(with_metaclass(models.SubfieldBase, models.Field)):
    #... (a bunch of logic) ...

class Secret(object):
    def __init__(self, ciphertext, salt, workfactor):
        self.ciphertext = ciphertext
        self.salt = salt
        self.workfactor = workfactor

    @classmethod
    def from_ciphertext(cls, ciphertext, salt, workfactor):
        """Generate a Secret object for later decyption."""
        return cls(ciphertext, salt, workfactor)

    def to_plaintext(self, password):
        """Decrypt ciphertext and return plaintext."""
        #... (decrypt ciphertext using salt & workfactor) ...
        return plaintext

bt = BuriedTreasure.objects.get(pk=1)
print bt.location.to_plaintext('mypassword')

调用bt.location.to_plaintext()时,我想检查bt.location.workfactor是否不再符合Django要求(由于Django升级而可能已更改)。如果没有,我应该重新加密Secret并发出bt.save(update_fields=['location'])以升级秘密的保护。此解决方案允许所有SecretField属性在解密时自动升级其工作因素。

我想实施的内容:

我想通过以下方式实现这一点:

class Secret(object):
    def __init__(self, ciphertext, salt, workfactor, plaintext=None, password=None):
        self.ciphertext = ciphertext
        self.salt = salt
        self.workfactor = workfactor
        #...

    @classmethod
    def from_ciphertext(cls, ciphertext, salt, workfactor):
        """Generate a Secret object for later decyption."""
        return cls(ciphertext, salt, workfactor)

    def from_plaintext(cls, plaintext, password):
        """Generate a Secret object from plaintext and password."""
        return cls(plaintext=plaintext, password=password)

    def to_plaintext(self, password):
        """Decrypt ciphertext and return plaintext."""
        #... (decrypt ciphertext using salt & workfactor) ...

        if self.workfactor < DJANGO_THRESHOLD:
            new_secret = Secret.from_plaintext(plaintext, password)
            #Update parent model's field with new_secret
            #Save parent model's field to database
        return plaintext

我被困的地方

目前,我无法弄清楚如何访问Secret所属的模型实例。即使我在SecretField内注册了Secret SecretField.from_db_value()实例,我找不到访问父模型的方法。这是可能的,还是有更好的方法来实现我的目标(也许是通过在通用的可继承模型上实现)?

0 个答案:

没有答案