Browse files

Field.deconstruct() howto docs

  • Loading branch information...
1 parent eece3c2 commit 19b34fbe63a8ec5edd61777b7ff681370cff14f2 @andrewgodwin andrewgodwin committed Nov 27, 2013
Showing with 89 additions and 0 deletions.
  1. +89 −0 docs/howto/custom-model-fields.txt
@@ -230,6 +230,95 @@ All of the options without an explanation in the above list have the same
meaning they do for normal Django fields. See the :doc:`field documentation
</ref/models/fields>` for examples and details.
+Field deconstruction
+.. versionadded:: 1.7
+ ``deconstruct()`` is part of the migrations framework in Django 1.7 and
+ above. If you have custom fields from previous versions they will
+ need this method added before you can use them with migrations.
+The counterpoint to writing your ``__init__`` method is writing the
+``deconstruct`` method. This method tells Django how to take an instance
+of your new field and reduce it to a serialized form - in particular, what
+arguments to pass to ``__init__`` to re-create it.
+If you haven't added any extra options on top of the field you inherited from,
+then there's no need to write a new ``deconstruct`` method. If, however, you're
+changing the arguments passed in ``__init__`` (like we are in ``HandField``),
+you'll need to supplement the values being passed.
+The contract of ``deconstruct`` is simple; it returns a tuple of four items:
+the field's attribute name, the full import path of the field class, the
+position arguments (as a list), and the keyword arguments (as a dict).
+As a custom field author, you don't need to care about the first two values;
+the base ``Field`` class has all the code to work out the field's attribute
+name and import path. You do, however, have to care about the positional
+and keyword arguments, as these are likely the things you are changing.
+For example, in our ``HandField`` class we're always forcibly setting
+max_length in ``__init__``. The ``deconstruct`` method on the base ``Field``
+class will see this and try to return it in the keyword arguments; thus,
+we can drop it from the keyword arguments for readability::
+ from django.db import models
+ class HandField(models.Field):
+ def __init__(self, *args, **kwargs):
+ kwargs['max_length'] = 104
+ super(HandField, self).__init__(*args, **kwargs)
+ def deconstruct(self):
+ name, path, args, kwargs = super(HandField, self).deconstruct()
+ del kwargs["max_length"]
+ return name, path, args, kwargs
+If you add a new keyword argument, you need to write code to put its value
+into ``kwargs`` yourself::
+ from django.db import models
+ class CommaSepField(models.Field):
+ "Implements comma-separated storage of lists"
+ def __init__(self, separator=",", *args, **kwargs):
+ self.separator = ","

mfogel May 3, 2014


should this read self.separator = separator ?


andrewgodwin May 3, 2014


It should indeed. I'll fix it.

+ super(CommaSepField, self).__init__(*args, **kwargs)
+ def deconstruct(self):
+ name, path, args, kwargs = super(CommaSepField, self).deconstruct()
+ # Only include kwarg if it's not the default
+ if self.separator != ",":
+ kwargs['separator'] = self.separator
+ return name, path, args, kwargs
+More complex examples are beyond the scope of this document, but remember -
+for any configuration of your Field instance, ``deconstruct`` must return
+arguments that you can pass to ``__init__`` to reconstruct that state.
+Pay extra attention if you set new default values for arguments in the
+``Field`` superclass; you want to make sure they're always included, rather
+than disappearing if they take on the old default value.
+In addition, try to avoid returning values as positional arguments; where
+possible, return values as keyword arguments for maximum future compatability.
+Of course, if you change the names of things more often than their position
+in the constructor's argument list, you might prefer positional, but bear in
+mind that people will be reconstructing your field from the serialized version
+for quite a while (possibly years), depending how long your migrations live for.
+You can see the results of deconstruction by looking in migrations that include
+the field, and you can test deconstruction in unit tests by just deconstructing
+and reconstructing the field::
+ name, path, args, kwargs = my_field_instance.deconstruct()
+ new_instance = MyField(*args, **kwargs)
+ self.assertEqual(my_field_instance.some_attribute, new_instance.some_attribute)
The ``SubfieldBase`` metaclass

0 comments on commit 19b34fb

Please sign in to comment.