added first example: an interactive tutorial for this app
This commit is contained in:
parent
a9241f2777
commit
4bbaf6fc9e
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
</project>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.3 (/usr/bin/python2.7)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/tenant_tutorial.iml" filepath="$PROJECT_DIR$/.idea/tenant_tutorial.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<component name="DependencyValidationManager">
|
||||
<state>
|
||||
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
||||
</state>
|
||||
</component>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="tenant_tutorial/settings.py" />
|
||||
<option name="manageScript" value="manage.py" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<option name="TRACKING_ENABLED" value="true" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
|
||||
<component name="CreatePatchCommitExecutor">
|
||||
<option name="PATCH_PATH" value="" />
|
||||
</component>
|
||||
<component name="DaemonCodeAnalyzer">
|
||||
<disable_hints />
|
||||
</component>
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||
<OptionsSetting value="true" id="Add" />
|
||||
<OptionsSetting value="true" id="Remove" />
|
||||
<OptionsSetting value="true" id="Checkout" />
|
||||
<OptionsSetting value="true" id="Update" />
|
||||
<OptionsSetting value="true" id="Status" />
|
||||
<OptionsSetting value="true" id="Edit" />
|
||||
<ConfirmationsSetting value="0" id="Add" />
|
||||
<ConfirmationsSetting value="0" id="Remove" />
|
||||
</component>
|
||||
<component name="ProjectReloadState">
|
||||
<option name="STATE" value="0" />
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<list size="0" />
|
||||
</component>
|
||||
<component name="ShelveChangesManager" show_recycled="false" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="VcsContentAnnotationSettings">
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<option name="myTodoPanelSettings">
|
||||
<TodoPanelSettings />
|
||||
</option>
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
from django import forms
|
||||
|
||||
|
||||
class GenerateUsersForm(forms.Form):
|
||||
pass
|
|
@ -0,0 +1,8 @@
|
|||
from django.db import models
|
||||
from tenant_schemas.models import TenantMixin
|
||||
|
||||
|
||||
class Client(TenantMixin):
|
||||
name = models.CharField(max_length=100)
|
||||
description = models.TextField(max_length=200)
|
||||
created_on = models.DateField(auto_now_add=True)
|
|
@ -0,0 +1,42 @@
|
|||
from django.contrib.auth.models import User
|
||||
from django.db.utils import DatabaseError
|
||||
from django.views.generic import FormView
|
||||
from customers.forms import GenerateUsersForm
|
||||
from customers.models import Client
|
||||
from random import choice
|
||||
|
||||
|
||||
class TenantView(FormView):
|
||||
form_class = GenerateUsersForm
|
||||
template_name = "index_tenant.html"
|
||||
success_url = "/"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TenantView, self).get_context_data(**kwargs)
|
||||
context['tenants_list'] = Client.objects.all()
|
||||
context['users'] = User.objects.all()
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
User.objects.all().delete() # clean current users
|
||||
|
||||
# generate five random users
|
||||
USERS_TO_GENERATE = 5
|
||||
first_names = ["Aiden", "Jackson", "Ethan", "Liam", "Mason", "Noah", "Lucas",
|
||||
"Jacob", "Jayden", "Jack", "Sophia", "Emma", "Olivia", "Isabella",
|
||||
"Ava", "Lily", "Zoe", "Chloe", "Mia", "Madison"]
|
||||
last_names = ["Smith", "Brown", "Lee ", "Wilson", "Martin", "Patel", "Taylor",
|
||||
"Wong", "Campbell", "Williams"]
|
||||
|
||||
while User.objects.count() != USERS_TO_GENERATE:
|
||||
first_name = choice(first_names)
|
||||
last_name = choice(last_names)
|
||||
try:
|
||||
User.objects.create_user(username=(first_name+last_name).lower(),
|
||||
email="%s@%s.com" % (first_name, last_name),
|
||||
first_name=first_name,
|
||||
last_name=last_name).save()
|
||||
except DatabaseError:
|
||||
pass
|
||||
|
||||
return super(TenantView, self).form_valid(form)
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tenant_tutorial.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<style type="text/css">
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
body * * { padding:0; }
|
||||
body { font:small sans-serif; }
|
||||
body>div { border-bottom:1px solid #ddd; }
|
||||
h1 { font-weight:normal; }
|
||||
h2 { margin-bottom:.8em; }
|
||||
h2 span { font-size:80%; color:#666; font-weight:normal; }
|
||||
h3 { margin:1em 0 .5em 0; }
|
||||
h4 { margin:0 0 .5em 0; font-weight: normal; }
|
||||
table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; }
|
||||
tbody td, tbody th { vertical-align:top; padding:2px 3px; }
|
||||
thead th { padding:1px 6px 1px 3px; background:#fefefe; text-align:left; font-weight:normal; font-size:11px; border:1px solid #ddd; }
|
||||
tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; }
|
||||
ul { margin-left: 2em; margin-top: 1em; }
|
||||
pre { background: white; padding: 10px; margin: 15px; border-left: 3px solid #006600; }
|
||||
#summary { background: #EFFFE0; }
|
||||
#summary h2 { font-weight: normal; color: #666; }
|
||||
#footer { background:#eee; overflow: auto; }
|
||||
#instructions { background:#f6f6f6; }
|
||||
#summary table { border:none; background:transparent; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="summary">
|
||||
{% block summary %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="instructions">
|
||||
{% block instructions %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p style="float: left;">Current Tenant:
|
||||
{% if request.tenant %}
|
||||
<a href="http://{{ request.tenant.domain_url }}:8000">{{ request.tenant.name }}</a>
|
||||
(<code>{{ request.tenant.schema_name }}</code>, <code>{{ request.tenant.domain_url }}</code>).
|
||||
|
||||
Available Tenants:
|
||||
{% else %}
|
||||
None.
|
||||
{% endif %}
|
||||
{% for tenant in tenants_list %}
|
||||
<a href="http://{{ tenant.domain_url }}:8000">{{ tenant.name }}</a> ·
|
||||
{% endfor %}</p>
|
||||
<p style="float: right;">
|
||||
<a href="https://github.com/bernardopires/django-tenant-schemas">django-tenant-schemas</a> · <a href="https://django-tenant-schemas.readthedocs.org/en/latest/">Documentation</a>
|
||||
</p>
|
||||
</div>
|
||||
</body></html>
|
|
@ -0,0 +1,116 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}Tenant Tutorial{% endblock %}
|
||||
|
||||
{% block summary %}
|
||||
<h1>Welcome to the Tenant Tutorial!</h1>
|
||||
<h2>This interactive tutorial will teach you how to use <a href="https://github.com/bernardopires/django-tenant-schemas">django-tenant-schemas</a>.</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block instructions %}
|
||||
{% if need_sync %}
|
||||
<h2>First Step: Sync your database</h2>
|
||||
<p>Your database is empty, so the first step is to sync it. We only want to sync the <code>SHARED_APPS</code>.
|
||||
For your convenience, here's the contents of <code>SHARED_APPS</code>:</p>
|
||||
<ul>
|
||||
{% for app in shared_apps %}
|
||||
<li>{{ app }}</li>
|
||||
{% endfor %}
|
||||
</ul><br>
|
||||
<p>Just run the command below on your shell to sync <code>SHARED_APPS</code>. Make sure your environment
|
||||
has <code>Django</code> and <code>django-tenant-schemas</code> available.</p>
|
||||
<pre>$ python manage.py sync_schemas --shared</pre>
|
||||
<p>When you're done refresh this page.</p>
|
||||
{% elif no_public_tenant %}
|
||||
<h2>Second Step: Create a public tenant</h2>
|
||||
<h3>So how does django-tenant-schemas work?</h3>
|
||||
<p><code>django-tenant-schemas</code> uses the request's hostname to try to find a tenant.
|
||||
When a match is found,
|
||||
<a href="http://www.postgresql.org/docs/8.1/static/ddl-schemas.html">PostgreSQL's search path</a>
|
||||
is automatically set to be this tenant.</p>
|
||||
<br>
|
||||
<p>For this request, <code>django-tenant-schemas</code> couldn't find any tenant for the current address (<code>{{ hostname }}</code>).
|
||||
When no tenant is found, <code>django-tenant-schemas</code> normally returns a <code>404</code>, but since
|
||||
this is a tutorial and no tenant exists yet, we let it proceed.
|
||||
<h3>Recommended Tenant's URLs Structure</h3>
|
||||
<p>Let's assume you have your main website at <code>trendy-sass.com</code>. The recommended structure is
|
||||
to put your tenants at subdomains, like <code>tenant1.trendy-sass.com</code>,
|
||||
<code>tenant2.trendy-sass.com</code> and so forth.</p>
|
||||
<h3>Creating the public tenant</h3>
|
||||
<p><code>django-tenant-schemas</code> requires a tenant for all addresses you use, including your main website,
|
||||
which we will from now on refer to as the public tenant.</p>
|
||||
<br>
|
||||
<p>Our model is called <code>Customer</code> and looks like this (taken from <code>models.py</code>):</p>
|
||||
<pre>
|
||||
class Client(TenantMixin):
|
||||
name = models.CharField(max_length=100)
|
||||
description = models.TextField(max_length=200)
|
||||
created_on = models.DateField(auto_now_add=True)</pre>
|
||||
<p>Let's create a tenant for our main website, located at <code>{{ hostname }}</code>. Open up a shell and enter the <code>django</code> shell:</p>
|
||||
<pre>$ ./manage.py shell</pre>
|
||||
<p>To create a tenant run the following commands:</p>
|
||||
<pre>from customers.models import Client</pre>
|
||||
<pre>
|
||||
Client(domain_url='{{ hostname }}',
|
||||
schema_name='public',
|
||||
name='Trendy SaSS',
|
||||
description='Public Tenant').save()</pre>
|
||||
<p>Done! <code>django-tenant-schemas</code> will now be able to locate our public tenant and won't return 404. Refresh this page to see the next step.</p>
|
||||
{% elif only_public_tenant %}
|
||||
<h2>Third Step: Create Tenants</h2>
|
||||
<p>We've already created the public tenant, now it's time to create some tenants for subdomains. I assume you're running this on your local machine,
|
||||
so the easiest way to simulate domains is to edit your <a href="http://en.wikipedia.org/wiki/Hosts_(file)"><code>hosts</code> file</a>.
|
||||
<a href="http://www.rackspace.com/knowledge_center/article/how-do-i-modify-my-hosts-file">Here are instructions for all platforms</a>.
|
||||
I'll assume you're on Linux.</p>
|
||||
<pre>$ nano /etc/hosts </pre>
|
||||
<p>Add the following lines:</p>
|
||||
<pre>
|
||||
127.0.0.1 tenant1.trendy-sass.com
|
||||
127.0.0.1 tenant2.trendy-sass.com</pre>
|
||||
<p>We're basically tricking our computer to think both <code>tenant1.trendy-sass.com</code> and <code>tenant2.trendy-sass.com</code> point to <code>127.0.0.1</code>.
|
||||
Once you're done, try visiting <a href="http://tenant1.trendy-sass.com:8000">tenant1.trendy-sass.com</a>,
|
||||
you should get a django <code>404</code>. As we have previously mentioned, we don't have a tenant there yet, so a <code>404</code> will be thrown.<br></p>
|
||||
<br>
|
||||
<p>We can now add tenants using these URLs and our project will be able to find them and identify them as our tenants. Back to the django shell:</p>
|
||||
|
||||
<pre>$ ./manage.py shell</pre>
|
||||
<pre>from customers.models import Client</pre>
|
||||
<pre>
|
||||
Client(domain_url='tenant1.trendy-sass.com',
|
||||
schema_name='tenant1',
|
||||
name='Tenant1 - Awesome',
|
||||
description='Our first real tenant, awesome!').save()</pre>
|
||||
<p>Saving a tenant that didn't exist before will create their schema and sync <code>TENANT_APPS</code> automatically. You should see
|
||||
the following lines as the result.</p>
|
||||
<pre>=== Running syncdb for schema: tenant1
|
||||
Creating tables ...
|
||||
Creating table auth_permission
|
||||
Creating table auth_group_permissions
|
||||
Creating table auth_group
|
||||
Creating table auth_user_groups
|
||||
Creating table auth_user_user_permissions
|
||||
Creating table auth_user
|
||||
Creating table django_content_type
|
||||
Installing custom SQL ...
|
||||
Installing indexes ...
|
||||
Installed 0 object(s) from 0 fixture(s)</pre>
|
||||
<p>This means your tenant was installed successfully. Now create the second tenant.</p>
|
||||
<pre>
|
||||
Client(domain_url='tenant2.trendy-sass.com',
|
||||
schema_name='tenant2',
|
||||
name='Tenant2 - Even Awesome-r',
|
||||
description='A second tenant, even more awesome!').save()</pre>
|
||||
|
||||
<p>Now try visiting <a href="http://tenant1.trendy-sass.com:8000">tenant1.trendy-sass.com</a> and
|
||||
<a href="http://tenant2.trendy-sass.com:8000">tenant2.trendy-sass.com</a> or refresh this page.</p>
|
||||
{% else %}
|
||||
<h2>Tutorial Complete!</h2>
|
||||
<p>Well done, you have completed the tutorial! Use the bottom menu to see your tenants.</p>
|
||||
<h3>Where to go from here</h3>
|
||||
<p>There are some interesting features that we did not cover.</p>
|
||||
<ul>
|
||||
<li><a href="https://django-tenant-schemas.readthedocs.org/en/latest/install.html#south-migrations">South Migrations</a>. This app supports <code>South</code> migrations.</li>
|
||||
<li><a href="https://django-tenant-schemas.readthedocs.org/en/latest/install.html#tenant-view-routing">Tenant View-Routing</a>. Serve different views for the same path. (this tutorial makes use of this feature)</li>
|
||||
<li><a href="https://django-tenant-schemas.readthedocs.org/en/latest/use.html#management-commands">Management Commands</a>. Run a command for a particular tenant.</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,27 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}{{ request.tenant.name }}{% endblock %}
|
||||
|
||||
{% block summary %}
|
||||
<h1>{{ request.tenant.name }}</h1>
|
||||
<h2>{{ request.tenant.description }}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block instructions %}
|
||||
<p>Each tenant is on a separate schema and contains different data. Take a look at the users list of this tenant.</p>
|
||||
<h3>Users List</h3>
|
||||
{% if users %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li>{{ user.first_name }} {{ user.last_name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>You don't have any users!</p>
|
||||
{% endif %}
|
||||
<h3>Generate Random Users</h3>
|
||||
<p>Click the button below to generate five random users.</p><br>
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Generate Users" />
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -0,0 +1,32 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import connection
|
||||
from django.http import Http404
|
||||
from tenant_schemas.utils import get_tenant_model, remove_www_and_dev, get_public_schema_name
|
||||
from django.db import utils
|
||||
|
||||
|
||||
class TenantTutorialMiddleware(object):
|
||||
def process_request(self, request):
|
||||
connection.set_schema_to_public()
|
||||
hostname_without_port = remove_www_and_dev(request.get_host().split(':')[0])
|
||||
|
||||
TenantModel = get_tenant_model()
|
||||
|
||||
try:
|
||||
request.tenant = TenantModel.objects.get(domain_url=hostname_without_port)
|
||||
except utils.DatabaseError:
|
||||
request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
|
||||
return
|
||||
except TenantModel.DoesNotExist:
|
||||
if hostname_without_port in ("127.0.0.1", "localhost"):
|
||||
request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
|
||||
return
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
connection.set_tenant(request.tenant)
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
if hasattr(settings, 'PUBLIC_SCHEMA_URLCONF') and request.tenant.schema_name == get_public_schema_name():
|
||||
request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
|
|
@ -0,0 +1,173 @@
|
|||
# Django settings for tenant_tutorial project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@example.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'tenant_schemas.postgresql_backend', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
||||
'NAME': 'tenant_tutorial', # Or path to database file if using sqlite3.
|
||||
'USER': 'postgres',
|
||||
'PASSWORD': 'root',
|
||||
'HOST': 'localhost', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
|
||||
'PORT': '', # Set to empty string for default.
|
||||
}
|
||||
}
|
||||
|
||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# In a Windows environment this must be set to your system time zone.
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# If you set this to False, Django will not format dates, numbers and
|
||||
# calendars according to the current locale.
|
||||
USE_L10N = True
|
||||
|
||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
||||
USE_TZ = True
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/var/www/example.com/media/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = ''
|
||||
|
||||
# URL prefix for static files.
|
||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = 'as-%*_93v=r5*p_7cu8-%o6b&x^g+q$#*e*fl)k)x0-t=%q0qa'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'tenant_tutorial.middleware.TenantTutorialMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Uncomment the next line for simple clickjacking protection:
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.core.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.core.context_processors.debug',
|
||||
'django.core.context_processors.media',
|
||||
'django.core.context_processors.static',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'tenant_tutorial.urls_tenants'
|
||||
PUBLIC_SCHEMA_URLCONF = 'tenant_tutorial.urls_public'
|
||||
|
||||
# Python dotted path to the WSGI application used by Django's runserver.
|
||||
WSGI_APPLICATION = 'tenant_tutorial.wsgi.application'
|
||||
|
||||
import os
|
||||
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), '..', 'templates').replace('\\','/'),)
|
||||
|
||||
SHARED_APPS = (
|
||||
'tenant_schemas', # mandatory
|
||||
'customers', # you must list the app where your tenant model resides in
|
||||
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
|
||||
TENANT_APPS = (
|
||||
# The following Django contrib apps must be in TENANT_APPS
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.auth',
|
||||
)
|
||||
|
||||
TENANT_MODEL = "customers.Client" # app.Model
|
||||
|
||||
INSTALLED_APPS = SHARED_APPS + TENANT_APPS
|
||||
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
from django.conf.urls import patterns
|
||||
from tenant_tutorial.views import HomeView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', HomeView.as_view()),
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
from django.conf.urls import patterns
|
||||
from customers.views import TenantView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', TenantView.as_view()),
|
||||
)
|
|
@ -0,0 +1,30 @@
|
|||
from django.conf import settings
|
||||
from django.db import utils
|
||||
from django.views.generic import TemplateView
|
||||
from tenant_schemas.utils import remove_www_and_dev
|
||||
from customers.models import Client
|
||||
|
||||
|
||||
class HomeView(TemplateView):
|
||||
template_name = "index_public.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(HomeView, self).get_context_data(**kwargs)
|
||||
|
||||
hostname_without_port = remove_www_and_dev(self.request.get_host().split(':')[0])
|
||||
try:
|
||||
Client.objects.get(schema_name='public')
|
||||
except utils.DatabaseError:
|
||||
context['need_sync'] = True
|
||||
context['shared_apps'] = settings.SHARED_APPS
|
||||
context['tenants_list'] = []
|
||||
return context
|
||||
except Client.DoesNotExist:
|
||||
context['no_public_tenant'] = True
|
||||
context['hostname'] = hostname_without_port
|
||||
|
||||
if Client.objects.count() == 1:
|
||||
context['only_public_tenant'] = True
|
||||
|
||||
context['tenants_list'] = Client.objects.all()
|
||||
return context
|
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
WSGI config for tenant_tutorial project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "tenant_tutorial.settings"
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tenant_tutorial.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
Loading…
Reference in New Issue