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

时间:2011-06-26 17:18:45

标签: django models entity-attribute-value abc

我有一个基于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所以我可以传入国家db,而不是要求中间件设置它。

  2. 看起来很乱。如果countrysettings.country ==“xx”:代码说谎,以及如此多的子类模型。

  3. 所以我考虑使用django-eav,但我预见到管理员的问题,特别是字段排序。我知道django-eav会为管理员构建一个包含eav字段的模型,但我希望这些显示或隐藏与国家相关。

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

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

2 个答案:

答案 0 :(得分:2)

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

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

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

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脚本/ manage.py脚本中为apps创建自定义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 :(得分:1)

实际上EAV可以解决您的问题。实际上,您可以使用EAV显示特定于当前对象的country属性的字段。 这是一个例子

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)

以下是如何使用它:

创建地址属性时,您现在还必须指定他们所属的国家/地区。

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

希望这有帮助。