【问题标题】:Django-Social-Auth ErrorsDjango-Social-Auth 错误
【发布时间】:2012-07-15 02:40:02
【问题描述】:

更新 我尝试用以下内容替换自定义管理器中的所有内容:

def create_user(self, username, email):
        return self.model._default_manager.create(username=username)

这会引发错误。然后我尝试从我的自定义用户管理器返回用户,我得到“无法分配”:“UserSocialAuth.user”必须是“Barbuser”实例。从 associate_user 抛出。它来自 django.db.models.fields.related.py 的内部。基本上,我坚持知道如何从我的自定义模型经理中正确创建用户。我直接离开了导致我从 django 内置的 ModelManager 中复制所有内容的文档。帮助? 更新

我在配置 django-social-auth 时遇到问题。我已经在这里待了 3-4 天,我正准备认输。我安装了一个可用的现有用户注册应用程序,然后我安装并遵循了 django-social-auth github 站点上的文档。我将以下内容添加到我的 settings.py

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'polls',
    'barbuser',
    'social_auth',
)

AUTHENTICATION_BACKENDS = (
    'social_auth.backends.facebook.FacebookBackend',
    'django.contrib.auth.backends.ModelBackend',
)

FACEBOOK_APP_ID              = os.environ.get('FACEBOOK_APP_ID')
FACEBOOK_API_SECRET          = os.environ.get('FACEBOOK_SECRET')

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'social_auth.context_processors.social_auth_by_type_backends',
)

#SOCIAL_AUTH_ENABLED_BACKENDS = ('facebook',)
SOCIAL_AUTH_DEFAULT_USERNAME = 'new_social_auth_user'

LOGIN_URL          = '/login/'
LOGIN_REDIRECT_URL = '/profile/'
LOGIN_ERROR_URL    = '/login-error/'

SOCIAL_AUTH_USER_MODEL = 'barbuser.Barbuser'

我的 models.py 看起来像:

from datetime import date
from django.db import models
from django.contrib.auth.models import User
from django.db.models import BooleanField
from django.db.models.fields import DateTimeField
from django.utils import timezone
from django.utils.crypto import get_random_string

class BarbuserManager(models.Manager):
    @classmethod
    def normalize_email(cls, email):
        """
        Normalize the address by converting the domain part of the email address to lowercase.
        """
        email = email or ''
        try:
            email_name, domain_part = email.strip().rsplit('@', 1)
        except ValueError:
            pass
        else:
            email = '@'.join([email_name, domain_part.lower()])
            return email

    def create_user(self, username, email=None, password=None):
        """
        Creates and saves a User with the given username, email and password.
        """
        email = 'you@email.com' if email.strip() == '' else email
        now = timezone.now()
        if not username:
            raise ValueError('The given username must be set')
        email = BarbuserManager.normalize_email(email)
        user = User(username=username, email=email,
            is_staff=False, is_active=True, is_superuser=False,
            last_login=now, date_joined=now)
        user.set_password(password)
        user.save()
        barbuser = Barbuser(user=user, birthday=date.today(), last_login=user.last_login, name=username)
        barbuser.save()
        return barbuser

    def create_superuser(self, username, email, password):
        u = self.create_user(username, email, password)
        u.is_staff = True
        u.is_active = True
        u.is_superuser = True
        u.save(using=self._db)
        return u
    def make_random_password(self, length=10,
                             allowed_chars='abcdefghjkmnpqrstuvwxyz'
                                           'ABCDEFGHJKLMNPQRSTUVWXYZ'
                                           '23456789'):
        """        Generates a random password with the given length and given        allowed_chars. Note that the default value of allowed_chars does not        have "I" or "O" or letters and digits that look similar -- just to        avoid confusion.        """
        return get_random_string(length, allowed_chars)

    def get_by_natural_key(self, username):
        return self.get(username=username)

class Barbuser(models.Model):
    user = models.OneToOneField(User)
    username = models.CharField(max_length=200)
    last_login = DateTimeField(blank=True)
    is_active  = BooleanField(default=True)
    birthday = models.DateField()
    name = models.CharField(max_length=200)
    objects = BarbuserManager()

    def __init__(self, *args, **kwargs):
        me = super(Barbuser, self).__init__(*args, **kwargs)
        barbuser = me
        return me


    def __unicode__(self):
        return self.name

    def is_authenticated(self):
        return self.user.is_authenticated()

我已更新我的 urls.py 以包含“social_auth.urls”,并且在身份验证后,用户会从我的 views.py 重定向到 ViewProfile 视图:

# Create your views here.
from barbuser.forms import RegistrationForm, LoginForm
from barbuser.models import Barbuser
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template.context import RequestContext

def create_Barbuser(form):
    user = User.objects.create_user(form.cleaned_data['username'], form.cleaned_data['email'], form.cleaned_data['password'])
    user.save()
    barbuser = Barbuser(user=user, name=form.cleaned_data['name'], birthday=form.cleaned_data['birthday'])
    barbuser.save()


def process_form(form, request_context):
    if form.is_valid():
        create_Barbuser(form)
        return HttpResponseRedirect('/profile/')
    else:
        return render_to_response('register.html', {'form': form}, context_instance=request_context)


def render_blank_registration_form(request):
    '''When the user is not submitting the form, show them the blank registration form.'''
    form = RegistrationForm()
    context = {'form': form}
    return render_to_response('register.html', context, context_instance=RequestContext(request))


def BarbuserRegistration(request):
    """
    Handles the registration of new Barbwire users.
    """
    if request.user.is_authenticated():
        return HttpResponseRedirect('/profile/')
    if request.method == "POST":
        return process_form(RegistrationForm(request.POST), RequestContext(request))
    else:
        return render_blank_registration_form(request)

def LoginRequest(request):
    '''
    Handles Login requests.
    '''
    if request.user.is_authenticated():
        return HttpResponseRedirect('/profile/')
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            barbuser = authenticate(username=username, password=password)
            if barbuser is not None:
                login(request, barbuser)
                return HttpResponseRedirect('/profile/')
            else:
                return render_to_response('login.html', {'form' : form}, context_instance=RequestContext(request))
        else:
            return render_to_response('login.html', {'form' : form}, context_instance=RequestContext(request))
    else:
        form = LoginForm()
        return render_to_response('login.html', {'form' : form}, context_instance=RequestContext(request))

def LoginError(request):
    return render_to_response('login.html', {'form' : LoginForm()}, context_instance=RequestContext(request))

def LogoutRequest(request):
    logout(request)
    return HttpResponseRedirect('/')

def ViewProfile(request):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/login/')
    else:
        return render_to_response('profile.html',{'barbuser' : request.user.barbuser }, context_instance=RequestContext(request))

我的问题是 2 倍。当我在我的 models.py 中添加这些额外的东西时:

def facebook_extra_values(sender, user, response, details, **kwargs):
    return False

from social_auth.signals import pre_update
from social_auth.backends.facebook import FacebookBackend

pre_update.connect(facebook_extra_values, sender=FacebookBackend)

服务器启动时出现错误:assert isinstance(to, basestring), "%s(%r) is invalid. ForeignKey 的第一个参数必须是模型、模型名称或字符串 %r" % ( self.class._name_, to, RECURSIVE_RELATIONSHIP_CONSTANT) AssertionError: ForeignKey(None) 无效。 ForeignKey 的第一个参数必须是模型、模型名称或字符串 'self'

当我删除它时,我可以通过 facebook 流程登录,但我得到:AttributeError at /complete/facebook/ “NoneType”对象没有“extra_data”属性

我不确定我错过了什么或哪里出错了。有人可以帮助解释我哪里出错了吗?

更新 我已经在调试中跟踪了这个问题,显然当它尝试“UserSocialAuth.objects.create”时,我在 associate_user 函数中的 social_auth.backends.pipeline.social.py 中得到了一个 IntegrityError。然后它回退到调用 social_auth_user() 的 except 块中,并且此函数为 social_user 返回 None。我得到的 IntegrityError 是:

insert or update on table "social_auth_usersocialauth" violates foreign key constraint "social_auth_usersocialauth_user_id_fkey"
DETAIL:  Key (user_id)=(17) is not present in table "auth_user".

我不够熟悉,不知道如何、在哪里或为什么将 social_user 与在我的 models.py 中的 CustomUserManager 中创建的用户相关联。此外,我还删除了 models.py 底部的 facebook_extra_values、额外导入和 preupdate.connect 内容,因为我真的不明白它的作用或用途。我只是从示例应用程序中复制东西,试图解决我的第一个问题,即缺少关联。帮助? 更新

【问题讨论】:

  • UserSocialAuth.objects.create() 调用失败时,您能否包括IntegrityError 和至少一些来自social_auth.backends.pipeline.social.py 的回溯?只需将 raise 放在 except 块的第一行即可重新引发相同的异常(并且不要运行 social_auth_user())。
  • 好的,我重新提出错误,我得到:在表“social_auth_usersocialauth”上插入或更新违反外键约束“social_auth_usersocialauth_user_id_fkey”详细信息:密钥(user_id)=(17)不存在表“auth_user”。
  • 好吧 再深入一点,问题似乎与我想提出的另一个问题有关,但一旦我弄清楚了,我觉得更适合单独发帖。它使用来自 barbuser 表的 user_id17 来查找 auth_user。我想我应该从我的模型经理返回用户而不是 Barbuser?

标签: django facebook django-socialauth


【解决方案1】:

第一个问题——ForeignKey 错误——是由循环导入引起的。解决方案很简单并且遵循惯例。在 models.py 末尾获取该信号注册代码块并将其移动到名为 barbuser/signals.py 的新文件中。然后在barbuser/__init__.py 中输入这一行,from .signals import *

我还没有运行你的代码足够远得到你的第二个错误——“NoneType”对象没有属性“extra_data”——但我发现了其他几个问题。

从 settings.py 中删除 SOCIAL_AUTH_ENABLED_BACKENDS 设置。我不确定你是从哪里得到的,但它不在当前 social-auth 0.7.0 的文档中。它也不在 social_auth 代码中。可能是旧设置。

您在 settings.py (social_auth_login_redirect) 中引用了一个不存在的上下文处理器。同样,也许是一个旧功能。确保您运行的是最新的 social-auth(可通过 PyPi 使用 pip install django-social-auth 获得)。此外,仅使用您需要的上下文处理器,不要全部使用。 (上下文处理器将变量添加到模板上下文中。如果您没有在模板中使用social_auth 变量,那么您不需要任何社交身份验证上下文处理器。)这很重要,因为其中两个处理器与彼此。来自documentation

social_auth_backends 和 social_auth_by_type_backends 不太好 在一起。

确保您已运行 ./manage.py syncdb 来为 social-auth 模型设置数据库。如果模型没有数据库表,则会导致“UserSocialAuth.objects.create()”中断。

最后想一想,Django 不喜欢您定义自己的用户模型所做的事情。最好不要管 auth.User 并创建一个 UserProfile

更新

检查你的数据库结构;我怀疑索引不正确。更好的是,只需再次删除三个social_auth_* 表和syncdb。 UserSocialAuth 表对用户模型有一个 ForeignKey 约束。它应该将“user_id”映射到“Barbuser.id”,但如果在设置settings.py/SOCIAL_AUTH_USER_MODEL 值之前创建了表,那么该表将永远损坏,因为在运行初始 SQL 时,SocialAuth 默认为 Django 的 Auth.User。有道理?无论如何,现在您已经配置了社交身份验证,只需重新创建表即可。

注意:您最近使用self.model._default_manager.create() 的实验没有取得任何成果。 “self”是一个 BarbuserManager; “模型”是 Barbuser; “_default_manager”又回到了 BarbuserManager。为什么? “Barbuser.objects”模型的默认管理器。

哦,你第一次就正确了。 BarbuserManager.create_user() 需要返回一个 Barbuser。

【讨论】:

  • 非常感谢您的回复!刚进去就赶上这里。我删除了 SOCIAL_AUTH_ENABLED_BACKENDS,我相信我是从视频教程中复制的。我已经运行了 syncdb 来创建 social_auth 表,但为了预防起见,我只是再次运行它。我现在从我的models.py 中删除了额外的东西b/c 我似乎更接近于没有它的工作,我不知道它的用途。我只是从示例中复制了它,以为我遗漏了一些东西。我再试一次,看看会发生什么。
  • 我已经用我当前的代码更新了我的原始帖子。我仍然没有将任何内容写入任何 social_auth 表。当我进行初始注册时,它会通过我的自定义模型管理器运行并按预期创建用户和 barbuser,但是当它尝试进行关联时会出现问题。好像我仍然缺少 settings.py 中的某些内容,或者我的模型中需要发生一些魔法,但我不明白是什么或为什么。
  • 成功了!感谢一百万!你是对的,我的索引设置不正确。我通过 sqlclear/synchdb 删除并重新创建了我的 social_auth 表,但情况却大不相同。我知道我的一些实验是没有意义的,但我实际上是在抓住稻草,试图尽可能接近地匹配这个例子,而不知道它有多少是有效的。现在,我将在一个单独的线程中发布我最近的误解,因为通过所有更新来关注这个变得越来越困难。我担心为什么 request.user 曾经在 social_auth 之前返回一个 User 对象,但现在它没有。
  • 很高兴它成功了!同意,下一个问题在另一个线程中解决。
  • 是的,我的另一个问题是理解为什么我在获得用户之前通过 social_auth 登录 Facebook 时在我的 request.user 字段中获得了 Barbuser。在解决应用您的修复后出现的其他一些小问题后,我会在稍后发布。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
相关资源
最近更新 更多