7

Я хочу обслуживать приложение Django, которое обслуживает несколько веб-сайтов по одной базе данных, но с разными пользовательскими наборами. Подумайте, как приложение для блога, оно будет использоваться несколькими доменами с разными темами, но использовать ту же базу данных, добавив поле сайта в модели.Django 1.7 multisite User model

Я использую Django's SitesFramework для этой работы. Но проблема в том, что я не мог разделять модели пользователей для разных сайтов. Я хочу использовать ту же модель пользователя с полем сайта и полем электронной почты, которое уникально для каждого сайта.

Я попытался расширить AbstractUser модель так:

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') 

Но дает эту ошибку: 'Member.email' must be unique because it is named as the 'USERNAME_FIELD'.

Что является лучшей практики для этой проблемы?

ответ

4

Я надеюсь, что этот подход поможет вам:

1) Написать имя пользователя перед сохранением:

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) Затем перезаписать ModelBackend в пользовательском Иденте бэкэнде:

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) Помните, что вы настраиваете свой собственный бэкэнд при настройках:

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

4) Включить сайт при Аутентифицировать:

>>> 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> 
>>> 
+0

С несколькими исправлениями, которые сработали для меня. Вот мой последний код: https://gist.github.com/muratcorlu/1ad61a43d4836d5b1227 Спасибо! –

3

Ну, если вы хотите сохранить электронное письмо как USERNAME_FIELD, которое по определению в User-model должно быть всегда уникальным, вы не сможете повторить его для каждого сайта.

Есть более одного подхода, я могу думать о том, что вероятно, будет работать, но я предполагаю, что я хотел бы сделать следующее:

  • Прежде всего, я бы не расширить AbstractUser-модель и сделать a Зависимость OneToOne от Сайта. Поскольку пользователю фактически разрешено принадлежать нескольким сайтам. Итак, лучший вариант imo - создать модель-модель с полем ForeignKey to User и Site и сделать этими unique_together. Таким образом, на сайте есть только один член, а Пользователь остается уникальным. Это и то, что в реальности тоже лучше.

  • Теперь, при регистрации нового пользователя для сайта, сначала сделайте чек, если пользователь (адрес электронной почты) уже существует, и если да, просто назначьте нового пользователя этому пользователю. Если нет, создайте нового пользователя.

Первый Edit на вопрос, «что, если пользователь хочет, чтобы зарегистрироваться на другой сайт с другим именем пользователя, пароль или адрес электронной почты?»

Если в соответствии с моими комментариями, это нормально, чтобы использовать учетную запись пользователя для сайтов (и, конечно, пользователь знает об этом). В процессе регистрации в случае, если пользователь уже существует для данного адреса электронной почты, то он может быть проинформирован о том, что, поскольку учетная запись для этого адреса уже существует для сайта-a, эта учетная запись пользователя будет назначена членству на сайте-b. Затем можно отправить сообщение электронной почты с подтвержденной ссылкой, и при подтверждении новый член будет создан и назначен действительному пользователю.

Другой подход

Если бы я был не прав в предположении, что это нормально и даже желательно, чтобы разделить пользователей среди сайтов, то я думаю, здесь требуется совершенно новый подход:

Продлить AbstractUser, как вы были но вместо того, чтобы использовать электронную почту как USERNAME_FIELD, используйте новое поле, состоящее из <email>_<site_id> (которое всегда было бы уникальным, так как эти два поля уникальны), поле можно было бы назвать unique_site_id или около того. И это поле может быть заполнено после отправки форм входа и входа.

+0

Что делать, если посетитель подписывает с той же электронной почты и имя пользователя, но другой пароль? Или же адрес электронной почты, но другое имя пользователя? Или же имя пользователя, но другой адрес электронной почты? –

+0

Ну, если письмо правильное, это тот же пользователь, поэтому пользователю не нужно будет создавать новую учетную запись. ... Если личная информация пользователя не совпадает, пользователь может спросить, хочет ли он обновлять свои данные? ... или пользователь должен «думать», что он создает новую/отдельную учетную запись для каждого сайта? ... Я думал, что это может быть больше похоже на сайты stackoverflow/-exchange ... один логин/профиль для нескольких связанных сайтов. – andzep

+0

... Возможно, я неправильно понял суть обмена базами данных между сайтами. Я думал, что сайты каким-то образом связаны и будут делиться некоторыми данными между ними, включая пользователей. – andzep