Мне нужно хранить комплексное число в модели Джанго. Для тех, кто забывает, это просто означает Z=R+jX
, где R и X - действительные числа, представляющие действительные и мнимые компоненты комплекса. Будут отдельные номера, а также списки, которые нужно хранить. Мои поиски до сих пор не дали хорошего решения для списков, поэтому я намерен позволить базе данных обрабатывать список как отдельные записи.
Я вижу два варианта хранения комплексного числа:
1) создать настраиваемое поле: class Complex(models.CharField)
Это позволило бы мне настроить все аспекты поля, но это много дополнительной работы для проверка, если это должно быть сделано правильно. Главный плюс в том, что одно число представлено одним полем в таблице.
2) пусть каждое комплексное число будет представлено строкой с полем float
для вещественной части R и другим полем float
для мнимой части X. Недостатком этого подхода является то, что я бы Нужно написать несколько преобразователей, которые будут создавать комплексное число из компонентов, и наоборот. Плюс в том, что база данных просто увидит это как очередную запись.
Конечно, эта проблема была решена в прошлом, но я не могу найти хороших ссылок, не говоря уже о Джанго.
Это моя первая трещина на поле, она основана на другом примере, который я обнаружил, который включал несколько манипуляций со струнами. Что мне неясно, так это то, как и где должны выполняться различные проверки (например, приведение простого числа к сложному числу путем добавления + 0j). Я намерен также добавить функциональность формы, чтобы поле работало как поле с плавающей точкой, но с дополнительными ограничениями или требованиями.
Я еще не тестировал этот код, поэтому могут быть проблемы с ним. Он основан на коде из ответа на этот вопрос SO. После запуска кода выясняется, что в именах методов произошли некоторые изменения.
Каков наиболее эффективный способ сохранить список в моделях Django?
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)
Если ваше выражение каждый раз напоминает R + jX, вы можете создать следующий класс
class ComplexNumber(models.Model):
real_number = models.FloatField('Real number part')
img_number = models.FloatFoeld('Img number part')
def __str__(self):
return complex(self.real_number, self.img_number)
и обработать строку результата с помощью python , см. Здесь
Если вы иметь несколько реальных и img-частей, вы можете справиться с этим с помощью внешних ключей или полей ManyToMany. Это может зависеть от ваших потребностей.
Что касается пользовательских полей, вы, вероятно, уже нашли соответствующую часть в документации Django .
Вопрос о том, стоит ли создавать пользовательское поле (или пользовательский тип базы данных, см. Ниже), зависит от того, что вам нужно делать с сохраненными числами. Для хранения и некоторых случайных перемещений вы можете использовать самое простое и вменяемое решение (ваш номер два, дополненный Tobit).
С PostgreSQL у вас есть возможность реализовать пользовательские типы непосредственно в базе данных, включая операторы . Вот соответствующая часть в Документах Postgres , в комплекте с примером комплексных чисел, не меньше.
Конечно, тогда вам нужно представить новый тип и операторы Django. Довольно много работы, но тогда вы можете выполнять арифметику с отдельными полями прямо в базе данных, используя Django ORM.
Если честно, я бы просто разделил комплексное число на два поля с плавающей запятой / десятичное поле и добавил свойство для чтения и записи в виде одного комплексного числа.
Я придумал это настраиваемое поле, которое заканчивается как поле разделения на фактической модели и также вводит вышеупомянутое свойство.
contribute_to_class
называется глубоко в механизме модели Джанго для всех полей, которые объявлены в модели. Как правило, они могут просто добавить само поле в модель и, возможно, дополнительные методы, такие как get_latest_by_...
, но здесь мы угоняем этот механизм, чтобы вместо этого добавить два поля, в которых мы строим, а не само поле «себя» вообще , поскольку он не должен существовать как столбец базы данных. (Это может что-то сломать, кто знает ...) Часть этого механизма объясняется здесь в вики Django.
Класс ComplexProperty
является дескриптором свойства , который позволяет настраивать то, что происходит, когда свойство, к которому он «присоединен», обращается к экземпляру (осуществляется чтение или запись). (То, как работают дескрипторы, немного выходит за рамки этого ответа, но в документации по Python есть руководство .)
NB: я сделал не проверяйте это за пределами бегущих миграций, так что все может сломаться неожиданным образом, но, по крайней мере, теория обоснована. :)
from django.db import models
class ComplexField(models.Field):
def __init__(self, **kwargs):
self.field_class = kwargs.pop('field_class', models.FloatField)
self.field_kwargs = kwargs.pop('field_kwargs', {})
super().__init__(**kwargs)
def contribute_to_class(self, cls, name, private_only=False):
for field in (
self.field_class(name=name + '_real', **self.field_kwargs),
self.field_class(name=name + '_imag', **self.field_kwargs),
):
field.contribute_to_class(cls, field.name)
setattr(cls, name, ComplexProperty(name))
class ComplexProperty:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
if not instance:
return self
real = getattr(instance, self.name + '_real')
imag = getattr(instance, self.name + '_imag')
return complex(real, imag)
def __set__(self, instance, value: complex):
setattr(instance, self.name + '_real', value.real)
setattr(instance, self.name + '_imag', value.imag)
class Test(models.Model):
num1 = ComplexField()
num2 = ComplexField()
num3 = ComplexField()
Миграция для этого выглядит как
migrations.CreateModel(
name="Test",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("num1_real", models.FloatField()),
("num1_imag", models.FloatField()),
("num2_real", models.FloatField()),
("num2_imag", models.FloatField()),
("num3_real", models.FloatField()),
("num3_imag", models.FloatField()),
],
)
, так что, как вы можете видеть, три ComplexField
разбиты на шесть FloatField
с. [1119 ]