Ответ 1
Недавно я разработал систему электронной коммерции с аналогичными требованиями - многие экземпляры, запущенные из одного и того же проекта, используют почти все. В предыдущей версии системы была куча независимых установок (~ 30), поэтому она была довольно незаметна. Я уверен, что требования по-прежнему отличаются от ваших (например, все экземпляры поделились одними и теми же моделями в моем случае), но все же может быть полезно поделиться своим опытом.
Вы правы, что Django не помогает в таких сценариях, как это из коробки, но на самом деле удивительно легко работать с ним. Вот краткое описание того, что я сделал.
Я мог видеть синергию между тем, чего я хотел достичь, и django.contrib.sites
. Также потому, что многие сторонние приложения Django знают, как работать с ним и использовать его, например, для создания абсолютных URL-адресов для текущего сайта. Основная проблема с sites
заключается в том, что он хочет, чтобы вы указали текущий идентификатор сайта в settings.SITE_ID
, что очень наивный подход к проблеме с несколькими узлами. Естественно, что вы хотите, и то, что вы также упоминаете, - это определить текущий сайт из заголовка запроса Host
. Чтобы исправить эту проблему, я заимствовал идею hook от django-multisite
: https://github.com/shestera/django-multisite/blob/master/multisite/threadlocals.py#L19
Далее я создал приложение, инкапсулирующее все функциональные возможности, связанные с аспектом multi-хоста моего проекта. В моем случае приложение было вызвано stores
, и среди прочего он показал два важных класса: stores.middleware.StoreMiddleware
и stores.models.Store
.
Класс модели является подклассом django.contrib.sites.models.Site
. Хорошая вещь о подклассе Site
заключается в том, что вы можете передать Store
любой функции, где ожидается Site
. Таким образом, вы по-прежнему используете только старую, хорошо документированную и проверенную структуру sites
. В класс Store
я добавил все поля, необходимые для настройки всех разных магазинов. Таким образом, он получил поля типа urlconf
, theme
, robots_txt
и еще что.
Функция класса middleware должна соответствовать заголовку Host
с соответствующим экземпляром Store
в базе данных. После получения соответствия Store
он будет исправлять SITE_ID
способом, аналогичным https://github.com/shestera/django-multisite/blob/master/multisite/middleware.py. Кроме того, он посмотрел на Store
urlconf
, и если он не был None, он установил бы request.urlconf
для применения своих специальных требований к URL-адресу. После этого текущий экземпляр Store
хранился в request.store
. Это оказалось невероятно полезным, потому что я мог делать такие вещи в моих взглядах:
def homepage(request):
featured = Product.objects.filter(featured=True, store=request.store)
...
request.store
стал естественным дополнительным размером объекта request
во всем проекте для меня.
Еще одна вещь, которая была определена в классе Store
, была функцией get_absolute_url
, реализация которой выглядела примерно так:
def get_absolute_url(self, to='/'):
"""
Return an absolute url to this `Store` or to `to` on this store.
The URL includes http:// and the domain name of the store.
`to` can be an object with `get_absolute_url()` or an absolute path as string.
"""
if isinstance(to, basestring):
path = to
elif hasattr(to, 'get_absolute_url'):
path = to.get_absolute_url()
else:
raise ValueError(
'Invalid argument (need a string or an object with get_absolute_url): %s' % to
)
url = 'http://%s%s%s' % (
self.domain,
# This setting allowed for a sane development environment
# where I just set it to ".dev:8000" and configured `dnsmasq`.
# The same value was also removed from the `Host` value in the middleware
# before looking up the `Store` in database.
settings.DOMAIN_SUFFIX,
path
)
return url
Поэтому я мог бы легко создавать URL-адреса для объектов, отличных от текущего хранилища, например:
# Redirect to `product` on `store`.
redirect(store.get_absolute_url(product))
Это было в основном все, что мне нужно, чтобы иметь возможность внедрять систему, позволяющую пользователям создавать новый электронный магазин, живущий в собственном домене, с помощью администратора Django.