[PYTHON] About Django's deconstruct and deconstructible

Overview

--Django has a built-in migration feature since 1.7. -In order to automatically extract differences with ** makemigrations **, it is necessary to work on custom fields and custom validators of Model. --Field requires implementation of ** deconstruct ** method --Validator requires ** deconstructible ** decorator and ** \ _ \ _ eq \ _ \ _ ** method implementation ――Easy to summarize the correspondence contents.

Error encountered

--When I created a custom validator and ran ** makemigrations **, I ran into the following error:

python


There are some values Django cannot serialize into migration files.
For more, see https://docs.djangoproject.com/en/1.9/topics/migrations/#migration-serializing

--When I tried to solve this, I came to the story of ** deconstruct **.

Custom validator

--Class-based custom validators were written as follows before 1.6

# validators.py ----

from django.core.exceptions import ValidationError

class UploadSizeValidator:

    def __init__(self, size=None):
        if size:
            self.size = size

    def __call__(self, value):
        if value.file.size > self.size:
            raise ValidationError('Upload Size Error')

# models.py ----

class Question(models.Model):

    question_image = models.ImageField(
        upload_to='question_image',
        validators=[UploadSizeValidator(size=200)],
        default=None
    )

--However, from 1.7, the above error is displayed unless Validator is implemented as follows.

from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible

@deconstructible # <-Add decorator
class UploadSizeValidator:

    def __init__(self, size=None):
        if size:
            self.size = size

    def __call__(self, value):
        if value.file.size > self.size:
            raise ValidationError('Upload Size Error')

    def __eq__(self, other): # <- __eq__Implement method
        return (
            isinstance(other, self.__class__) and (self.size == other.size)
        )

--This is the first time that ** make migrations ** passes. -The reason why ** \ _ \ _ eq \ _ \ _ ** is necessary is that it is used for difference comparison when generating a migration file. ――Specifically, the following things happen.

1. UploadSizeValidator(size=100)Suppose you created a migration file with
2.Change size to 200 and run makemigrations
3. __eq__Before change in method(other.size=100)And after the change(self.size=200)Compare size
4.AlterField migration files are generated because they do not match

――The content of the actually generated migration looks like this

operations = [
    migrations.AlterField(
        model_name='question',
        name='question_image',
        field=models.ImageField(
          default=None, upload_to='question_image',
          validators=[polls.validators.UploadSizeValidator(size=200)]
        ),
    ),

--By doing this, the contents of the validator can also be migrated or returned with backwards. ――By the way, function-based validators do not have anything that can be compared by difference, so it is the same as the writing method so far. --AlterField is only generated when specified as validators

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError(
            _('%(value)s is not an even number'),
            params={'value': value},
        )

Custom field

--When creating a custom field, you need to implement the ** deconstruct ** method. -** deconstruct ** has a role similar to ** \ _ \ _ eq \ _ \ _ ** above --That is, it returns information to compare whether the Field has changed. --Information is field name (name), field path (path), Field argument to constructor (args), keyword argument (kwargs)

class HandField(models.Field):

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super(HandField, self).__init__(*args, **kwargs)

    def deconstruct(self):  # <- deconstruct
        name, path, args, kwargs = super(HandField, self).deconstruct()
        del kwargs["max_length"]
        return name, path, args, kwargs

# refs https://docs.djangoproject.com/ja/1.9/howto/custom-model-fields/#field-deconstruction

Summary

--Custom validators and special work to create custom fields are required --** deconstructible ** is also required when creating a custom storage class

reference

Recommended Posts

About Django's deconstruct and deconstructible
About _ and __
About Django's ProxyModel
About Django's Model.save () behavior and MySQL deadlock error
About Class and Instance
About cumprod and cummax
About cross-validation and F-number
This and that about pd.DataFrame
About python objects and classes
About Python variables and objects
About Raid group and LUN
Beginner notes about django's setting.py
About fork () function and execve () function
About Python, len () and randint ()
About Python datetime and timezone
About Sharpe Ratio and Sortino Ratio
About Python and regular expressions
About Python and os operations
About http.Handle () and http.NewServeMux (). Handle ()
Python # About reference and copy
About Numpy array and asarray
About Python sort () and reverse ()
Django's MVT-Relationship between Models and Modules-
About installing Pwntools and Python2 series
Summary and common errors about cron
About python dict and sorted functions
About dtypes in Python and Cython
About MkDocs themes and their customs
About Python pickle (cPickle) and marshal
[Python] About Executor and Future classes
About Python, from and import, as
About time series data and overfitting