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
-~----------~----~----~----~------~----~------~--~---

Reply via email to