I have exactly the same issue. Here's how I dealt with it ...
First, the threadlocals module, this is used to keep track of the
site the user is browsing, once it's determined ...
---- mware/threadlocals.py ----
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
__thread_locals = local()
class __NoValueGiven:
""" sentinel value class """
pass
def set(what,value):
setattr(__thread_locals,what,value)
def get(what, defval = __NoValueGiven):
if defval is __NoValueGiven:
# will raise AttributeError if local isn't set.
return getattr(__thread_locals,what)
else:
return getattr(__thread_locals,what,defval)
----cut----
The threadlocals recipe has been floating around for a while; the only thing
I added is to make it a dict, and then call out threadlocals by 'name'.
Next, I have an app with model which represents each of the valid companies;
mapping a URL/hostname to an object in the DB.
---- company/models.py ----
#
# $Id$
#
# File: company/models.py - define model for company objects.
#
from django.db import models
from django.db.models import Q
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from django.conf import settings
import mware.threadlocals as locals
def get_current():
""" return Company object for current company. """
return locals.get('company')
def set_current(cur):
""" set current Company object. """
return locals.set('company', cur)
def set_current_by_domain(domain):
""" set current company object from domain. """
return set_current( Company.objects.get( domain = domain ) )
class Company(models.Model):
""" Object representing a theatre company and links the url
<companyname>.stagemgr.com to a company.
"""
# should be lower case.
domain = models.CharField(_("domain name"), unique = True, maxlength = 100 )
# name of theatre company
name = models.CharField(_("display name"), maxlength=50)
def save(self):
self.domain = self.domain.lower()
super(Company,self).save()
class Meta:
verbose_name = _('company')
verbose_name_plural = _('companies')
ordering = ('domain',)
class Admin:
list_display = ('domain', 'name')
search_fields = ('domain', 'name')
def __str__(self):
return self.domain
# set default company; default ID is set from settings (usually 1)
set_current(Company.objects.get(id = settings.SITE_ID))
---- snip ----
When the company.models module is imported, the last line, sets up the
default -- this is good so that when you're using the shell, things work as
expected.
>From the shell you can use set_current_by_domain('foo.bar.com') to switch
companies.
Next, we have a managers.py which defines a CurrentCompanyManager class
which should be used on any models which should be access-controlled by URL:
---- company/managers.py ----
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from company.models import get_current
import sys
class CurrentCompanyManager(models.Manager):
"Use this to limit objects to those associated with the current site."
def __init__(self, field_name='company'):
super(CurrentCompanyManager, self).__init__()
self.__field_name = field_name
self.__is_validated = False
def get_query_set(self):
if not self.__is_validated:
try:
self.model._meta.get_field(self.__field_name)
except FieldDoesNotExist:
raise ValueError, "%s couldn't find a field named %s in %s." % \
(self.__class__.__name__, self.__field_name,
self.model._meta.object_name)
self.__is_validated = True
company = get_current().id
qset = super(CurrentCompanyManager, self).get_query_set()
# magic!
if company == 1:
return qset
return qset.filter(**{self.__field_name + '__id__exact':
get_current().id })
------ snip --------
By default, this manager uses the managed object's 'company' field as
the index field, but that can be changed when constructing the
CurrentCompanyManager object.
This has a bit of magic in it -- if you're viewing objects from the
URL assocaited with site-id == 1, you get all objects, otherwise, you
get objects filtered by the current company.
(now that I think about it, it should match against settings.SITE_ID, as
a magic #)
Here's an example object which uses the CurrentCompanyManager:
----
from django.db import models
from company.models import Company
from company.managers import CurrentCompanyManager
class Patron(models.Model):
objects = CurrentCompanyManager()
all = models.Manager()
company = models.ForeignKey(Company)
first_name = models.CharField(maxlength=80)
last_name = models.CharField(maxlength=80)
class Admin:
# XXX - hack!
manager = CurrentCompanyManager()
pass
---- snip ----
The 'XXX hack' line is because, by default, the Django admin interface
does not respect the manager class for an object (That's a BUG!) If you
want to manipulate objects in the admin interface, you'll have to
add the 'manager = CurrentCompanyManager()' to your Admin class.
I then add "company" to my INSTALLED_APPS and
"company.middleware.SetCompanyByURLMiddleware" to 'MIDDLEWARE_CLASSES'.
Add a couple of companies to the Company table, and you're ready to go.
Note that this doesn't work with the Django sites infrastructure, and
I wanted flatpages to work, so I hacked the default django.contrib.flatpages
module a bit to make things index off of 'company' instead of Sites.
--
Mike Cuddy ([EMAIL PROTECTED]), Programmer, Baritone, Daddy, Human.
Fen's Ende Software, Redwood City, CA, USA, Earth, Sol System, Milky Way.
"The problem with defending the purity of the English language is
that English is about as pure as a cribhouse whore. We don't just
borrow words; on occasion, English has pursued other languages down
alleyways to beat them unconscious and rifle their pockets for new
vocabulary." -- James D. Nicoll
Join CAUCE: The Coalition Against Unsolicited Commercial E-mail.
<http://www.cauce.org/>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Django users" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---