Elegant implementation of Django Model field encryption

An earlier article Example of password management table developed by Django Yes, we wrote a password management tool to manage the password. At that time, the encryption and decryption function was implemented in the view layer and has been running stably, so we didn't pay too much attention to the elegance of the implementation. Recently, we need to add a few more password tables. Looking back at the previous code, we find that the implementation of encryption and decryption in the view layer is cumbersome, especially when it is used Sadmin Public Library After that, the code of view is much simpler. If it is not elegant enough to process in the view layer, it is time to implement it in a more elegant way

Sadmin addition, deletion, modification and query

Adding, deleting, modifying and querying database tables is the most commonly used function in the development process. The previous article also introduced that we use encapsulation Sadmin Public Library The most concise code is used to add, delete, change and query the table. See the code below for how concise it is

class TableList(ListCreateView):
    model = Table
    template = 'password/table.html'
    permission = {'get': 'password.select_table', 'post': 'password.create_table'}

class TableDetail(RetrieveUpdateDestroyView):
    model = Table
    permission = {'get': 'password.select_table', 'put': 'password.update_table',
                  'delete': 'password.delete_table'}

TableList class can query the table and create new table data. TableDetail can query, modify and delete a single piece of data in the table, corresponding to two URL s

path('table/', views.TableList.as_view(), name='table-list-url'),
path('table/<int:pk>/', views.TableDetail.as_view(), name='table-detail-url'),

If the encryption of table fields is implemented in the view layer, it is very troublesome to rewrite the post method of TableList and the put method of TableDetail class. What is more elegant? It is better to process the table fields when the table changes. It is obviously more appropriate to implement them directly in the model layer than in the view layer. If they are implemented in the model layer, they can be processed through Django's signals or Override the save method of the model Are good choices

As for whether to use signals or rewrite the save method, both can be implemented. Personally, I think it is better to rewrite save for simple processing logic, and clearer to use signals for complex processing logic. For our requirement of encrypting fields, we don't need too many simple logic codes. It's better to directly rewrite save

Override the save method of the model

For the core code of encryption and decryption, please refer to the article Example of password management table developed by Django Given the source code, rewrite the save method of model, and the code is as follows

class Table(models.Model):
    username = models.CharField(max_length=64, verbose_name='user name')
    password = models.CharField(max_length=512, verbose_name='password')

    def __str__(self):
        return self.application_name

    def save(self, *args, **kwargs):
        _encrypt = True

        if self.pk:
            old_password = Table.objects.get(id=self.id)
            _encrypt = False if old_password.password == self.password else True

        if _encrypt:
            _m = RsaCrypto().encrypt(self.password)
            if _m.get('state'):
                self.password = _m.get('message')
                raise Exception('Encryption failed:' + _m.get('message'))

        super(Table, self).save(*args, **kwargs)

Password encryption is usually performed when a new record is added for the first time and the password of the updated record changes

How to determine whether it is insert or update when saving? You can judge whether self.pk exists. Django's model must have a field as the primary key. If the user does not set the primary key field, Django will create a field named id as the primary key by default, and the primary key is also represented by PK alias. Therefore, you can judge whether the save is insert or update by whether self.pk exists

When this save is update, we also need to judge whether the password field has changed. If there is no change, we do not need to call the encryption method. To judge whether the field has changed, we need to obtain the value before the field submission. The value before submission can be obtained through Table.objects.get(id=self.id)

With the above information, encryption is natural. We have implemented the field encryption gracefully. What about decryption? Personally, I think it's more appropriate to put it in the model than in veiw. You can add a decode in mdel_ Password method

class Table(models.Model):
    def decode_password(self):
        _m = RsaCrypto().decrypt(self.password)
        if _m.get('state'):
            return {
                'state': 1,
                'message': _m.get('message'),
                'username': self.username
            return {'state': 0, 'message': 'Error: ' + _m.get('message')}

Call the Model's decode when decryption is required_ The password method is OK

def decode_password(request, pk):
        _t = Table.objects.get(id=pk)
        return JsonResponse(_t.decode_password())
    except Exception as e:
        return JsonResponse({'state': 0, 'message': 'Error: ' + str(e)})

Write at the end

I have a little cleanliness of the code, and the implementation functions are mainly simple and practical. Those who can handle it in two lines will never write three lines. If they can have a better solution, they will not hesitate to reconstruct. At the same time, they firmly oppose the saying that "it is not unusable". Welcome to discuss whether the above implementation is elegant or has a better solution

Tags: Python Django Back-end

Posted on Sun, 24 Oct 2021 21:00:18 -0400 by ShiloVir