2

I'm new to Django and python in general, so please bear with me. I want to create a very simple SaaS app to get started with Django and python. My design dictates that all domains will run on the same codebase. The only thing that will differ between each website is the database connection details. (IE each domain receives its own database. All databases contain the same schema)

How would I go about this with Django? I've seen the Sites framework, but I'm not sure that's what I'm looking for. Essentially, I'd like to either

A) Have Django look up the database connection details from a MASTER database, when the page loads, and then use those details when connecting to the site database

B) Have a settings.py per-site and have django include the correct file at runtime (IE settings_domain.py)

C) Have the index WSGI include the appropriate settings file based on the accessing domain (This seems like it could work, but I'm sure I'm missing a potential pitfall of this implementation)

D) Some other implementation I haven't thought of....

Am I making this more complicated than it needs to be? How can I easily implement this functionality into a new django project?

Kevin
  • 512
  • 4
  • 15

2 Answers2

3

Well, there's an interesting project at: https://bitbucket.org/uysrc/django-dynamicsites. It attempts to let you have entirely unique sites all running off the same project. However, currently, I don't think it will do much for you since you're going to need a bit more customization over settings than it offers.

I actually just did this myself, and originally tried to use django-dynamicsites, but found it a little too touchy and not quite right for my project. As a result, I ended up taking a little bit different approach.

My project has a "sites" module and in that a module for each unique site. Each module has it's own settings.py and urls.py and templates directory. For example:

sites
    - __init__.py
    - site1
        - __init__.py
        - settings.py
        - urls.py
        - templates
            - base.html
    - site 2
        - __init__.py
        - settings.py
        - urls.py
        - templates
            - base.html

Each settings.py looks roughly like this:

from myproject.settings import *

SITE_ID = 1
URL_CONF = 'sites.site1.urls'

SITE_ROOT = os.path.dirname(__file__)
TEMPLATE_DIRS = (
    os.path.join(SITE_ROOT, 'templates')
)

CACHE_MIDDLEWARE_KEY_PREFIX = 'site1'

So, what this does is import your projects settings file, and then overrides the settings that are unique to the site. Then, all you have to do is make sure whatever server you're using loads the particular site's settings.py instead of the main project settings.py. I'm using a combo of nginx+Gunicorn, so here's roughly how that config looks:

site1.conf (nginx)

upstream site1 {
    server 127.0.0.1:8001 fail_timeout=0;
}

server {
    listen 80;
    server_name site1.domain.com;

    root /path/to/project/root;

    location / {
        try_files $uri @proxy;
    }

    location @proxy {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 10;
        proxy_read_timeout 30;
        proxy_pass http://site1;
        proxy_redirect off;
    }

}

I use supervisor to manage the Gunicorn server, so here's my config for that:

site1.conf (supervisor)

[program:site1]
command=/path/to/virtualenv/bin/python manage.py run_gunicorn --settings=sites.site1.settings
directory=/path/to/project/root
user=www-data
autostart=true
autorestart=true
redirect_stderr=True

The important part is that there's no fancy Django middleware or such checking for particular hosts and going this way or that accordingly. You fire up a Django instance for each on uwsgi, Gunicorn, etc. pointing to the right settings.py file, and your webserver proxies the requests for each subdomain to the matching upstream connection.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
0

The best way to do this is to do rewrites/proxying on your webserver, whether it be apache or nginx. In apache, you can set up your hosts file with something like this to catch all domains. The django will get a URL where it can then determine which domain it is.

<VirtualHost *:80>
     ServerName domain.com
     ServerAlias *
     DocumentRoot /var/www/domain.com/DjangoProj/public

     Alias /media/ /var/www/domain.com/DjangoProj/misc/media/
     Alias /static/ /var/www/domain.com/DjangoProj/misc/static/

     <Directory /var/www/domain.com/DjangoProj/public>
         Order deny,allow
         Allow from all
     </Directory>

     WSGIScriptAlias / /var/www/domain.com/DjangoProj/app/wsgi.py

     <Directory /var/www/domain.com/DjangoProj/app>
     <Files wsgi.py>
     Order deny,allow
     Allow from all
     </Files>
     </Directory>

     <IfModule mod_rewrite.c>
          RewriteEngine on
          RewriteCond %{HTTP_HOST} ^(.*) [NC] # Catch domain
          RewriteCond %{REQUEST_URI} !userdomain [NC] # Don't rewrite if we already have it
          RewriteRule ^(.*)$ /userdomain/%1$1 [PT,L] # Pass in PT to give the URL back to mod_python!
     </IfModule>

     ErrorLog /var/www/domain.com/logs/error.log
     CustomLog /var/www/domain.com/logs/access.log combined
</VirtualHost>
pyramation
  • 1,631
  • 4
  • 22
  • 35
  • this configuration uses mod_python and apache2. But the idea translates to other webserver configurations – pyramation May 11 '12 at 20:39
  • this configuration allows you to add sites dynamically, without having a different folder structure. Users can add websites to your framework without you having to setup new python files. e.g. `someuser.com` => `yourdomain.com/userdomain/someuser.com`. You can then just do everything custom in your urlconf, etc, and do with it what you like – pyramation May 11 '12 at 20:50
  • This sounds similar to what I was thinking, but I can't make sense of it completely (VERY new to Python and Django both) I understand catching the domain and sending it to the wsgi, but I'm not sure what happens on the Python side to include the correct settings.py file / Connect to the correct database for the domain. My thought was that we would load a unique settings.py, depending on what domain was being called. Each settings.py would have database connection details unique to that domain. Your idea sounds similar, but I'm not sure how to finalize it and make it work. Thanks! – Kevin May 11 '12 at 23:18
  • if you want a separate database for each domain, this is not the solution. This allows you to host all domains for one settings.py and database – pyramation May 15 '12 at 19:37
  • That's what I eventually figured out. So what is the solution for hosting multiple websites with multiple databases (Unique per site) with one Django installation? Why can't I create a unique settings.py PER SITE (With relevant database connection details) and then point the index.wsgi for each domain to the correct settings.py. Would that not work? I am going to attempt to set up a test server this weekend and can test this theory out, but wanted to find out if there was a better way first. – Kevin May 16 '12 at 01:49