如何在Django模型中存储复数

时间:2019-03-18 19:55:44

标签: python django django-models django-custom-field

我需要在Django模型中存储一个复数。对于那些忘记的人,这仅表示Z=R+jX,其中R和X是表示复数的实部和虚部的实数。将有单独的数字,以及需要存储的列表。到目前为止,我的搜索还没有为列表提供良好的解决方案,因此我打算让数据库将列表作为单独的记录来处理。

我看到两个用于存储复数的选项:

1)创建一个自定义字段:class Complex(models.CharField) 这将允许我自定义该领域的所有方面,但是如果要正确完成的话,这将需要大量的额外工作来进行验证。主要优点是表中的单个字段代表单个数字。

2)让每个复数由一行表示,实部为R的float字段,虚部为X的另一个float字段。这种方法的缺点我需要编写一些转换器,这些转换器将根据组件创建一个复数,反之亦然。好处是数据库只会将其视为另一条记录。

当然,这个问题过去已经解决了,但是我找不到任何好的参考文献,不要在乎Django的特殊之处。

这是我在该领域的第一个漏洞,它是基于我发现的另一个涉及一些字符串操作的示例。我不清楚应该如何以及在何处执行各种验证(例如,通过添加+ 0j将一个简单的float转换为一个复数)。我还打算添加表单功能,以便该字段的行为类似于float字段,但有其他限制或要求。

我尚未测试此代码,因此可能存在问题。它基于此SO问题中答案的代码。运行代码后,似乎方法名称发生了一些变化。

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)

3 个答案:

答案 0 :(得分:1)

如果您的表达式每次都像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 see here

处理结果字符串

如果您有多个实部和img部分,则可以使用外键或ManyToMany Fields处理。这可能取决于您的需求。

答案 1 :(得分:1)

关于自定义字段,您可能已经在Django documentation中找到了相关部分。

自定义字段(或自定义数据库类型,见下文)是否值得,麻烦实际上取决于您需要对存储的数字执行什么操作。对于存储和偶尔的移动,您可以使用最简单的理智的解决方案(您的第二个解决方案由Tobit增强)。

使用PostgreSQL,您必须可以直接在数据库中实现自定义类型,包括operators。这是Postgres docs中的相关部分,并附有一个复杂的数字示例。

当然,您需要将新类型和运算符公开给Django。很多工作,但是您可以使用Django ORM在数据库中对各个字段进行算术运算。

答案 2 :(得分:0)

老实说,我只是将复数分为两个浮点/小数字段,并添加了一个属性以作为单个复数进行读写。

我想出了这个自定义字段,该字段最终在实际模型上作为拆分字段并注入了上述属性。

    对于在模型上声明的所有字段,
  • contribute_to_class被称为deep in the Django model machinery。通常,他们可能只是将字段本身添加到模型中,也许还会添加诸如get_latest_by_...之类的其他方法,但是在这里,我们劫持了该机制以添加我们在其中构造的两个字段,而不是实际的“ self”字段本身根本不需要,因为它不需要作为数据库列存在。 ((这可能会破坏某些东西,谁知道...)某些机制在Django Wiki中here进行了解释。

  • ComplexProperty类是property descriptor,它可以自定义访问“读取为”的属性(作为实例附加)时所发生的情况。 (描述符的工作方式超出了此答案的范围,但有一个how-to guide in the Python docs。)

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