Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the purpose of Django "deconstruct" model field function?

Tags:

python

django

In order to develop custom Django model fields, I'm reading the documentation.

I have already developed my custom field (which is almost equal to that of the example, HandField: a field mapped over a Python class... With the only difference that I inherit from models.CharField and not models.Field).

from external_library import ExternalClass

class ExternalClassField(models.CharField):
    description = "An ExternalClass field"

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

    def from_db_value(self, value, expression, connection, context):
        if value is None:
            return value
        return ExternalClass(value)

    def to_python(self, value):
        if isinstance(value, ExternalClass):
            return value

        if value is None:
            return value

        return ExternalClass(value)

    def get_prep_value(self, value):
        if value is None:
            return value

        if isinstance(value, ExternalClass):
            return value.to_string()

        return value

The field behave as expected. However, I'm stuck at this part of the documentation: the deconstruct() function.

In particular, what I don't understand is this:

  • What exactly is the purpose of the deconstruct function?
  • How come my field works perfectly well even without it (and even if I modify the init arguments)?
  • How and when is that Django calls the deconstruct function?

I don't want to blindly copy-paste code that I don't understand, but the documentation is not clear.

like image 797
Saturnix Avatar asked Jan 01 '16 17:01

Saturnix


People also ask

What is the purpose of the models py file in Django?

Django web applications access and manage data through Python objects referred to as models. Models define the structure of stored data, including the field types and possibly also their maximum size, default values, selection list options, help text for documentation, label text for forms, etc.

What is a field in Django?

Fields in Django are the data types to store a particular type of data. For example, to store an integer, IntegerField would be used. These fields have in-built validation for a particular data type, that is you can not store “abc” in an IntegerField. Similarly, for other fields.

How do I add a field to a Django model?

To answer your question, with the new migration introduced in Django 1.7, in order to add a new field to a model you can simply add that field to your model and initialize migrations with ./manage.py makemigrations and then run ./manage.py migrate and the new field will be added to your DB.


1 Answers

The deconstruct() method is used to help perform model migrations that aren't able to automatically be handled by the system. Let's walk through a scenario where deconstruct would get called.

Let's say we had some model, and we added a custom field to it. The we try to migrate with python manage.py makemigrations.

We encounter the following error:

ValueError: Cannot serialize: Foo
There are some values Django cannot serialize into migration files.

It turns out that there's already a related ticket that's been filed with the Django Project, let's check it out.

ticket-issue

One of the core developers responded that this is intended behavior, because our field contains a callable.

ticket-resolution

So, we missed something in the documentation. There's a callable value being stored, and it can't be automatically migrated for some reason. What can we do?

Well, in addition to telling us about the ValueError, manage.py also gave us a useful link to the documentation:

docs-link

Once on that page, scroll down a bit, until we get to the section about serializing values.

Django can serialize the following:

  • ...
  • Anything with a custom deconstruct() method (see below)
  • ...

Well, let's see below:

You can let Django serialize your own custom class instances by giving the class a deconstruct() method. It takes no arguments, and should return a tuple of three things (path, args, kwargs):

  • path should be the Python path to the class, with the class name included as the last part (for example, myapp.custom_things.MyClass). If your class is not available at the top level of a module it is not serializable.
  • args should be a list of positional arguments to pass to your class’ init method. Everything in this list should itself be serializable.
  • kwargs should be a dict of keyword arguments to pass to your class’ init method. Every value should itself be serializable.

Note that the deconstruct() method works hand in hand with __eq__(), as stated by the documentation:

To prevent a new migration from being created each time makemigrations is run, you should also add a __eq__() method to the decorated class. This function will be called by Django’s migration framework to detect changes between states.

In my case, the mistake was adding parenthesis after a value that should not have been called, but in many cases you'll want to implement that deconstruct method for migrations. (Here's another useful link that has an example.)

like image 101
Moshe Avatar answered Sep 29 '22 00:09

Moshe