we are running multiple django sites (let's call them site1, site2, site3) against the same database, and we'd like to permit duplicated usernames accross them. Site and auth framework do not seem to achieve this, by default, username is a unique field in auth.User.
So what I've done so far (monkey patch, messing up with the user object...):
User._meta.get_field('username')._unique = False
User.add_to_class('site', models.ForeignKey(Site, default=Site.objects.get_current().id, blank=True, null=True))
User._meta.unique_together = (('username', 'site'),)
This piece removes the uniqueness of username, add a site field, make the couple (username, site) unique.
Then come problems which could occure when requesting a User.objects.get(username=xx) (e.g., authentication backends), if some users have the same username on different site. So, I decided to patch the User.objects manager:
def get_query_set(filter=True):
q = QuerySet(User.objects.model, using=User.objects._db)
if filter:
return q.filter(site = Site.objects.get_current())
return q
User.objects.get_query_set = get_query_set
Seems to work so far. But... the sites use pretty much the same objects, and it's all likely we change user field of these objects using the admin interface, which is common to all sites... hence, if I want to attribute an object (which has a foreignkey to auh.User) to a user of site2 while being logged in as admin on site1, that won't work, as the user manager will filter on site=site1.
I digged up a little, found that this seems to work:
class UserDefaultManager(UserManager):
def get_query_set(self, filter=None):
return QuerySet(User.objects.model)
User._default_manager = UserDefaultManager()
As far as I understand, _default_manager is used by the related objects manager. Then, User.objects.get(username=xx) filter on sites, and an_object.user won't.
Well, question is: yes, this is messy, and I'm pretty sure there will be flaws, but which are they ?
Next question is: if it's valid, then where is the best place to put this piece of code ? It's currently in a models.py file, just ran as the module is loaded...