Django 通用工厂
Django Generic Factory
我有两个问题困扰着我。我面临一个实现,其中一些文档与不同级别的地理数据相关,并希望工厂生成它们。让我们看一个我认为它可能如何工作的例子:
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")
我想要一些工厂方法,因为我有一些工作来填充所有法律通用的 "Law" 的字段,但是这个例子没有显示它。
我的问题如下:
- 有没有更好的方法来设计 Law 对象?
- 这样的经理有用吗?我怎样才能访问它?
我在 google 和 Whosebug 上搜索答案,但不知道使用什么关键字,也没有找到任何可以帮助我的东西..
感谢您的帮助!
这里有几个选项。以下 "list" 并不详尽,尽管它可能提供了一些想法,并且可以根据这些想法构建变体。
让模型保持原样
在这种情况下,我们将其建模为:
+---------+ 1 N +------------+
| Country |----------| CountryLaw |
+---------+ +------------+
| 1
|
| N
+---------+ 1 N +-----------+
| Region |----------| RegionLaw |
+---------+ +-----------+
这里我们构造了两个Law
。虽然我们当然可以超类化这两个 Law
,但这意味着每个都有自己的类型。
的优点是如果两者有特定的语义,例如CountryLaw
和RegionLaw
的处理方式应该完全不同,那么更容易实现。此外,如果 CountryLaw
具有特定字段,例如对 RegionLaw
不重要(反之亦然),那么我们将避免在 NULL
值(或其他占位符)上浪费磁盘空间。
缺点是,例如,如果我们想查询属于 'Germany'
的法律,我们必须分两步进行:查询德国的 CountryLaw
,然后查询对于 Germany
的所有区域的 RegionLaw
s。如果您还有 SubRegion
s、City
s 等
,这也很容易失控
使用 代理 地区
这里我们认为所有 Law
都是为特定的 Region
制作的,但诀窍在于我们构建了一个像整个国家一样运作的 Region
。因此,除了 'Saxony'
和 'Bavaria'
,我们还使用一个 虚拟 区域 'Germany'
来代表整个国家。
然后我们可以引入一个 Law
模型,它附加到 Region
。如果我们经常需要区分区域和国家,我们可以添加一个字段 is_country
,例如指定这是 "country proxy" 还是真实区域:
+---------+
| Country |
+---------+
| 1
|
| N
+-------------------+ 1 N +-----+
| Region |----------| Law |
+-------------------+ +-----+
| is_country : bool |
+-------------------+
优点是我们只有一个Law
对象,设计起来比较容易。此外,很容易查询映射到一个国家(包括或不包括地区)的法律。
不利的是,如果国家 Law
s 和地区 Law
s 明显不同,那么这将导致大量的检查(每次查看附加区域是否真的是一个地区或国家),而且它会导致很多 unused 字段。此外,如果我们启用越来越多的层,我们需要引入越来越多的代理对象。例如,如果我们将使用三层(Country
、Region
、SubRegion
),那么我们需要为每个国家构建一个 "proxy" 区域(其中包含一个 proxy 子区域),对于每个区域,一个 proxy 子区域。所以如果有 n 个国家和 m 个地区,这将导致 2×n + m 代理objects,这也会造成data duplication(我们多次重复country/region的名称,如果以后对某个国家或地区进行重命名,会导致一些痛苦更新所有这些代理)。
使用 树 类结构
如果级别数可以很大,或者动态(在某种意义上,一些"areas"*有子区域,而另一些则没有区域) ,或者我们想以 uniform 的方式处理所有这些级别,我们可以决定使用类似 tree 的结构。
这里我们定义一个模型,比如Area
,一个Area
可以有一个parent
,也就是一个Area
,我们可以构造一个tree-结构呀。例如:
class <b>Area</b>(models.Model):
parent = ForeignKey('app.Area', on_delete=models.SET_NULL, related_name='children')
然后我们可以为每个 Area
附加零、一个或多个定律。所以模型看起来像:
----
N| |1
+------+ 1 N +-----+
| Area |----------| Law |
+------+ +-----+
优点是这里我们有一个模型用于Country
、Region
、Subregion
等。此外,我们可以实现一个层次结构正是我们想要的方式,例如一些(小)国家没有 Region
s(例如“城市国家”,如 梵蒂冈城、新加坡等)。此外,Law
对象将 link 变成类似 "area" 的对象。也很容易获得某个区域的附属法则
然而一个问题是很难获得所有一个国家的Law
,它的区域,它的次区域等等。但是这可以处理,例如通过设计一个多对多 table 来编码这个树结构的 传递闭包 :这个多对多关系将包含 links 是一个拥有 所有 区域、次区域等的国家,但这仍然不是很优雅。这也意味着所有这些 Area
实例都是统一表示的。因此,如果我们想在 Area
中添加一个包含官方语言的列表,所有地区都有 "official languages",而大多数次区域可能只是 "inherit" 他们国家的官方语言。
在 Law
对象中使用 GenericForeignKey
(Django 功能)
Django 还有一种特殊的关系,称为 GenericForeignKey
[doc]。对于此类问题,这可能看起来 很棒 功能,但我真的建议尽可能避免这种关系。
默认情况下,Django 为每个模型添加一个隐式主键:如果开发人员没有 指定带有primary_key=True
的字段。 Django 会自动添加一个 IntegerField
来指定一个标识符作为主键。因此可以合理地假设 大多数 模型有一个 IntegerField
作为主键(事实上它是 un-Django 来指定另一个首要的关键)。我们还可以生成一个列表,将每个模型映射到一个整数:例如,我们可以说 0
映射到 User
,1
映射到 Group
,等等。
这意味着大多数模型实例可以用两个整数来标识:一个整数指定模型,一个指定对应模型的主键。例如 (0, 14)
是带有主键 14
的 User
(假设我们使用上面段落中定义的 "lookup table")。这是一个强大的概念:因此我们可以使用两个数据库列来存储这些整数,并且每次都让 Django 获取对象。这就是 GenericForeignKey
背后的想法。因此我们可以定义一个 Law
像:
class Law(models.Model):
<b>content_type</b> = models.ForeignKey(ContentType, on_delete=models.CASCADE)
<b>object_id</b> = models.PositiveIntegerField()
<b>area = GenericForeignKey('content_type', 'object_id')</b>
所以这意味着现在我们的 Law
存储了两个真实字段:一个 content_type
和一个 object_id
,如果我们查询 some_law.area
,Django 将获取一个与这两个整数对应的对象。
因此我们可以用它来指代 Country
、Region
、Subregion
,这在我们可以指代各种模型时很有用。但是也有很多缺点。主要问题是,如果我们想在查询中使用它们,这种关系通常非常麻烦。确实:假设我们想将 Law
模型与 area
字段结合起来。那我们应该加入哪个table呢? Country
、Region
、SubRegion
?如果一个 Law
指的是 Country
而另一个指的是 Region
怎么办?所以通常我们不能 JOIN
.
此外,模型 本身 不 保证 GenericForeignKey
将始终引用 "area"-像对象。它可以引用另一个 Law
、User
、Criminal
等。因此,您有责任编写 sane 逻辑将始终确保关系有意义。虽然这看起来很容易,但要维持良好的 理智 关系却很难。大多数数据库无法检查外键是否引用 有效 对象,因为没有 FOREIGN KEY
约束,因为 "target table" 是未知的。
虽然有很多缺点,但在某些情况下GenericForeignKey
可以成为某些问题的优雅解决方案,但必须小心。
尽管据我所知,没有普遍接受的方式在图表中指定此类关系,但它可能看起来像这样:
+---------+
| Country |
+---------+.
| 1 .
| .
| N .
+---------+ . +-----+
| Region |. . . . . | Law |
+---------+ 1 N +-----+
我有两个问题困扰着我。我面临一个实现,其中一些文档与不同级别的地理数据相关,并希望工厂生成它们。让我们看一个我认为它可能如何工作的例子:
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")
我想要一些工厂方法,因为我有一些工作来填充所有法律通用的 "Law" 的字段,但是这个例子没有显示它。 我的问题如下:
- 有没有更好的方法来设计 Law 对象?
- 这样的经理有用吗?我怎样才能访问它?
我在 google 和 Whosebug 上搜索答案,但不知道使用什么关键字,也没有找到任何可以帮助我的东西..
感谢您的帮助!
这里有几个选项。以下 "list" 并不详尽,尽管它可能提供了一些想法,并且可以根据这些想法构建变体。
让模型保持原样
在这种情况下,我们将其建模为:
+---------+ 1 N +------------+
| Country |----------| CountryLaw |
+---------+ +------------+
| 1
|
| N
+---------+ 1 N +-----------+
| Region |----------| RegionLaw |
+---------+ +-----------+
这里我们构造了两个Law
。虽然我们当然可以超类化这两个 Law
,但这意味着每个都有自己的类型。
的优点是如果两者有特定的语义,例如CountryLaw
和RegionLaw
的处理方式应该完全不同,那么更容易实现。此外,如果 CountryLaw
具有特定字段,例如对 RegionLaw
不重要(反之亦然),那么我们将避免在 NULL
值(或其他占位符)上浪费磁盘空间。
缺点是,例如,如果我们想查询属于 'Germany'
的法律,我们必须分两步进行:查询德国的 CountryLaw
,然后查询对于 Germany
的所有区域的 RegionLaw
s。如果您还有 SubRegion
s、City
s 等
使用 代理 地区
这里我们认为所有 Law
都是为特定的 Region
制作的,但诀窍在于我们构建了一个像整个国家一样运作的 Region
。因此,除了 'Saxony'
和 'Bavaria'
,我们还使用一个 虚拟 区域 'Germany'
来代表整个国家。
然后我们可以引入一个 Law
模型,它附加到 Region
。如果我们经常需要区分区域和国家,我们可以添加一个字段 is_country
,例如指定这是 "country proxy" 还是真实区域:
+---------+
| Country |
+---------+
| 1
|
| N
+-------------------+ 1 N +-----+
| Region |----------| Law |
+-------------------+ +-----+
| is_country : bool |
+-------------------+
优点是我们只有一个Law
对象,设计起来比较容易。此外,很容易查询映射到一个国家(包括或不包括地区)的法律。
不利的是,如果国家 Law
s 和地区 Law
s 明显不同,那么这将导致大量的检查(每次查看附加区域是否真的是一个地区或国家),而且它会导致很多 unused 字段。此外,如果我们启用越来越多的层,我们需要引入越来越多的代理对象。例如,如果我们将使用三层(Country
、Region
、SubRegion
),那么我们需要为每个国家构建一个 "proxy" 区域(其中包含一个 proxy 子区域),对于每个区域,一个 proxy 子区域。所以如果有 n 个国家和 m 个地区,这将导致 2×n + m 代理objects,这也会造成data duplication(我们多次重复country/region的名称,如果以后对某个国家或地区进行重命名,会导致一些痛苦更新所有这些代理)。
使用 树 类结构
如果级别数可以很大,或者动态(在某种意义上,一些"areas"*有子区域,而另一些则没有区域) ,或者我们想以 uniform 的方式处理所有这些级别,我们可以决定使用类似 tree 的结构。
这里我们定义一个模型,比如Area
,一个Area
可以有一个parent
,也就是一个Area
,我们可以构造一个tree-结构呀。例如:
class <b>Area</b>(models.Model):
parent = ForeignKey('app.Area', on_delete=models.SET_NULL, related_name='children')
然后我们可以为每个 Area
附加零、一个或多个定律。所以模型看起来像:
----
N| |1
+------+ 1 N +-----+
| Area |----------| Law |
+------+ +-----+
优点是这里我们有一个模型用于Country
、Region
、Subregion
等。此外,我们可以实现一个层次结构正是我们想要的方式,例如一些(小)国家没有 Region
s(例如“城市国家”,如 梵蒂冈城、新加坡等)。此外,Law
对象将 link 变成类似 "area" 的对象。也很容易获得某个区域的附属法则
然而一个问题是很难获得所有一个国家的Law
,它的区域,它的次区域等等。但是这可以处理,例如通过设计一个多对多 table 来编码这个树结构的 传递闭包 :这个多对多关系将包含 links 是一个拥有 所有 区域、次区域等的国家,但这仍然不是很优雅。这也意味着所有这些 Area
实例都是统一表示的。因此,如果我们想在 Area
中添加一个包含官方语言的列表,所有地区都有 "official languages",而大多数次区域可能只是 "inherit" 他们国家的官方语言。
在 Law
对象中使用 GenericForeignKey
(Django 功能)
Django 还有一种特殊的关系,称为 GenericForeignKey
[doc]。对于此类问题,这可能看起来 很棒 功能,但我真的建议尽可能避免这种关系。
默认情况下,Django 为每个模型添加一个隐式主键:如果开发人员没有 指定带有primary_key=True
的字段。 Django 会自动添加一个 IntegerField
来指定一个标识符作为主键。因此可以合理地假设 大多数 模型有一个 IntegerField
作为主键(事实上它是 un-Django 来指定另一个首要的关键)。我们还可以生成一个列表,将每个模型映射到一个整数:例如,我们可以说 0
映射到 User
,1
映射到 Group
,等等。
这意味着大多数模型实例可以用两个整数来标识:一个整数指定模型,一个指定对应模型的主键。例如 (0, 14)
是带有主键 14
的 User
(假设我们使用上面段落中定义的 "lookup table")。这是一个强大的概念:因此我们可以使用两个数据库列来存储这些整数,并且每次都让 Django 获取对象。这就是 GenericForeignKey
背后的想法。因此我们可以定义一个 Law
像:
class Law(models.Model):
<b>content_type</b> = models.ForeignKey(ContentType, on_delete=models.CASCADE)
<b>object_id</b> = models.PositiveIntegerField()
<b>area = GenericForeignKey('content_type', 'object_id')</b>
所以这意味着现在我们的 Law
存储了两个真实字段:一个 content_type
和一个 object_id
,如果我们查询 some_law.area
,Django 将获取一个与这两个整数对应的对象。
因此我们可以用它来指代 Country
、Region
、Subregion
,这在我们可以指代各种模型时很有用。但是也有很多缺点。主要问题是,如果我们想在查询中使用它们,这种关系通常非常麻烦。确实:假设我们想将 Law
模型与 area
字段结合起来。那我们应该加入哪个table呢? Country
、Region
、SubRegion
?如果一个 Law
指的是 Country
而另一个指的是 Region
怎么办?所以通常我们不能 JOIN
.
此外,模型 本身 不 保证 GenericForeignKey
将始终引用 "area"-像对象。它可以引用另一个 Law
、User
、Criminal
等。因此,您有责任编写 sane 逻辑将始终确保关系有意义。虽然这看起来很容易,但要维持良好的 理智 关系却很难。大多数数据库无法检查外键是否引用 有效 对象,因为没有 FOREIGN KEY
约束,因为 "target table" 是未知的。
虽然有很多缺点,但在某些情况下GenericForeignKey
可以成为某些问题的优雅解决方案,但必须小心。
尽管据我所知,没有普遍接受的方式在图表中指定此类关系,但它可能看起来像这样:
+---------+
| Country |
+---------+.
| 1 .
| .
| N .
+---------+ . +-----+
| Region |. . . . . | Law |
+---------+ 1 N +-----+