我有一个自定义字段类型,偶尔需要更新其属性并将其自身重新保存到数据库。我正在尝试确定是否可以在字段层强制执行此操作而不在模型层实现。
我查看了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()
实例,我找不到访问父模型的方法。这是可能的,还是有更好的方法来实现我的目标(也许是通过在通用的可继承模型上实现)?