How to do site specific configuration with Python Django - django

At the moment i run some django sites with site specific config by using multiple settings.py with different SITE_ID in it and application specifig APP_OPTION="foo" settings.
Now i would like to run the sites from one process selecting the site by domain (which is done by get_current_site). But in this case they share the global settings.
One option would be of course to patch the applications to use APP_OPTION[site_id]="foo", another option would be to place the settings in the database. Both seem not to be the best solutions to the problem.
Which is the most elegant way to have such site specific config values?

Related

Django-cms for multiple websites

How to setup a django-cms project to support multiple websites?
There's no reference to this in the official documentation and only limited information in the mailing list, but it's in the headline "A Django application for managing hierarchical pages of content, possibly in multiple languages and/or on multiple sites." and through the Django Sites Framework there's already built in support, and in the admin pages can be associated to different sites.
Related:
Django-CMS: Multiple domains on same project
there are a few different options to manage different websites (and, thus, templates and page contents) in Django-cms.
The basic approach
My favorite is probably the simplest:
In my virtualenv I have a single django-cms installation AND a single "project" that contains ALL the templates I use.
I have a global settings file plus one for each website that does only import all global settings and set "SITE_ID".
from base import *
SITE_ID = XXX
For structure i usually have a settings folder, an empty __init__.py inside, a base.py with all the common settings - including django-cms settings, and then the different websites eg. site1.py site2.py etc. (sometimes my structure is even slightly more complex to also account for dev/production, different machines, etc. but that's not relevant here).
I launch each website as a different instance - I use gunicorn so that's extremely easy too, each one of a different port.
I have an nginx fronted with a separate server configuration for each of my websites, and each of these points to a different gunicorn.
server {
listen 80;
server_name example1.com www.example1.com;
...
location / {
proxy_pass http://localhost:PORT;
}
}
Any of the gunicorn instances can access the admin, and all the data is shared in a single database, but for simplicity
That's all!
Of course it can be done similarly with Apache, mod_wsgi and different virtualhosts.
Advanced
Themes
I actually structured my folders to have an apps folder called themes. Each theme is actually an APP, though mostly contains only the templates and static folders, and it's added to the INSTALLED_APPS.
This allows for cute things such as inheritance and/or overriding between different themes.
Dynamic SITE_ID
It's also possible to use a middleware that will dynamically extract and set the SITE_ID from the URL. This allows to have one single instance... but I don't see any real advantage in this solution and rather find it a potential source of risks.

How to setup django with unique settings.py per SITE_ID

I have been researching the various (and there are many) ways to have multiple sites under a single Django framework.
I still don't see a solution that fits well for my use case. The use case being:
A single database, as the majority of data (95%+) will be shared between two
apps
Two distinct user types, that each login via different domains,
and in fact interact with the same raw data, differently
What I want to achieve is this:
When a visitor comes to example.com, the settings for SITE_ID = 1 are in effect.
When a visitor comes to training.example.com, the settings for SITE_ID = 2 are in effect.
I want to do this because:
I want to use explicitly different UserProfile objects for the different users
I want to mix and match views and apps at my will between the different domains
Django docs on the Sites framework don't seem to help me, even though with this and some other articles around, I can see a few possibilities.
My current thinking is to actually solve it using the wsgi server (uWSGI in my case), where I'd take Django's default wsgi.py, duplicate it, and each wsgi conf wiill have its own Django settings.py.
Then the server will in fact be serving two distinct wsgi apps, even though they use much of the same code.
So, in this scenario, my Django project will have:
example.com.settings.py (SITE_ID = 1)
training.example.com.settings.py (SITE_ID = 2)
example.com.wsgi.py (uses example.com.settings.py)
training.example.com.wsgi.py (uses training.example.com.settings.py)
My scenario here should work but it will be twice the memory of solving this within the same Django instance.
Any better implementation for what I need to achieve?
For what I know settings.py is unique for every django project, and the SITE_ID is defined at this level. IMHO the best approach is to use many django projects with database routers. In each project you can define the SITE_ID for use with the django sites framework.
https://docs.djangoproject.com/en/dev/topics/db/multi-db/#database-routers

How do I make Django figure out which Site object to use based on "Host" header in the HTTP request?

Consider a Django app built to serve multiple sites with slightly differing content using the standard Django sitesframework.
The traditional way to host this would be to configure multiple Site objects and setup the app in multiple Django projects with each project pointing to a different SITE_ID in their respective settings.py:s.
For various reasons I'd like to avoid having to create a new project for each new site. I want to be able to setup one project and have Django figure out which Site object to use based on the hostname referenced in the incoming HTTP request.
What is the recommended way to achieve this functionality?
Clarification: I want the site framework to ignore settings.SITE_ID (which is hard-coded in settings.py) and instead dynamically fetch Site objects based on what is in the Host header. Why this requirement? I'll be adding and removing sites multiple times per hour and the total amount of sites will exceed 10,000, so setting up a Django project for each site is not an option. Is this a problem that is solvable in Django? If so, what is the best way to achieve it?
The recommended way is to not attempt it at all, since settings should never change at runtime. Instead, set a variable in your virtual host configuration and have the WSGI adapter script or settings module pick one of the sites based on that.

Django on Twisted with multiple virtual hosts?

I have a django website that I am hosting on twisted via the django WSGIHandler as described here - http://www.clemesha.org/blog/Django-on-Twisted-using-latest-twisted-web-wsgi
All seems OK up to the point where I want to add an extra "site" configuration to my django site using the django Sites framework. Doing so, I add an extra settings.py file for the new site and that seems to work.
What I then want to do is use the twisted NameVirtualHost class to be able to direct one domain (say site1.example.com) to the first settings file, then use another domain (say site2.example.com) to use the second settings file. This works with Apache & mod_wsgi.
The problem I face is that the twisted code can only access one django environment at a time. If I call setup_environ with the first settings file and setup a host for the first domain, a subsequent call to setup_environ will replace the settings file in use so therefore only one set of settings can be used at one time.
Any ideas how to proceed?
Gave up on this in the end. Looks like you cannot easily access 2 Django environments within the same twisted instance. I think it would require multiple instances of twisted with a reverse proxy or some sort of multiprocess hacking - either way its not worth the effort for me so I'm going to try something else...

How to locally test Django's Sites Framework

Django has the sites framework to support multiple web site hosting from a single Django installation.
EDIT (below is an incorrect assumption of the system)
I understand that middleware sets the settings.SITE_ID value based on a lookup/cache of the request domain.
ENDEDIT
But when testing locally, I'm at http://127.0.0.1:8000/, not http://my-actual-domain.com/
How do I locally view my different sites during development?
Create a separate settings.py file for every site, including an appropriate SITE_ID setting. Of course you can use the import statement to share common setting between files.
From now on, when running Django development server specify the --settings option to tell Django which site to run.
For example (assuming you've got two setting files - settings_first.py and settings_second.py):
manage.py runserver --settings settings_first
will run the first site, and
manage.py runserver --settings settings_second
will give you an access to the second site.
You can also run them simultaneously, specifying different ports:
manage.py runserver 8001 --settings settings_first
manage.py runserver 8002 --settings settings_second
The above commands (run on two different consoles) will make the first website accesible under http://127.0.0.1:8001/, and the second one under http://127.0.0.1:8002/
Maybe you are mislead by the documentation. You wrote:
I understand that middleware sets the settings.SITE_ID value based on a lookup/cache of the request domain.
This is not the case. It works exactly the other way around. Django uses the settings.SITE_ID value to look up the correct Site object in the database. This returns your prefered domain and site name.
The sites application was designed to fill the (in my opinion) rare usecase that you want to have multiple sites with the same database in the background. This allows you to publish the same articles on different sites but still have the flexibility that some models are available just for a single site.
For developing multiple projects (that doesn't actually make use of the sites framework) you don't have to specify anything special. You can use the default SITE_ID set to 1. For utilizing the admin's view on website links you can set in your development database the Site's domain to localhost:8000.
If you want to develop multiple sites using the same database (and make use of the sites framework) you must have each project with a distinct SITE_ID but the same database settings. The values for SITE_ID in each project on your development machine are in most cases the same as for your production servers.
FYI - I released django-dynamicsites today which has facilities to solve this issue - https://bitbucket.org/uysrc/django-dynamicsites/src