Django模型可以使用MySQL函数吗?

时间:2009-11-13 13:56:25

标签: python mysql django django-models

每次读取或加载模型数据时,有没有办法强制Django模型将字段传递给MySQL函数?为了澄清我在SQL中的含义,我希望Django模型能够产生如下内容:

在模型加载时:SELECT AES_DECRYPT(fieldname,password)FROM tablename

在模型保存上:INSERT INTO tablename VALUES(AES_ENCRYPT(userinput,password))

6 个答案:

答案 0 :(得分:6)

您可以在模型上创建属性,而不是在模型加载上,当访问该属性时,它可以读取数据库:

def _get_foobar(self):
    if not hasattr(self, '_foobar'):

        cursor = connection.cursor()
        self._foobar = cursor.execute('SELECT AES_DECRYPT(fieldname, password) FROM tablename')[0]
    return self._foobar
foobar = property(_get_foobar)

现在加载后,您可以引用mything.foobar,第一次访问将从数据库中检索解密,并保留以供以后访问。

这也有一个好处,如果你的一些代码没有用于解密,它就不会发生。

答案 1 :(得分:4)

我会为您想要加密/解密的列定义custom modelfield。在加载模型时覆盖to_python方法以运行解密,并在保存时覆盖get_db_prep_value以运行加密。

请记住将字段的元类设置为models.SubfieldBase,否则不会调用这些方法。

答案 2 :(得分:3)

这是一个有效的解决方案,部分基于(http://www.djangosnippets.org/snippets/824/):

class Employee(models.Model):
   social_security_number = models.CharField(max_length=32)

   def _get_ssn(self):
       cursor = connection.cursor()
       cursor.execute("SELECT AES_DECRYPT(UNHEX(social_security_number), %s) as ssn FROM tablename WHERE id=%s", [settings.SECRET_KEY, self.id])
       return cursor.fetchone()[0]

   def _set_ssn(self, ssn_value):
       cursor = connection.cursor()
       cursor.execute("SELECT HEX(AES_ENCRYPT(%s, %s)) as ssn", [ssn_value, settings.SECRET_KEY])
       self.social_security_number = cursor.fetchone()[0]

   ssn = property(_get_ssn, _set_ssn)

结果:

>>> from foo.bar.models import Employee
>>> p=Employee.objects.create(ssn='123-45-6789')
>>> p.ssn
'123-45-6789'

mysql> select * from foo_employee;
+----+----------------------------------+
| id | social_security_number           |
+----+----------------------------------+
| 31 | 41DF2D946C9186BEF77DD3307B85CC8C |
+----+----------------------------------+
1 row in set (0.00 sec)

答案 3 :(得分:1)

这绝对是hackish,但似乎Django现在不会让你以任何其他方式做到这一点。值得注意的是,每次更改python中的值时,除了首次加载时,都会调用to_python。

from django.db import connection, models
import re

class EncryptedField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        if not re.match('^*some pattern here*$', value):
            cursor = connection.cursor()
            cursor.execute('SELECT AES_DECRYPT(%s, %s)', [value, settings.SECRET_KEY])
            return cursor.fetchone()[0]
        return value

    def get_db_prep_value(self, value):
        cursor = connection.cursor()
        cursor.execute('SELECT AES_ENCRYPT(%s, %s)', [value, settings.SECRET_KEY])
        return cursor.fetchone()[0]


class Encrypt(models.Model):
    encrypted = EncryptedField(max_length = 32)

答案 4 :(得分:1)

深入搜索Django ORM的实现后,

我发现它可以通过以下方式解决:

class EncryptedField(models.BinaryField):
    @staticmethod
    def _pad(value):
        return value + (AES.block_size - len(value) % AES.block_size) * b('\x00')

    def _encrypt(self, data):
        if not data:
            return None
        return self.cipher.encrypt(self._pad(data.encode('utf8')))

    def _decrypt(self, data):
        if not data:
            return None
        return self.cipher.decrypt(force_bytes(data)).rstrip(b'\x00').decode('utf8')

    @property
    def cipher(self):
        return AES.new(KEY, mode=AES.MODE_CBC, IV=self._iv)

    def get_db_prep_value(self, value, connection, prepared=False):
        if value is not None:
            value = self._encrypt(value)
            if value:
                value = binascii.hexlify(value)
        return value

    def get_placeholder(self, value, compiler, connection):
        return 'unhex(%s)'

答案 5 :(得分:0)

使用Django signals,您可以在保存模型实例时执行操作,但据我所知,您无法在读取时触发任何内容。

编辑:我的不好,似乎你可以在初始化模型实例时做些事情。