12

How can I retrieve profile picture and date of birth from google and facebook using python-social-auth by extending pipeline? I've read that I can make functions to do so and set path to them but I don't know the attribute names that I must retrieve. Please help!

Keshav Agarwal
  • 811
  • 1
  • 10
  • 28

4 Answers4

14

To get avatars from social login, you need create a pipeline.py file in your app and add this lines to settings.py:

SOCIAL_AUTH_PIPELINE = (

    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details',
    'apps.users.pipeline.get_avatar', # This is the path of your pipeline.py
    #and get_avatar is the function.
)

and later add this content to your pipeline.py file

def get_avatar(backend, strategy, details, response,
        user=None, *args, **kwargs):
    url = None
    if backend.name == 'facebook':
        url = "http://graph.facebook.com/%s/picture?type=large"%response['id']
    if backend.name == 'twitter':
        url = response.get('profile_image_url', '').replace('_normal','')
    if backend.name == 'google-oauth2':
        url = response['image'].get('url')
        ext = url.split('.')[-1]
    if url:
        user.avatar = url
        user.save()
Roberth Solís
  • 1,520
  • 18
  • 16
6

Here's what I have used to save pictures for Facebook:

def save_profile_picture(backend, user, response, details,
                         is_new=False,*args,**kwargs):

    if backend.__class__.__name__ == 'FacebookOAuth2':
        up = UserProperties.objects.get_or_create(user=user) #RETURNS TUPLE (instance, created(boolean))
        if not up[0].photo:
            url = 'http://graph.facebook.com/{0}/picture'.format(response['id'])
            response = urllib.request.urlopen(url)
            io = BytesIO(response.read())
            up[0].photo.save('profile_pic_{}.jpg'.format(user.pk), File(io))
            up[0].save() 

Save this function into a file, for instance, pipelines.py, and then add the function to your SOCIAL_AUTH_PIPELINE in your settings.

SOCIAL_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.social_auth.associate_by_email', 
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details',
    'projects.pipeline.save_profile_picture', #save facebook profile image,
)

For Facebook you need to create your own Facebook app. You can only retrieve information and pictures from users who have given you the permission to do so. Same rules more or less apply for Google. Read their API docs for more detail.

Jaakko
  • 584
  • 6
  • 13
  • So what I understood is that I must find a URL on which Google+ serves its images and use that to retrieve profile picture and then override my pipeline. Did I get it right? – Keshav Agarwal Apr 11 '15 at 09:49
  • Yes. Read their docs for more details. You need to add your functions to the pipeline, in the correct place to have the desired behaviour. And to the one who down voted: I gave a good answer how to get started. You cannot expect people to write the whole program for other people, but give answers that take the OP to the right track, as was the case here. That kind of negativity is unfortunately rampant here. – Jaakko Apr 11 '15 at 10:10
  • I have another doubt, I've a custom model which inherits the user model from Django.contrib.author and then two more fields for DOB and profile picture. When I signed in with Google using OAuth2, the django.contrib.auth.user in the admin panel got its values but my custom model wasn't linked on a one to one basis with it. Any advice on how I might be able to do that? – Keshav Agarwal Apr 11 '15 at 11:37
  • 1
    It seems google now needs a api request `GET https://www.googleapis.com/plus/v1/people/userId`, which will return a json response containing the requested image. See this [answer](http://stackoverflow.com/a/22532031/1761793) – Ajoy Jun 26 '15 at 08:15
  • Be sure to use HTTPS and not HTTP when accessing `graph.facebook.com` – rgov May 29 '18 at 03:37
4

The above answers may not work (it did not work for me) as the facebook profile URL does not work anymore without accesstoken. The following answer worked for me.

def save_profile(backend, user, response, is_new=False, *args, **kwargs):
    if is_new and backend.name == "facebook":
        # The main part is how to get the profile picture URL and then do what you need to do
        Profile.objects.filter(owner=user).update(
            imageUrl='https://graph.facebook.com/{0}/picture/?type=large&access_token={1}'.format(response['id'],
                                                                                                  response[
                                                                                                      'access_token']))
    elif backend.name == 'google-oauth2':
        if is_new and response.get('picture'):
            Profile.objects.filter(owner=user).update(imageUrl=response['picture'])

add to the pipeline in setting.py,

SOCIAL_AUTH_PIPELINE+ = ('<full_path>.save_profile')
sadat
  • 4,004
  • 2
  • 29
  • 49
1

Just to extend sadat's answer which works perfectly for saving the url. If you are wanting to save the actual image from the url to a django imagefield would need to do the below:

import requests
from io import BytesIO
from django.core import files

def save_profile(backend, user, response, is_new=False, *args, **kwargs):
if is_new and backend.name == "facebook":
    
    picture_url='https://graph.facebook.com/{0}/picture/?type=large&access_token={1}'.format(response['id'],
                                                                                              response[
                                                                                                  'access_token']))   
    file_name = f"{uuid.uuid4()}.jpeg"
            resp = requests.get(picture_url)
            if resp.status_code == requests.codes.ok:
                fp = BytesIO()
                fp.write(resp.content)
  
                profile.image.save(file_name, files.File(fp))
                profile.save()
Nick
  • 51
  • 1
  • 3