Могут ли модели Django использовать функции MySQL? - PullRequest
4 голосов
/ 13 ноября 2009

Есть ли способ заставить модели Django передавать поле в функцию MySQL каждый раз, когда данные модели читаются или загружаются? Чтобы пояснить, что я имею в виду в SQL, я хочу, чтобы модель Django выглядела примерно так:

При загрузке модели: ВЫБЕРИТЕ AES_DECRYPT (имя поля, пароль) ОТ имени таблицы

При сохранении модели: INSERT INTO VALUES namename (AES_ENCRYPT (userinput, password))

Ответы [ 6 ]

6 голосов
/ 13 ноября 2009

Вместо загрузки модели вы можете создать свойство для вашей модели, а когда оно будет доступно, оно может прочитать базу данных:

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, и при первом доступе расшифровка будет извлечена из базы данных, удерживая ее для последующих обращений.

Это также имеет то преимущество, что если часть вашего кода не будет использоваться для расшифровки, это не произойдет.

4 голосов
/ 13 ноября 2009

Я бы определил настраиваемое поле модели для столбца, который вы хотите зашифровать / расшифровать. Переопределите метод to_python для запуска дешифрования при загрузке модели и get_db_prep_value для запуска шифрования при сохранении.

Не забудьте установить метакласс поля на models.SubfieldBase, иначе эти методы не будут вызываться.

3 голосов
/ 13 ноября 2009

Вот рабочее решение, частично основанное на (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)
1 голос
/ 03 сентября 2017

После глубокого поиска в реализации 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)'
1 голос
/ 13 ноября 2009

Это определенно хакерский, но, похоже, Django не позволит вам сделать это по-другому. Стоит также отметить, что to_python будет вызываться каждый раз, когда вы меняете значение в 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)
0 голосов
/ 13 ноября 2009

Используя сигналы Django , вы можете делать что-то, когда экземпляр модели сохраняется, но, насколько я знаю, вы ничего не можете вызвать при чтении.

РЕДАКТИРОВАТЬ: мой плохой, кажется, вы можете делать вещи при инициализации экземпляра модели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...