Django通用工厂

2024-04-20 12:57:41 发布

您现在位置:Python中文网/ 问答频道 /正文

我有两个问题困扰着我。我面对的是一个实现,其中一些文档与不同级别的地理数据相关,并且希望工厂生成它们。让我们看一个我认为它可能起作用的例子:

from django.contrib.gis.db import models


class Country(models.Model):
    name = models.CharField(max_length=60)


class Region(models.Model):
    country = models.ForeignKey(Country, on_delete=models.PROTECT)
    name = models.CharField(max_length=60)


class Law(models.Model):
    text = models.CharField(max_length=60)

    class Meta:
        abstract = True


class CountryLaw(Law):
    country = models.ForeignKey(Country, on_delete=models.CASCADE)


class RegionLaw(Law):
    region = models.ForeignKey(Region, on_delete=models.CASCADE)

# Not sure this work but the idea is here
class LawManager(models.Manager):
    def create_law(text,geodata):
        if isinstance(geodata, Country):
            return CountryLaw(text=text, country=geodata)
        elif isinstance(geodata, Region)
            return RegionLaw(text=text, region=geodata)
        else:
            raise TypeError("Inapropriate geodata type")

我想要一些工厂方法,因为我有一些工作来填充所有法律都通用的“法律”字段,但是这个例子没有显示它。 我的问题是:

  • 有没有更好的方法来设计法律对象?在
  • 这样的经理能行吗?我怎样才能访问它?在

我在google和stackoverflow上搜索答案,但不知道该用什么关键字,也没有找到任何可以帮助我的东西。。在

谢谢你的帮助!在


Tags: textmodelonmodelsdeletecountrylengthregion
1条回答
网友
1楼 · 发布于 2024-04-20 12:57:41

这里有几种选择。下面的“列表”并不是详尽无遗的,尽管它可能提供一些想法,并且可以基于这些想法构建变体。在

像现在这样离开模特

在这种情况下,我们将其建模为:

+    -+ 1      N +      +
| Country |     | CountryLaw |
+    -+          +      +
    | 1
    |
    | N
+    -+ 1      N +     -+
| Region  |     | RegionLaw |
+    -+          +     -+

在这里我们构造了两个Laws,虽然我们当然可以超类这两个Law,但这意味着每个都有自己的类型。在

这样做的好处是,如果这两种语言有特定的语义,例如CountryLaw应该与RegionLaw完全不同的处理,那么这就更容易实现。此外,如果CountryLaw具有特定的字段,例如对于RegionLaws不重要(反之亦然),那么我们就避免在NULL值(或其他占位符)上浪费磁盘空间。在

缺点是,例如,如果我们想查询属于'Germany'的法律,我们必须分两个步骤进行查询:查询德国的CountryLaw,以及查询{}所有地区的{}。如果您还有SubRegions、Citys等,这也很容易失控

使用代理区域

这里我们认为所有的Law都是为一个特定的Region而设计的诀窍是我们构造了一个Region,它的行为就像整个国家一样。因此,除了'Saxony'和{},我们使用一个代表整个国家的虚拟区域'Germany'。在

然后我们可以引入一个单独的Law模型,它附属于Region。如果我们经常需要区分一个地区和一个国家,我们可以添加一个字段is_country,例如指定这是一个“国家代理”还是一个真正的地区:

^{pr2}$

优点是我们只有一个Law对象,因此设计更容易。此外,查询映射到一个国家(包括或不包括地区)的法律也很容易。在

缺点是,如果国家Law和地区Law显著不同,那么这将导致大量的检查(每次检查附加的区域是否真的是一个区域,还是一个国家),而且还可能导致许多未使用的字段。此外,如果我们启用越来越多的层,我们需要引入越来越多的代理对象。例如,如果我们将使用三层(CountryRegionSubRegion),那么我们需要为每个国家构造一个“代理”区域(它也包含一个代理子区域),并且对于每个区域,一个代理子区域。因此,如果存在n国家和m地区,这将导致2×n+m代理对象,这也会导致重复数据(我们会多次重复该国家/地区的名称,如果以后重命名某个国家或地区,则更新所有这些代理会带来一定的痛苦)。在

使用类似于树的结构

如果级别的数量可能很大,或者是动态的(在某种意义上,有些“区域”有子区域,而对于其他区域则没有区域),或者我们希望以一种统一的方式处理所有这些级别,我们可以决定使用一种类似树的结构。在

在这里我们定义一个模型,例如Area,一个Area可以有一个parent,这也是一个Area,我们可以这样构造一个结构。例如:

^{3}$

然后我们可以给每个Area附加0,一个或多个定律。所以模型看起来像:

    
 N|  |1
+   + 1      N +  -+
| Area |     | Law |
+   +          +  -+

这样做的好处是,我们有了一个适用于CountryRegionSubregion等的模型。此外,我们可以按照我们想要的方式实现一个层次结构,例如一些(小)国家没有Regions(例如“城市国家”,如梵蒂冈城新加坡,等等)。此外,Law对象将链接到类似“区域”的对象。也很容易获得附属于某一领域的法律。

然而,一个问题是,很难获得一个国家、其地区、其分区域等的所有信息。然而,这可以通过设计一个多对多表来处理,例如,设计一个多对多表,对这种树结构的传递闭包进行编码:这个多对多关系将包含一个国家与所有国家的链接它的地区、分区域等,但仍然不是很优雅。这也意味着所有这些Area实例都是统一表示的。因此,如果我们想在Area中增加一份有正式语文的清单,所有地区都有“正式语文”,而大多数分区域可能只是“继承”其国家的官方语文。在

Law对象中使用GenericForeignKey(Django特性)

Django还有一种特殊的关系,称为^{} [doc]。对于这样的问题,这可能看起来是一个很好的特性,但我建议尽量避免这种关系。在

默认情况下,Django会为每个模型添加一个隐式主键:如果开发人员不使用primary_key=True指定一个字段。Django将自动添加一个IntegerField,它将指定一个标识符作为主键。因此,可以合理地假设大多数模型都有一个IntegerField作为主键(实际上,指定另一个主键是undjango)。我们还可以生成一个将每个模型映射到一个整数的列表:例如,0映射到{},1映射到{},等等

这意味着大多数模型实例可以由两个整数标识:一个整数指定模型,另一个指定相应模型的主键。例如,(0, 14)是带有主键14的{}(假设我们使用上面段落中定义的“查找表”)。这是一个强大的概念:因此,我们可以使用两个数据库列来存储这些整数,并且每次都让Django获取对象。这就是GenericForeignKey背后的想法。因此,我们可以定义一个Law,如:

class Law(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    area = GenericForeignKey('content_type', 'object_id')

这意味着现在我们的Law存储了两个实字段:acontent_typeobject_id,如果我们查询some_law.area,Django将获取与这两个整数对应的对象。在

因此,我们可以用它来指代CountryRegionSubregion,这在我们可以参考各种模型的情况下是有用的。但也有很多缺点。主要问题是,在我们希望在查询中使用这些关系的情况下,这些关系通常非常麻烦。实际上:假设我们想用area字段加入我们的Law模型。那我们该加入哪一张桌子?是CountryRegionSubRegion?如果一个Law指的是Country,而另一个是指Region,怎么办?所以通常我们不能JOIN。在

此外,模型本身并不保证GenericForeignKey总是指向一个类似“区域”的对象。它可以引用另一个Law,aUser,aCriminal,等等。因此,您有责任编写一个合理的逻辑来确保这些关系是有意义的。虽然这看起来很容易,但很难保持良好的关系。由于没有FOREIGN KEY约束,大多数数据库无法检查外键是否引用了一个有效的对象,因为“目标表”是未知的。在

虽然有很多缺点,但在某些情况下,aGenericForeignKey可以是某些问题的优雅解决方案,但必须小心。在

虽然-据我所知-没有公认的方法来在图表中指定这种关系,但它可以是这样的:

+    -+
| Country |
+    -+.
    | 1      .
    |          .
    | N          .
+    -+        . +  -+
| Region  |. . . . . | Law |
+    -+ 1      N +  -+

相关问题 更多 >