djangodjango-modelsdjango-socialauthdjango-facebook

Django social_auth custom user model FacebookBackend explanation


I'm trying to use social_auth (omab) for the first time and I'm find that there is no working example how to store basic facebook user data. Authentication works and user is created without problem as it's explained in the social_auth docs but I need to store gender and locale also. Both of them belongs to the basic facebook user data so they are in the facebook response all the time.

I'm use Django 1.4, Python2.7 and latest social_auth. So I was try to use SOCIAL_AUTH_USER_MODEL = 'myapp.UserProfile' in settings.py file and model.py is:

#!/usr/bin/python
#-*- coding: UTF-8 -*-
from django.db import models
from django.contrib.auth.models import User
from django.db.models import signals
import datetime
from datetime import timedelta
from django.db.models.signals import post_save
from social_auth.signals import pre_update
from social_auth.backends.facebook import FacebookBackend


class CustomUserManager(models.Manager):
    def create_user(self, username, email):
        return self.model._default_manager.create(username=username)

class UserProfile(models.Model):
    gender = models.CharField(max_length=150, blank=True)
    locale = models.CharField(max_length=150, blank=True)
    #social_auth requirements
    username   = models.CharField(max_length=150)
    last_login = models.DateTimeField(blank=True)
    is_active  = models.BooleanField()

    objects = CustomUserManager()

    class Meta:
        verbose_name_plural = 'Profiles'

    def __unicode__(self):
        return self.username

    def get_absolute_url(self):
        return '/profiles/%s/' % self.id

    def facebook_extra_values(sender, user,response, details, **kwargs):     
        profile = user.get_profile()
        current_user = user 
        profile, new = UserProfile.objects.get_or_create(user=current_user)

        profile.gender = response.get('gender')
        profile.locale = response.get('locale')
        profile.save()
        return True

    pre_update.connect(facebook_extra_values, sender=FacebookBackend, weak = False, dispatch_uid = 'facebook_extra_values_user')

In the settings.py I'm define pipeline

SOCIAL_AUTH_PIPELINE = (
    'social_auth.backends.pipeline.social.social_auth_user',
    #'social_auth.backends.pipeline.associate.associate_by_email',
    'social_auth.backends.pipeline.user.create_user',
    'social_auth.backends.pipeline.social.associate_user',
    'social_auth.backends.pipeline.social.load_extra_data',
    'social_auth.backends.pipeline.user.update_user_details',
    'social_auth.backends.pipeline.misc.save_status_to_session',
)

but with above I get error AssertionError: ForeignKey(None) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string 'self'

Also I was try to use AUTH_PROFILE_MODULE = 'myapp.UserProfile' instead as I was do before to extend user.model, which works well but don't understand how to populate needed data when UserProfile is created. Does anyone can place working code for this problem?

Thanks


Solution

  • There are a couple of ways to archive it, what fits better to your project is up to you of course, here's a list of available options:

    1. Define this setting FACEBOOK_EXTRA_DATA = ('gender', 'locale'), the values will be available at the UserSocialAuth instance, to get them just do user.social_auth.get(provider='facebook').extra_data['gender'] or ['locale']. This is possible just because the information is available in the basic user data response.

    2. Use a user profile to store this data (check django doc about it). Then just add a pipeline entry that stores the values in your profile instance.

    3. Create a custom user model, SOCIAL_AUTH_USER_MODEL = 'myapp.CustomUser', and again add a custom pipeline entry that stores the values in your user instance.

    Number 1 is not the best solution IMO, since a user can have several Facebook accounts connected and it could create a mess. Number 2 is good for Django 1.4 and lower, but it's deprecated starting from Django 1.5, something to take into account. Number 3 is a bit messy IMO.