10 个站点通过相同的代码库 django MTI、ABC 或 EAV

问题描述 投票:0回答:2

我有一个基于 Django 的网上商店,在过去的一年里一直在发展。目前,大约有 8 个国家/地区的特定商店通过相同的代码库运行,再加上一个 API,并且很快就会有一个 B2B 网站,并且还会有更多国家/地区添加到列表中。

模型结构需要变化,特别是地址模型、帐户模型等中的字段。

让事情变得更复杂的是,该网站正在运行 multidb,每个商店实例都在一个单独的数据库中。所以我遇到的情况是我可能有一个基本的 ABC 模型,例如:

class Address(models.Model):
    class Meta:
        abstract=True

class Address_UK(Address):
    class Meta:
        db_table="shop_address"

class Address_IT(Address):
    class Meta:
        db_table="shop_address"
[etc]

然后在整个应用程序中进行编码以选择正确的型号,例如

if countrysettings.country == "UK":
    address = Address_UK()
elif countrysettings.country == "IT":
    address = Address_IT()

countrysettings.country实际上是一个单独的设置类,它是threading.local的子类,而国家/地区代码也与settings.DATABASES中的键相对应,由地理位置中间件处理程序配置。因此,选择了正确的数据库,并且模型特定的变化反映在每个国家的数据库中。

但是这种方法存在问题:

  1. 它完全破坏了syncdb,对南方没有好处,除非我破解./manage.py,这样我就可以传入国家数据库,而不是要求中间件来设置它。

  2. 看起来很乱。如此多的 if Countrysettings.country == "xx": 代码,以及如此多的子类模型。

所以我正在考虑使用 django-eav 来代替,但我预见到管理中会出现问题,特别是字段排序。我知道 django-eav 将为管理员构建一个包含 eav 字段的模型表单,但我理想情况下希望显示或隐藏与国家/地区相关的这些字段。

我也考虑过有一个非抽象基类,例如地址,然后在需要时创建特定于国家/地区的变体(例如模型表继承)。但后来我预见到基本模型会因每个模型变体的 one2one 字段而超载。但这可以解决管理员的问题。

另一个选择可能是有一个额外的数据字段,并将附加字段序列化为 json 或 csv 或其他内容并将它们存储在该字段中。

python django model entity-attribute-value
2个回答
2
投票

我可以考虑一些其他方法来解决你的问题。我认为选项 1 或选项 2 是最好的,但人们可能会选择选项 3。

选项 1:一个代码库、一个数据库、一个 django 实例、站点框架: 如果您实际上不需要为每个商店使用不同的数据库,请创建所有表和/或所有可能的字段,并巧妙地使用 sites 框架 用于秃鹰场和模型。例如:为每个地址保留一个 address_type 字段等,并在每个站点的同一数据库上使用不同的字段(和表)。这会使您的代码更加复杂,但会大大简化您的 IT。如果站点之间的代码更改非常少,请使用此选项。 (顺便说一句 - json 序列化是地址的一个不错的选择)。

选项2:一个代码库,许多数据库,许多django实例:使用相同的代码库设置许多站点,但仔细使用Python的条件设置和动态功能来为每个站点生成不同的模型。每个站点都有自己的 settings-uk.py、settings-us.py 等,并且有自己的数据库和使用动态模型的模型。例如:

from django.conf import settings
# ...
class Address(models.Model):
     name = models.CharField(max_length=100)
     if settings.country == "US":
        state = models.CharField(max_length=2)
     else:
        country = models.CharField(max_length=100)

此方法的其他可能技巧:通过设置启用/禁用应用程序;在 wsgi script/manage.py 脚本中为应用程序制作自定义 pythonpath;使用

if settings.country=='us': import uk_x as x else: import us_x as x
。另请参阅:http://code.flickr.com/blog/2009/12/02/flipping-out/

选项3:并行代码分支,许多数据库,许多django实例:使用git保留代码的一些分支并相互重新建立基础。需要更多的 IT 工作。如果您计划拥有许多数据库和服务器(以及许多开发人员?),无论如何,您可能会发现这很有用。

另一种选择:一个数据库,许多 django 实例每个实例自定义 settings.py,无需站点框架。


1
投票

其实EAV可以解决你的问题。事实上,您可以使用 EAV 显示特定于当前对象的国家/地区属性的字段。 这是一个例子



class Country(models.Model):
    name = models.CharField(_("country"), max_length=50

class Address(eav.models.BaseEntity):
    country = models.ForeignKey(Country, related_name="country_attrs")

    # EAV specific staff
    @classmethod
    def get_schemata_for_model(self):
        # When creating object, country field is still not set
        # So we do not return any country-specific attributes
        return AddressEAVSchema.objects.filter(pk=-1).all()

    def get_schemata_for_instance(self, qs):
        # For specific instance, return only country-specific attributes if any
        try:
            return AdressEAVSchema.objects.filter(country=self.country).all()
        except:
            return qs

# Attributes now can be marked as belonging to specific country
class AdressEAVSchema(eav.models.BaseSchema)
    country = models.ForeignKey(Country, related_name="country_attrs")

# Rest of the standard EAV stuff    
class AdressEAVChoice(eav.models.BaseChoice):
    schema = models.ForeignKey(AdressEAVSchema, related_name='choices')

class AddressEAVAttribute(eav.models.BaseAttribute):
    schema = models.ForeignKey(AdressEAVSchema, related_name='attrs')
    choice = models.ForeignKey(AdressEAVChoice, blank=True, null=True)

class Country(models.Model):
    name = models.CharField(_("country"), max_length=50

class Address(eav.models.BaseEntity):
    country = models.ForeignKey(Country, related_name="country_attrs")

    # EAV specific staff
    @classmethod
    def get_schemata_for_model(self):
        # When creating object, country field is still not set
        # So we do not return any country-specific attributes
        return AddressEAVSchema.objects.filter(pk=-1).all()

    def get_schemata_for_instance(self, qs):
        # For specific instance, return only country-specific attributes if any
        try:
            return AdressEAVSchema.objects.filter(country=self.country).all()
        except:
            return qs

# Attributes now can be marked as belonging to specific country
class AdressEAVSchema(eav.models.BaseSchema)
    country = models.ForeignKey(Country, related_name="country_attrs")

# Rest of the standard EAV stuff    
class AdressEAVChoice(eav.models.BaseChoice):
    schema = models.ForeignKey(AdressEAVSchema, related_name='choices')

class AddressEAVAttribute(eav.models.BaseAttribute):
    schema = models.ForeignKey(AdressEAVSchema, related_name='attrs')
    choice = models.ForeignKey(AdressEAVChoice, blank=True, null=True)

使用方法如下:

创建地址属性时,您现在还必须指定它们属于哪个国家/地区。

现在,当您创建新的地址对象本身(例如在管理中)时,保存,然后返回编辑它,您会看到与对象国家/地区匹配的其他国家/地区特定属性。

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.