I have a django based web shop that has been evolving over the past year. Currently there's about 8 country specific shops running through the same code base, plus an API, and there's soon to be a B2B website, and a few more countries to add to the list.
Variations are needed in model structure, particularly around fields in address models, the account model, and so on.
To make matters a bit more complicated, the site is running multidb with each shop instance in a separate db. So I have a situation where I might have a base ABC model, e.g:
class Address(models.Model):
class Meta:
abstract=True
class Address_UK(Address):
class Meta:
db_table="shop_address"
class Address_IT(开发者_如何学JAVAAddress):
class Meta:
db_table="shop_address"
[etc]
Then code throughout the app to select the the correct model, e.g.
if countrysettings.country == "UK":
address = Address_UK()
elif countrysettings.country == "IT":
address = Address_IT()
The countrysettings.country is actually a separate settings class which subclasses threading.local and the country code, which also corresponds with a key in settings.DATABASES, is configured by a geolocation middleware handler. So the correct database is selected, and the model specific variations are reflected in each country database.
But there are problems to this approach:
It completely breaks syncdb and is no good for south, unless I hack ./manage.py so I can pass in the country db, instead of requiring the middleware to set it.
It seems messy. So much if countrysettings.country == "xx": code lying about, and so many sub classed models.
So I was thinking of using django-eav instead, but I foresee problems in the admin, and in particular field ordering. I know that django-eav will build a modelform for the admin that includes the eav fields, but I'd ideally want these to be displayed or hidden relevant to the country.
Also I've considered having a none abstract base class, e.g. Address, and then creating country specific variations where needed (e.g Model Table Inheritance). But then I foresee the base models getting overloaded with one2one fields to each model variant. But it would solve issues with the admin.
Another option might be to have an extra data field, and to serialise additional fields into json or csv or something and store them in this field.
I can think about a few others way to attack your problem. I believe option 1 or option 2 are the best, but one might choose option 3.
Option 1: One code base, One db, One django instance, Sites framwork: If you do not actually need a distinct db for each store, create all tables and/or all possible fields, and smartly use the sites framework for condour fields fields and models. For example: keep for each address a address_type field etc and use different fields (and tables) on the same db for each site. This makes your code more complicated but simplifies your IT a lot. Use this if the code changes between sites is very minimal. (btw - The json serialization is a good option for address).
Option2: One code base, Many DBs, Many django instances: Set many sites with the same code base but carefully use conditional settings and dynamic features of python to generate different models per site. Each site will have it's own settings-uk.py, settings-us.py etc., and will have it's own db and own models using dynamic models. For example:
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)
Other possible tricks for this method: Enable/disable apps via the settings; Crafting custom pythonpaths for appsin the wsgi script/manage.py script; use if settings.country=='us': import uk_x as x else: import us_x as x
. See also: http://code.flickr.com/blog/2009/12/02/flipping-out/
Option3: Parallel code branches, Many DBs, Many django instances: Use git to keep a few branches of your code and rebase them with each other. Requires much more IT effort. If you are planning to have many db and many server, (and many developers?) anyway, you might find this useful.
Another options: One DB, many django instances custom settings.py per instance without sites framework.
Actually EAV can solve your issue. You can in fact use EAV to show fields that a specific to the country attribute of the current object. Here is an example
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)
Here how to use it:
When you create Address attributes you now have also to specify which country they belong to.
Now, when you create new Address object itself (say in admin), save, and then go back editing it you see additional country-specific attributes that match objects country.
Hope this helps.
精彩评论