在Django中存储自定义数据类型

时间:2019-03-06 16:23:19

标签: python django geospatial

我有一个自定义类(可能很快就会发布),我想将其整齐地存储在Django中的某些模型中。

该类大致如下:

class BBOX(object):

    def __init__(self, min_x, min_y, max_x, max_y, srs_code, **kwargs):
        self.min_x = min_x
        self.max_x = max_x
        self.min_y = min_y
        self.max_y = max_y

        self._srs_code = get_srs_code(srs_code)

        self._srs_identifier = 'EPSG:{}'.format(self.srs_code)
        self._srs = SRS(self.srs_code)

该类具有许多属性和辅助函数,可用于安全处理基于地理空间的应用程序中的边界框对象。在代码中,我们安全地传递了这些信息,知道它们将易于使用并且可以与其余代码很好地交互。通常,除非绝对必须传递列表或类似内容(例如,当将值序列化为时),否则我们永远都不想在未由此类实例表示的代码中使用边界框。 JSON,YAML或类似的内容。

我们的几个Django模型具有与BBOX相关的值。当前,这些不同的模型都必须手动定义它们代表BBOX所需的字段。为了方便起见,我们在其中一些助手属性中添加了一些属性以转换为对象。例如:

class Layer(PolymorphicModel):

    name = models.CharField(max_length=255, blank=True, null=True)
    title = models.CharField(max_length=255, blank=True, null=True)
    parent_layer = models.ForeignKey('self', blank=True, null=True)

    min_x = models.FloatField()
    min_y = models.FloatField()
    max_x = models.FloatField()
    max_y = models.FloatField()
    srs_code = models.IntegerField()

    @property
    def bbox(self):
        return BBOX(self.min_x, self.min_y, self.max_x, self.max_y, self.srs_code)

很明显,如果我们需要为每个可以具有BBOX的模型添加一组字段,那么这可能会导致很多代码重复。这可能使其难以维护且难以更改。

处理这样的事情的最佳方法是什么?我想本质上为BBOX创建一个新表:

class DjangoBbox(Model):

    min_x = models.FloatField()
    min_y = models.FloatField()
    max_x = models.FloatField()
    max_y = models.FloatField()
    srs_code = models.IntegerField()

然后,我可以在需要的地方添加外键引用,但是这种想法对我来说却很奇怪。这个想法有天生的错误吗?我能想到的第一件事是,从理论上讲,多个键可以指向同一个DjangoBbox条目,并且对键的更改即使在无意间也会反映在这两个条目中。

1 个答案:

答案 0 :(得分:1)

我看到了三种解决方案:

  1. 使用abstract model class:您可以创建一个抽象的BBox基类,该基类在每次创建模型时都继承自。只需将其他字段添加到您的子类中即可。因此,假设有一个抽象的BBOXModel类:

    class Layer(BBOXModel):
        name = models.CharField(max_length=255, blank=True, null=True)
        title = models.CharField(max_length=255, blank=True, null=True)
        parent_layer = models.ForeignKey('self', blank=True, null=True)
    
    layer = Layer.objects.first()
    layer.min_x  # returns the min_x field
    layer.bbox  # returns the bbox property from `BBOXModel`
    layer.bbox = myBbox  # if you also create a setter for this property
    
  2. Multi-table inheritance也可能会起作用,因此您的模型继承自具体的BBox模型:这将自动创建一对一的关系。它还使用所有边界框创建一个单独的表(因此您可以单独查询它),但是每个表都仅附加到一个子模型实例,该子模型实例可以是不同的类型。同样,父类上的所有属性和方法都可以像子类上期望的那样工作。上面的代码示例是相同的,除了因为您现在有了具体的BBOXModel,您还可以执行以下操作:

    bbox = BBOXModel.objects.first()
    bbox.layer  # works if this bbox "is part of a" Layer, otherwise it will throw an AttributeError
    
  3. 最后,您可以创建一个完整的custom field type:它可以在模型中容纳适当的BBOX:该字段本身就是您已经拥有的BBOX,所以当说layer.bbox,就像您已经习惯的那样,这将是一个完整的python BBOX对象。它需要确定如何在数据库中编码BBOX。链接中的示例(一手牌)可能会有所帮助。就个人而言,如果使用PostgreSQL,我将使用JSONField作为基础数据库字段(继承自JSONField)。您可能只需要定义to_pythonget_prep_value,与JSON进行相互转换并调用super(),让JSONField处理它在数据库中的表示方式。这样您就可以像这样定义您的类:

    class Layer(PolymorphicModel):
        name = ...
        ...
        bbox = BBOXField()
    

使用所有这三个选项,您可以通过将对BBOX对象的处理封装在类(或字段)中来完全重用已经拥有的BBOX类。您应该最终可以只为模型实例分配一个BBOX,并且当从db获取模型实例时,获取其bbox属性,该属性将返回BBOX