2

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

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

1)创建一个自定义字段:class Complex(models.CharField) 这将允许我自定义该字段的所有方面,但如果要正确完成,那将是很多额外的验证工作。主要优点是单个数字由表中的单个字段表示。

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

当然,这个问题在过去已经解决了,但我找不到任何好的参考资料,更不用说 Django 的特别之处了。

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

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

在 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)
4

3 回答 3

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 处理结果字符串,请参见此处

如果您有多个 real 和 img 部分,您可以使用外键或 ManyToMany 字段来处理。这可能取决于您的需要。

于 2019-03-18T23:00:46.210 回答
1

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

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

使用 PostgreSQL,您必须能够直接在数据库中实现自定义类型,包括操作符这是Postgres 文档中的相关部分,并附有一个复数示例,不少。

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

于 2019-03-19T08:22:18.407 回答
0

老实说,我只是将复数拆分为两个浮点/十进制字段,并添加一个用于读取和写入的属性作为单个复数。

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

  • contribute_to_class在模型上声明的所有字段的 Django 模型机制中都被调用。通常,他们可能只是将字段本身添加到模型中,可能还有其他方法get_latest_by_...,例如不需要作为数据库列存在。(这可能会破坏某些东西,谁知道......) Django wiki中解释一些这种机制。

  • 该类ComplexProperty是一个属性描述符,它允许自定义当它“附加”到实例中的属性被访问(读取或写入)时发生的情况。(描述符的工作方式有点超出了这个答案的范围,但是Python 文档中有一个操作指南。)

注意:除了运行迁移之外,我没有对此进行测试,所以事情可能会以意想不到的方式被破坏,但至少理论是合理的。:)

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()),
    ],
)

如您所见,三个ComplexFields 被分解为六个FloatFields。

于 2019-03-19T14:19:46.273 回答