我有一个基于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中的键对应,由地理位置中间件处理程序配置。因此,选择了正确的数据库,并且模型特定的变体反映在每个国家/地区数据库中。
但这种方法存在问题:
它完全打破了syncdb并且对南方不利,除非我破解./manage.py所以我可以传入国家db,而不是要求中间件设置它。
看起来很乱。如果countrysettings.country ==“xx”:代码说谎,以及如此多的子类模型。
所以我考虑使用django-eav,但我预见到管理员的问题,特别是字段排序。我知道django-eav会为管理员构建一个包含eav字段的模型,但我希望这些显示或隐藏与国家相关。
我也考虑过一个没有抽象的基类,例如解决,然后根据需要创建特定于国家/地区的变体(例如,模型表继承)。但后来我预见到基础模型会将每个模型变量的one2one字段超载。但它可以解决管理员的问题。
另一种选择可能是拥有一个额外的数据字段,并将其他字段序列化为json或csv等,并将它们存储在该字段中。
答案 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中),保存,然后返回编辑它时,您会看到与对象国家/地区匹配的其他国家/地区特定属性。
希望这有帮助。