I want to serve a Django application that serves multiple web sites by single database but different user sets. Think like a blog application, it will be used by several domains with different themes, but use same database by adding a site field to models.
I use Django's SitesFramework for that job. But the problem is, I couldn't separate user models for different sites. I want to use same user model with a site field and email field that unique per site.
I tried to extend AbstractUser
model like that:
from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManagerclass Member(AbstractUser):username = Nonesite = models.ForeignKey(Site)USERNAME_FIELD = 'email'REQUIRED_FIELDS = []on_site = CurrentSiteManager()class Meta:unique_together = ('site', 'email')
But gives that error: 'Member.email' must be unique because it is named as the 'USERNAME_FIELD'.
What is the best practice for that issue?
I hope this approach helps to you:
1) Compose username before save:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManagerclass Member(AbstractUser):site = models.ForeignKey(Site)on_site = CurrentSiteManager()USERNAME_FIELD = 'username'REQUIRED_FIELDS = []class Meta:unique_together = ('site', 'email')from django.db.models.signals import pre_save
from django.dispatch import receiver@receiver(pre_save, sender=Member)
def compose_username(sender, instance, **kwargs):instance.username = "{0}__{1}".format( instance.email, instance.site_id )
2) Then overwrite ModelBackend in your custom auth backend:
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_modelclass MyModelBackend(ModelBackend):def authenticate(self, username=None, password=None, **kwargs):UserModel = get_user_model()site = kwargs.get('site')identifier = "{0}__{1}".format( username, site )try:user = UserModel.objects.get(username=identifier)if user.check_password(password):return userexcept UserModel.DoesNotExist:# Run the default password hasher once to reduce the timing# difference between an existing and a non-existing user (#20760).UserModel().set_password(password)
3) Remember set your custom backend on settings:
AUTH_USER_MODEL='s1.Member'
SITE_ID = 1
AUTHENTICATION_BACKENDS = ( 'MyApp.MyModule.MyModelBackend',)
4) Include site when authenticate:
>>> from s1.models import Member as M
>>> m1 = M()
>>> m1.site_id = 1
>>> m1.email = '[email protected]'
>>> m1.save()
>>> m1.set_password('hi')
>>> m1.save()
>>>
>>> from django.contrib.auth import authenticate, login
>>> u=authenticate(username='[email protected]', password='hi', site=1)
>>> u
<Member: [email protected]_at_1>
>>>