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:
init
arguments)?I don't want to blindly copy-paste code that I don't understand, but the documentation is not clear.
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.
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.
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.
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.
One of the core developers responded that this is intended behavior, because our field contains a callable.
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:
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With