Search code examples
pythondjangodjango-modelsdjango-custom-field

How to store a complex number in Django model


I need to store a complex number in a Django model. For those who forget, that simply means Z=R+jX where R and X are real numbers representing the real and imaginary components of the complex. There will be individual numbers, as well as lists that need to be stored. My searches so far haven't provided a good solution for lists, so I intend to let the database handle the list as individual records.

I see two options for storing a complex number:

1) create a custom field: class Complex(models.CharField) This would allow me to customize all aspects of the field, but that is a lot of extra work for validation if it is to be done properly. The major upside is that a single number is represented by a single field in the table.

2) let each complex number be represented by a row, with a float field for the real part, R, and another float field for the imaginary part, X. The downside to this approach is that I would need to write some converters that will create a complex number from the components, and vice versa. The upside is that the database will just see it as another record.

Surely this issue has been resolved in the past, but I can't find any good references, never mind one particular to Django.

This is my first crack at the field, it is based on another example I found that involved a few string manipulations. What isn't clear to me is how and where various validations should be performed (such as coercing a simple float into a complex number by adding +0j). I intend to add form functionality as well, so that the field behaves like a float field, but with additional restrictions or requirements.

I have not tested this code yet, so there may be issues with it. It is based on the code from an answer in this SO question. It appears after running the code that some changes took place in method names.

What is the most efficient way to store a list in the Django models?

class ComplexField(models.CharField):

    description = 'A complex number represented as a string'

    def __init__(self, *args, **kwargs):
        kwargs['verbose_name'] = 'Complex Number'
        kwargs['max_length'] = 64
        kwargs['default'] = '0+0j'

        super().__init__(*args, **kwargs)

    def to_python(self, value):
        if not value: return
        if isinstance(value, complex):
            return value
        return complex(value)

    def get_db_prep_value(self, value):
        if not value: return
        assert(isinstance(value, complex))
        return str(item)[1:-1]

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

Solution

  • Regarding custom fields, you've probably found the relevant part in the Django documentation already.

    Whether a custom field (or a custom database type, see below) is worth the trouble really depends on what you need to do with the stored numbers. For storage and some occasional pushing around, you can go with the easiest sane solution (your number two as enhanced by Tobit).

    With PostgreSQL, you have to possibility to implement custom types directly in the database, including operators. Here's the relevant part in the Postgres docs, complete with a complex numbers example, no less.

    Of course you then need to expose the new type and the operators to Django. Quite a bit of work, but then you could do arithmetics with individual fields right in the database using Django ORM.