Django 1.7 multisite Custom Model

I want to use the Django application, which serves several websites in a single database, but with different sets of users. Think of it as a blog application, it will be used by several domains with different themes, but use the same database by adding a site field to the model.

I am using Django SitesFramework for this work. But the problem is that I could not share user models for different sites. I want to use the same user model with the site field and email field, which is unique to each site.

I tried to extend the AbstractUser model as follows:

 from django.contrib.auth.models import AbstractUser from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Member(AbstractUser): username = None site = models.ForeignKey(Site) USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] on_site = CurrentSiteManager() class Meta: unique_together = ('site', 'email') 

But it gives this error: 'Member.email' must be unique because it is named as the 'USERNAME_FIELD'.

What is the best practice for this problem?

+7
python django django-models django-authentication django-sites
source share
2 answers

I hope this approach helps you:

1) Write username before saving:

 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 CurrentSiteManager class 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 rewrite ModelBackend in your user archive:

 from django.contrib.auth.backends import ModelBackend from django.contrib.auth import get_user_model class 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 user except 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 that you are setting up your own backend in the settings:

 AUTH_USER_MODEL='s1.Member' SITE_ID = 1 AUTHENTICATION_BACKENDS = ( 'MyApp.MyModule.MyModelBackend',) 

4) Turn on the site during authentication:

 >>> from s1.models import Member as M >>> m1 = M() >>> m1.site_id = 1 >>> m1.email = ' peter@hello.com ' >>> m1.save() >>> m1.set_password('hi') >>> m1.save() >>> >>> from django.contrib.auth import authenticate, login >>> u=authenticate(username=' peter@hello.com ', password='hi', site=1) >>> u <Member: peter@hello.com _at_1> >>> 
+4
source share

Well, if you want to save the email as USERNAME_FIELD, which by definition in the User-model should always be unique, you will not be able to repeat it for each site.

There are several approaches that I can think of that will probably work, but I think I would do the following:

  • First of all, I would not extend the AbstractUser model and make OneToOne dependent on the Site. Since the user is actually allowed to belong to several sites. So, the best imo option is to create a Member Model with a ForeignKey field for the user and site, and those unique ones. Thus, there is only one member on the site, and the user remains unique. This is also what is better in reality.

  • Now that you are registering a new user for the site, first check if the user (email address) already exists, and if so, simply assign a new user to this user. If not, create a new user.

First edit the question, "what if a user wants to register on another site with a different username, password or email?"

If according to my comments, OK to share the user account for sites (and, of course, the user knows about it). During the registration process, if the user already exists for this letter, he may be informed that, since an account for this address already exists for site-a, this user account will be assigned to membership on site-b. Then you can send an email with a confirmed link, and upon confirmation, a new member will be created and assigned to a valid user.

Another approach

If I was mistaken, I would prefer and even want to share users between sites, then I assume that a completely new approach is needed:

Extend AbstractUser as you did, but instead of using email like USERNAME_FIELD, use a new field consisting of <email>_<site_id> (which would always be unique since these two fields are unique). This field may be called unique_site_id or so. And this field can be filled in after submitting the login and entry forms.

+3
source share

All Articles