diff options
Diffstat (limited to 'parts/django/docs/howto/custom-model-fields.txt')
-rw-r--r-- | parts/django/docs/howto/custom-model-fields.txt | 745 |
1 files changed, 0 insertions, 745 deletions
diff --git a/parts/django/docs/howto/custom-model-fields.txt b/parts/django/docs/howto/custom-model-fields.txt deleted file mode 100644 index 1840c5b..0000000 --- a/parts/django/docs/howto/custom-model-fields.txt +++ /dev/null @@ -1,745 +0,0 @@ -=========================== -Writing custom model fields -=========================== - -.. versionadded:: 1.0 -.. currentmodule:: django.db.models - -Introduction -============ - -The :doc:`model reference </topics/db/models>` documentation explains how to use -Django's standard field classes -- :class:`~django.db.models.CharField`, -:class:`~django.db.models.DateField`, etc. For many purposes, those classes are -all you'll need. Sometimes, though, the Django version won't meet your precise -requirements, or you'll want to use a field that is entirely different from -those shipped with Django. - -Django's built-in field types don't cover every possible database column type -- -only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure -column types, such as geographic polygons or even user-created types such as -`PostgreSQL custom types`_, you can define your own Django ``Field`` subclasses. - -.. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html - -Alternatively, you may have a complex Python object that can somehow be -serialized to fit into a standard database column type. This is another case -where a ``Field`` subclass will help you use your object with your models. - -Our example object ------------------- - -Creating custom fields requires a bit of attention to detail. To make things -easier to follow, we'll use a consistent example throughout this document: -wrapping a Python object representing the deal of cards in a hand of Bridge_. -Don't worry, you don't have know how to play Bridge to follow this example. -You only need to know that 52 cards are dealt out equally to four players, who -are traditionally called *north*, *east*, *south* and *west*. Our class looks -something like this:: - - class Hand(object): - """A hand of cards (bridge style)""" - - def __init__(self, north, east, south, west): - # Input parameters are lists of cards ('Ah', '9s', etc) - self.north = north - self.east = east - self.south = south - self.west = west - - # ... (other possibly useful methods omitted) ... - -.. _Bridge: http://en.wikipedia.org/wiki/Contract_bridge - -This is just an ordinary Python class, with nothing Django-specific about it. -We'd like to be able to do things like this in our models (we assume the -``hand`` attribute on the model is an instance of ``Hand``):: - - example = MyModel.objects.get(pk=1) - print example.hand.north - - new_hand = Hand(north, east, south, west) - example.hand = new_hand - example.save() - -We assign to and retrieve from the ``hand`` attribute in our model just like -any other Python class. The trick is to tell Django how to handle saving and -loading such an object. - -In order to use the ``Hand`` class in our models, we **do not** have to change -this class at all. This is ideal, because it means you can easily write -model support for existing classes where you cannot change the source code. - -.. note:: - You might only be wanting to take advantage of custom database column - types and deal with the data as standard Python types in your models; - strings, or floats, for example. This case is similar to our ``Hand`` - example and we'll note any differences as we go along. - -Background theory -================= - -Database storage ----------------- - -The simplest way to think of a model field is that it provides a way to take a -normal Python object -- string, boolean, ``datetime``, or something more -complex like ``Hand`` -- and convert it to and from a format that is useful -when dealing with the database (and serialization, but, as we'll see later, -that falls out fairly naturally once you have the database side under control). - -Fields in a model must somehow be converted to fit into an existing database -column type. Different databases provide different sets of valid column types, -but the rule is still the same: those are the only types you have to work -with. Anything you want to store in the database must fit into one of -those types. - -Normally, you're either writing a Django field to match a particular database -column type, or there's a fairly straightforward way to convert your data to, -say, a string. - -For our ``Hand`` example, we could convert the card data to a string of 104 -characters by concatenating all the cards together in a pre-determined order -- -say, all the *north* cards first, then the *east*, *south* and *west* cards. So -``Hand`` objects can be saved to text or character columns in the database. - -What does a field class do? ---------------------------- - -All of Django's fields (and when we say *fields* in this document, we always -mean model fields and not :doc:`form fields </ref/forms/fields>`) are subclasses -of :class:`django.db.models.Field`. Most of the information that Django records -about a field is common to all fields -- name, help text, uniqueness and so -forth. Storing all that information is handled by ``Field``. We'll get into the -precise details of what ``Field`` can do later on; for now, suffice it to say -that everything descends from ``Field`` and then customizes key pieces of the -class behavior. - -It's important to realize that a Django field class is not what is stored in -your model attributes. The model attributes contain normal Python objects. The -field classes you define in a model are actually stored in the ``Meta`` class -when the model class is created (the precise details of how this is done are -unimportant here). This is because the field classes aren't necessary when -you're just creating and modifying attributes. Instead, they provide the -machinery for converting between the attribute value and what is stored in the -database or sent to the :doc:`serializer </topics/serialization>`. - -Keep this in mind when creating your own custom fields. The Django ``Field`` -subclass you write provides the machinery for converting between your Python -instances and the database/serializer values in various ways (there are -differences between storing a value and using a value for lookups, for -example). If this sounds a bit tricky, don't worry -- it will become clearer in -the examples below. Just remember that you will often end up creating two -classes when you want a custom field: - - * The first class is the Python object that your users will manipulate. - They will assign it to the model attribute, they will read from it for - displaying purposes, things like that. This is the ``Hand`` class in our - example. - - * The second class is the ``Field`` subclass. This is the class that knows - how to convert your first class back and forth between its permanent - storage form and the Python form. - -Writing a field subclass -======================== - -When planning your :class:`~django.db.models.Field` subclass, first give some -thought to which existing :class:`~django.db.models.Field` class your new field -is most similar to. Can you subclass an existing Django field and save yourself -some work? If not, you should subclass the :class:`~django.db.models.Field` -class, from which everything is descended. - -Initializing your new field is a matter of separating out any arguments that are -specific to your case from the common arguments and passing the latter to the -:meth:`~django.db.models.Field.__init__` method of -:class:`~django.db.models.Field` (or your parent class). - -In our example, we'll call our field ``HandField``. (It's a good idea to call -your :class:`~django.db.models.Field` subclass ``<Something>Field``, so it's -easily identifiable as a :class:`~django.db.models.Field` subclass.) It doesn't -behave like any existing field, so we'll subclass directly from -:class:`~django.db.models.Field`:: - - from django.db import models - - class HandField(models.Field): - - description = "A hand of cards (bridge style)" - - def __init__(self, *args, **kwargs): - kwargs['max_length'] = 104 - super(HandField, self).__init__(*args, **kwargs) - -Our ``HandField`` accepts most of the standard field options (see the list -below), but we ensure it has a fixed length, since it only needs to hold 52 -card values plus their suits; 104 characters in total. - -.. note:: - Many of Django's model fields accept options that they don't do anything - with. For example, you can pass both - :attr:`~django.db.models.Field.editable` and - :attr:`~django.db.models.Field.auto_now` to a - :class:`django.db.models.DateField` and it will simply ignore the - :attr:`~django.db.models.Field.editable` parameter - (:attr:`~django.db.models.Field.auto_now` being set implies - ``editable=False``). No error is raised in this case. - - This behavior simplifies the field classes, because they don't need to - check for options that aren't necessary. They just pass all the options to - the parent class and then don't use them later on. It's up to you whether - you want your fields to be more strict about the options they select, or - to use the simpler, more permissive behavior of the current fields. - -The :meth:`~django.db.models.Field.__init__` method takes the following -parameters: - - * :attr:`~django.db.models.Field.verbose_name` - * :attr:`~django.db.models.Field.name` - * :attr:`~django.db.models.Field.primary_key` - * :attr:`~django.db.models.Field.max_length` - * :attr:`~django.db.models.Field.unique` - * :attr:`~django.db.models.Field.blank` - * :attr:`~django.db.models.Field.null` - * :attr:`~django.db.models.Field.db_index` - * :attr:`~django.db.models.Field.rel`: Used for related fields (like - :class:`ForeignKey`). For advanced use only. - * :attr:`~django.db.models.Field.default` - * :attr:`~django.db.models.Field.editable` - * :attr:`~django.db.models.Field.serialize`: If ``False``, the field will - not be serialized when the model is passed to Django's :doc:`serializers - </topics/serialization>`. Defaults to ``True``. - * :attr:`~django.db.models.Field.unique_for_date` - * :attr:`~django.db.models.Field.unique_for_month` - * :attr:`~django.db.models.Field.unique_for_year` - * :attr:`~django.db.models.Field.choices` - * :attr:`~django.db.models.Field.help_text` - * :attr:`~django.db.models.Field.db_column` - * :attr:`~django.db.models.Field.db_tablespace`: Currently only used with - the Oracle backend and only for index creation. You can usually ignore - this option. - * :attr:`~django.db.models.Field.auto_created`: True if the field was - automatically created, as for the `OneToOneField` used by model - inheritance. For advanced use only. - -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. - -The ``SubfieldBase`` metaclass ------------------------------- - -As we indicated in the introduction_, field subclasses are often needed for -two reasons: either to take advantage of a custom database column type, or to -handle complex Python types. Obviously, a combination of the two is also -possible. If you're only working with custom database column types and your -model fields appear in Python as standard Python types direct from the -database backend, you don't need to worry about this section. - -If you're handling custom Python types, such as our ``Hand`` class, we need to -make sure that when Django initializes an instance of our model and assigns a -database value to our custom field attribute, we convert that value into the -appropriate Python object. The details of how this happens internally are a -little complex, but the code you need to write in your ``Field`` class is -simple: make sure your field subclass uses a special metaclass: - -.. class:: django.db.models.SubfieldBase - -For example:: - - class HandField(models.Field): - - description = "A hand of cards (bridge style)" - - __metaclass__ = models.SubfieldBase - - def __init__(self, *args, **kwargs): - # ... - -This ensures that the :meth:`to_python` method, documented below, will always be -called when the attribute is initialized. - -ModelForms and custom fields -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you use :class:`~django.db.models.SubfieldBase`, :meth:`to_python` -will be called every time an instance of the field is assigned a -value. This means that whenever a value may be assigned to the field, -you need to ensure that it will be of the correct datatype, or that -you handle any exceptions. - -This is especially important if you use :doc:`ModelForms -</topics/forms/modelforms>`. When saving a ModelForm, Django will use -form values to instantiate model instances. However, if the cleaned -form data can't be used as valid input to the field, the normal form -validation process will break. - -Therefore, you must ensure that the form field used to represent your -custom field performs whatever input validation and data cleaning is -necessary to convert user-provided form input into a -`to_python()`-compatible model field value. This may require writing a -custom form field, and/or implementing the :meth:`formfield` method on -your field to return a form field class whose `to_python()` returns the -correct datatype. - -Documenting your Custom Field ------------------------------ - -.. class:: django.db.models.Field - -.. attribute:: description - -As always, you should document your field type, so users will know what it is. -In addition to providing a docstring for it, which is useful for developers, -you can also allow users of the admin app to see a short description of the -field type via the :doc:`django.contrib.admindocs -</ref/contrib/admin/admindocs>` application. To do this simply provide -descriptive text in a ``description`` class attribute of your custom field. In -the above example, the type description displayed by the ``admindocs`` -application for a ``HandField`` will be 'A hand of cards (bridge style)'. - -Useful methods --------------- - -Once you've created your :class:`~django.db.models.Field` subclass and set up -the ``__metaclass__``, you might consider overriding a few standard methods, -depending on your field's behavior. The list of methods below is in -approximately decreasing order of importance, so start from the top. - -Custom database types -~~~~~~~~~~~~~~~~~~~~~ - -.. method:: db_type(self, connection) - -.. versionadded:: 1.2 - The ``connection`` argument was added to support multiple databases. - -Returns the database column data type for the :class:`~django.db.models.Field`, -taking into account the connection object, and the settings associated with it. - -Say you've created a PostgreSQL custom type called ``mytype``. You can use this -field with Django by subclassing ``Field`` and implementing the :meth:`db_type` -method, like so:: - - from django.db import models - - class MytypeField(models.Field): - def db_type(self, connection): - return 'mytype' - -Once you have ``MytypeField``, you can use it in any model, just like any other -``Field`` type:: - - class Person(models.Model): - name = models.CharField(max_length=80) - gender = models.CharField(max_length=1) - something_else = MytypeField() - -If you aim to build a database-agnostic application, you should account for -differences in database column types. For example, the date/time column type -in PostgreSQL is called ``timestamp``, while the same column in MySQL is called -``datetime``. The simplest way to handle this in a ``db_type()`` method is to -check the ``connection.settings_dict['ENGINE']`` attribute. - -For example:: - - class MyDateField(models.Field): - def db_type(self, connection): - if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql': - return 'datetime' - else: - return 'timestamp' - -The :meth:`db_type` method is only called by Django when the framework -constructs the ``CREATE TABLE`` statements for your application -- that is, when -you first create your tables. It's not called at any other time, so it can -afford to execute slightly complex code, such as the ``connection.settings_dict`` -check in the above example. - -Some database column types accept parameters, such as ``CHAR(25)``, where the -parameter ``25`` represents the maximum column length. In cases like these, -it's more flexible if the parameter is specified in the model rather than being -hard-coded in the ``db_type()`` method. For example, it wouldn't make much -sense to have a ``CharMaxlength25Field``, shown here:: - - # This is a silly example of hard-coded parameters. - class CharMaxlength25Field(models.Field): - def db_type(self, connection): - return 'char(25)' - - # In the model: - class MyModel(models.Model): - # ... - my_field = CharMaxlength25Field() - -The better way of doing this would be to make the parameter specifiable at run -time -- i.e., when the class is instantiated. To do that, just implement -:meth:`django.db.models.Field.__init__`, like so:: - - # This is a much more flexible example. - class BetterCharField(models.Field): - def __init__(self, max_length, *args, **kwargs): - self.max_length = max_length - super(BetterCharField, self).__init__(*args, **kwargs) - - def db_type(self, connection): - return 'char(%s)' % self.max_length - - # In the model: - class MyModel(models.Model): - # ... - my_field = BetterCharField(25) - -Finally, if your column requires truly complex SQL setup, return ``None`` from -:meth:`db_type`. This will cause Django's SQL creation code to skip over this -field. You are then responsible for creating the column in the right table in -some other way, of course, but this gives you a way to tell Django to get out of -the way. - -Converting database values to Python objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: to_python(self, value) - -Converts a value as returned by your database (or a serializer) to a Python -object. - -The default implementation simply returns ``value``, for the common case in -which the database backend already returns data in the correct format (as a -Python string, for example). - -If your custom :class:`~django.db.models.Field` class deals with data structures -that are more complex than strings, dates, integers or floats, then you'll need -to override this method. As a general rule, the method should deal gracefully -with any of the following arguments: - - * An instance of the correct type (e.g., ``Hand`` in our ongoing example). - - * A string (e.g., from a deserializer). - - * Whatever the database returns for the column type you're using. - -In our ``HandField`` class, we're storing the data as a VARCHAR field in the -database, so we need to be able to process strings and ``Hand`` instances in -:meth:`to_python`:: - - import re - - class HandField(models.Field): - # ... - - def to_python(self, value): - if isinstance(value, Hand): - return value - - # The string case. - p1 = re.compile('.{26}') - p2 = re.compile('..') - args = [p2.findall(x) for x in p1.findall(value)] - return Hand(*args) - -Notice that we always return a ``Hand`` instance from this method. That's the -Python object type we want to store in the model's attribute. - -**Remember:** If your custom field needs the :meth:`to_python` method to be -called when it is created, you should be using `The SubfieldBase metaclass`_ -mentioned earlier. Otherwise :meth:`to_python` won't be called automatically. - -Converting Python objects to query values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: get_prep_value(self, value) - -.. versionadded:: 1.2 - This method was factored out of ``get_db_prep_value()`` - -This is the reverse of :meth:`to_python` when working with the -database backends (as opposed to serialization). The ``value`` -parameter is the current value of the model's attribute (a field has -no reference to its containing model, so it cannot retrieve the value -itself), and the method should return data in a format that has been -prepared for use as a parameter in a query. - -This conversion should *not* include any database-specific -conversions. If database-specific conversions are required, they -should be made in the call to :meth:`get_db_prep_value`. - -For example:: - - class HandField(models.Field): - # ... - - def get_prep_value(self, value): - return ''.join([''.join(l) for l in (value.north, - value.east, value.south, value.west)]) - -Converting query values to database values -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: get_db_prep_value(self, value, connection, prepared=False) - -.. versionadded:: 1.2 - The ``connection`` and ``prepared`` arguments were added to support multiple databases. - -Some data types (for example, dates) need to be in a specific format -before they can be used by a database backend. -:meth:`get_db_prep_value` is the method where those conversions should -be made. The specific connection that will be used for the query is -passed as the ``connection`` parameter. This allows you to use -backend-specific conversion logic if it is required. - -The ``prepared`` argument describes whether or not the value has -already been passed through :meth:`get_prep_value` conversions. When -``prepared`` is False, the default implementation of -:meth:`get_db_prep_value` will call :meth:`get_prep_value` to do -initial data conversions before performing any database-specific -processing. - -.. method:: get_db_prep_save(self, value, connection) - -.. versionadded:: 1.2 - The ``connection`` argument was added to support multiple databases. - -Same as the above, but called when the Field value must be *saved* to -the database. As the default implementation just calls -``get_db_prep_value``, you shouldn't need to implement this method -unless your custom field needs a special conversion when being saved -that is not the same as the conversion used for normal query -parameters (which is implemented by ``get_db_prep_value``). - -Preprocessing values before saving -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: pre_save(self, model_instance, add) - -This method is called just prior to :meth:`get_db_prep_save` and should return -the value of the appropriate attribute from ``model_instance`` for this field. -The attribute name is in ``self.attname`` (this is set up by -:class:`~django.db.models.Field`). If the model is being saved to the database -for the first time, the ``add`` parameter will be ``True``, otherwise it will be -``False``. - -You only need to override this method if you want to preprocess the value -somehow, just before saving. For example, Django's -:class:`~django.db.models.DateTimeField` uses this method to set the attribute -correctly in the case of :attr:`~django.db.models.Field.auto_now` or -:attr:`~django.db.models.Field.auto_now_add`. - -If you do override this method, you must return the value of the attribute at -the end. You should also update the model's attribute if you make any changes -to the value so that code holding references to the model will always see the -correct value. - -Preparing values for use in database lookups -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As with value conversions, preparing a value for database lookups is a -two phase process. - -.. method:: get_prep_lookup(self, lookup_type, value) - -.. versionadded:: 1.2 - This method was factored out of ``get_db_prep_lookup()`` - -:meth:`get_prep_lookup` performs the first phase of lookup preparation, -performing generic data validity checks - -Prepares the ``value`` for passing to the database when used in a lookup (a -``WHERE`` constraint in SQL). The ``lookup_type`` will be one of the valid -Django filter lookups: ``exact``, ``iexact``, ``contains``, ``icontains``, -``gt``, ``gte``, ``lt``, ``lte``, ``in``, ``startswith``, ``istartswith``, -``endswith``, ``iendswith``, ``range``, ``year``, ``month``, ``day``, -``isnull``, ``search``, ``regex``, and ``iregex``. - -Your method must be prepared to handle all of these ``lookup_type`` values and -should raise either a ``ValueError`` if the ``value`` is of the wrong sort (a -list when you were expecting an object, for example) or a ``TypeError`` if -your field does not support that type of lookup. For many fields, you can get -by with handling the lookup types that need special handling for your field -and pass the rest to the :meth:`get_db_prep_lookup` method of the parent class. - -If you needed to implement ``get_db_prep_save()``, you will usually need to -implement ``get_prep_lookup()``. If you don't, ``get_prep_value`` will be -called by the default implementation, to manage ``exact``, ``gt``, ``gte``, -``lt``, ``lte``, ``in`` and ``range`` lookups. - -You may also want to implement this method to limit the lookup types that could -be used with your custom field type. - -Note that, for ``range`` and ``in`` lookups, ``get_prep_lookup`` will receive -a list of objects (presumably of the right type) and will need to convert them -to a list of things of the right type for passing to the database. Most of the -time, you can reuse ``get_prep_value()``, or at least factor out some common -pieces. - -For example, the following code implements ``get_prep_lookup`` to limit the -accepted lookup types to ``exact`` and ``in``:: - - class HandField(models.Field): - # ... - - def get_prep_lookup(self, lookup_type, value): - # We only handle 'exact' and 'in'. All others are errors. - if lookup_type == 'exact': - return self.get_prep_value(value) - elif lookup_type == 'in': - return [self.get_prep_value(v) for v in value] - else: - raise TypeError('Lookup type %r not supported.' % lookup_type) - -.. method:: get_db_prep_lookup(self, lookup_type, value, connection, prepared=False) - -.. versionadded:: 1.2 - The ``connection`` and ``prepared`` arguments were added to support multiple databases. - -Performs any database-specific data conversions required by a lookup. -As with :meth:`get_db_prep_value`, the specific connection that will -be used for the query is passed as the ``connection`` parameter. -The ``prepared`` argument describes whether the value has already been -prepared with :meth:`get_prep_lookup`. - -Specifying the form field for a model field -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: formfield(self, form_class=forms.CharField, **kwargs) - -Returns the default form field to use when this field is displayed in a model. -This method is called by the :class:`~django.forms.ModelForm` helper. - -All of the ``kwargs`` dictionary is passed directly to the form field's -:meth:`~django.forms.Field__init__` method. Normally, all you need to do is -set up a good default for the ``form_class`` argument and then delegate further -handling to the parent class. This might require you to write a custom form -field (and even a form widget). See the :doc:`forms documentation -</topics/forms/index>` for information about this, and take a look at the code in -:mod:`django.contrib.localflavor` for some examples of custom widgets. - -Continuing our ongoing example, we can write the :meth:`formfield` method as:: - - class HandField(models.Field): - # ... - - def formfield(self, **kwargs): - # This is a fairly standard way to set up some defaults - # while letting the caller override them. - defaults = {'form_class': MyFormField} - defaults.update(kwargs) - return super(HandField, self).formfield(**defaults) - -This assumes we've imported a ``MyFormField`` field class (which has its own -default widget). This document doesn't cover the details of writing custom form -fields. - -.. _helper functions: ../forms/#generating-forms-for-models -.. _forms documentation: ../forms/ - -Emulating built-in field types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: get_internal_type(self) - -Returns a string giving the name of the :class:`~django.db.models.Field` -subclass we are emulating at the database level. This is used to determine the -type of database column for simple cases. - -If you have created a :meth:`db_type` method, you don't need to worry about -:meth:`get_internal_type` -- it won't be used much. Sometimes, though, your -database storage is similar in type to some other field, so you can use that -other field's logic to create the right column. - -For example:: - - class HandField(models.Field): - # ... - - def get_internal_type(self): - return 'CharField' - -No matter which database backend we are using, this will mean that ``syncdb`` -and other SQL commands create the right column type for storing a string. - -If :meth:`get_internal_type` returns a string that is not known to Django for -the database backend you are using -- that is, it doesn't appear in -``django.db.backends.<db_name>.creation.DATA_TYPES`` -- the string will still be -used by the serializer, but the default :meth:`db_type` method will return -``None``. See the documentation of :meth:`db_type` for reasons why this might be -useful. Putting a descriptive string in as the type of the field for the -serializer is a useful idea if you're ever going to be using the serializer -output in some other place, outside of Django. - -Converting field data for serialization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. method:: value_to_string(self, obj) - -This method is used by the serializers to convert the field into a string for -output. Calling :meth:`Field._get_val_from_obj(obj)` is the best way to get the -value to serialize. For example, since our ``HandField`` uses strings for its -data storage anyway, we can reuse some existing conversion code:: - - class HandField(models.Field): - # ... - - def value_to_string(self, obj): - value = self._get_val_from_obj(obj) - return self.get_db_prep_value(value) - -Some general advice --------------------- - -Writing a custom field can be a tricky process, particularly if you're doing -complex conversions between your Python types and your database and -serialization formats. Here are a couple of tips to make things go more -smoothly: - - 1. Look at the existing Django fields (in - :file:`django/db/models/fields/__init__.py`) for inspiration. Try to find - a field that's similar to what you want and extend it a little bit, - instead of creating an entirely new field from scratch. - - 2. Put a :meth:`__str__` or :meth:`__unicode__` method on the class you're - wrapping up as a field. There are a lot of places where the default - behavior of the field code is to call - :func:`~django.utils.encoding.force_unicode` on the value. (In our - examples in this document, ``value`` would be a ``Hand`` instance, not a - ``HandField``). So if your :meth:`__unicode__` method automatically - converts to the string form of your Python object, you can save yourself - a lot of work. - - -Writing a ``FileField`` subclass -================================= - -In addition to the above methods, fields that deal with files have a few other -special requirements which must be taken into account. The majority of the -mechanics provided by ``FileField``, such as controlling database storage and -retrieval, can remain unchanged, leaving subclasses to deal with the challenge -of supporting a particular type of file. - -Django provides a ``File`` class, which is used as a proxy to the file's -contents and operations. This can be subclassed to customize how the file is -accessed, and what methods are available. It lives at -``django.db.models.fields.files``, and its default behavior is explained in the -:doc:`file documentation </ref/files/file>`. - -Once a subclass of ``File`` is created, the new ``FileField`` subclass must be -told to use it. To do so, simply assign the new ``File`` subclass to the special -``attr_class`` attribute of the ``FileField`` subclass. - -A few suggestions ------------------- - -In addition to the above details, there are a few guidelines which can greatly -improve the efficiency and readability of the field's code. - - 1. The source for Django's own ``ImageField`` (in - ``django/db/models/fields/files.py``) is a great example of how to - subclass ``FileField`` to support a particular type of file, as it - incorporates all of the techniques described above. - - 2. Cache file attributes wherever possible. Since files may be stored in - remote storage systems, retrieving them may cost extra time, or even - money, that isn't always necessary. Once a file is retrieved to obtain - some data about its content, cache as much of that data as possible to - reduce the number of times the file must be retrieved on subsequent - calls for that information. |