1

This question is about saving Facebook Profile pictures in the Django model automatically, using https://github.com/PhilipGarnero/django-rest-framework-social-oauth2 library.

Edit: There are 2 ways to solve this question: Save the URL of the image in CharField() or Save the image itself using ImageField(). Both solutions will do.


The above library allows me to create and authenticate users using bearer tokens. I have the created the profile model:

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='userprofile')
    photo = models.FileField(blank=True) # OR
    ######################################
    url = 'facebook.com{user id}/picture/'
    photo = models.CharField(default=url)

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.userprofile.save()

Which automatically creates user profiles for each user. Now, I would like to add the following code to save the photo from Facebook. Facebook API requires user id to get this picture.

photo = 'https://facebook/{user-id}/picture/'
UserProfile.objects.create(user=instance, photo=photo)

The above is not working because

1) I can't figure out where to get the user id from.

2) The image can't be stored like that, I need to convert it to bytes or some other method.

Gal Silberman
  • 3,756
  • 4
  • 31
  • 58
GRS
  • 2,807
  • 4
  • 34
  • 72
  • that's not an answer but you should use ImageField instead of FileField for images. – Peter Sobhi Feb 19 '18 at 00:30
  • User id which facebook return as json and to save image use char field as alternate column for image, and store facebook's path or you can read image file and store in your hard drive. – Anup Yadav Feb 19 '18 at 06:56
  • Why do you want to store profile images in the first place? You can just use the URL that automatically redirects to the user’s _current_ profile picture ... You are supposed to keep such data up-to-date anyways, and even if you did this every time I logged in to your app ... if I did _not_ log in to your app for a longer period of time, you might be showing other users an outdated profile picture of me, because in the meantime I already changed it on Facebook. – CBroe Feb 19 '18 at 09:03
  • @CBroe Yes, this would actually be beneficial. How would I get the URL of that image? I looked at the source code of the plugin, and I don't see where the user is getting created. – GRS Feb 19 '18 at 09:56
  • You can just use the URL directly wherever you need to show this image (img src, background-image: url(...)), you don’t need to actually store it somewhere for this purpose, you only need the app-scoped user id. https://developers.facebook.com/docs/graph-api/reference/user/picture – CBroe Feb 19 '18 at 09:58
  • @CBroe Django-REST framework library will transform the facebook token to its own backend token and all further authentification requests will be handled by it. This way, we only need to call the facebook SDK once to convert the token. Hence, I'm not sure where the `user id` resides. I have access to our db user id, but this is obviously not the facebook user id. – GRS Feb 19 '18 at 10:02

2 Answers2

6

There is a VERY simple solution for that. Use the python-social-auth pipelines.

The way this thing work is just like middleware, you can add in your settings page to the SOCIAL_AUTH_PIPELINE section a function that will run every time the user is authenticated using the social_django.

An example:

In your settings page, add the following:

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',
    'home.pipeline.save_profile',
)

Look at home.pipeline.save_profile, this is a new pipeline in home.pipeline file. (Change it into your own user module folder)

In there (home.pipeline) add the following:

from .models import UserProfile

def save_profile(backend, user, response, *args, **kwargs):
    if backend.name == "facebook":
        UserProfile.objects.create(
            user=user, 
            photo_url=response['user']['picture']
        )

This is an example. You need to change it for get/update in case the user already logged in. Also, try and play with the response argument, there might be different data you can use there.

One last thing, make sure you add the picture attribute into your settings:

SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
  'fields': 'id, name, email, picture'
}

http://python-social-auth.readthedocs.io/en/latest/backends/facebook.html

https://godjango.com/122-custom-python-social-auth-pipeline/

https://github.com/python-social-auth/social-app-django

Gal Silberman
  • 3,756
  • 4
  • 31
  • 58
0

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

add to the pipeline in setting.py,

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