1

So I have integrated django-allauth in my app, and now users have the ability to log in via instagram. Let's say I have a model called UserProfile and it has a field

user_avatar = models.ImageField(upload_to='profile_images', blank=True, default=None)

And with that I have a signal that creates a user profile as soon as a new user registers:

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

So usually when the user registers the user_avatar is blank since the default is set as None, now I want to add in the signal(if that's the correct way of doing it), to check if the user created his account via signing in using instagram, to go and fetch his profile picture and use it in the user_avatar. I think it's possible https://instagram.com/developer/endpoints/users/, but since I am a complete noob in python and django I don't know how to exactly do it.

So I found this signal from the django-allauth docs allauth.socialaccount.signals.pre_social_login(request, social_login) so this states that I can check that the user has signed up using a social account, but how would I use it with my create_user_profile function? The steps that I thought of is to first create the profile which I did and then to check whether the user signed up using a social account or not, if they did then the user_avatar which use their instagram profile picture and if not it would stay as none.

And as a plus I know that I can fetch the users social account profile picture in a template using {{user.socialaccount_set.all.0.get_avatar_url}}, but I don't want to do it via templates rather than doing it via Models which is the best way.

This might look really stupid but I gave it a go and tried to come up with something (this is what a newbie thinks would work, I thought this on top of my head, as I have no idea if this how signals work)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)
        def pre_social_login(request, social_login):
            user_logged_social = social_login.account.user
            if user_logged_social:
                UserProfile.objects.get(user_avatar=user_logged_social.profile_picture)
            else:
                pass
post_save.connect(create_user_profile, sender=User)

UPDATE Got it working with the help of @bellum! Thank you!

Here is the code that I used:

models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name="profile")
    user_avatar = models.ImageField(upload_to='profile_images'
                                blank=True,
                                default=None)


    def __unicode__(self):
        return self.user.username

    class Meta:
        verbose_name_plural = "User Profiles"

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

3utils.py

def download_file_from_url(url):
    # Stream the image from the url
    try:
        request = requests.get(url, stream=True)
    except requests.exceptions.RequestException as e:
        # TODO: log error here
        return None

    if request.status_code != requests.codes.ok:
        # TODO: log error here
        return None

    # Create a temporary file
    lf = tempfile.NamedTemporaryFile()

    # Read the streamed image in sections
    for block in request.iter_content(1024 * 8):

        # If no more file then stop
        if not block:
            break

        # Write image block to temporary file
        lf.write(block)

    return files.File(lf)

class SocialAccountAdapter(DefaultSocialAccountAdapter):
    def save_user(self, request, sociallogin, form=None):
        user = super(SocialAccountAdapter, self).save_user(request, sociallogin, form)

        url = sociallogin.account.get_avatar_url()
        avatar = download_file_from_url(url)
        if avatar:
            profile = user.profile  # access your profile from user by correct name
            profile.user_avatar.save('avatar%d.jpg' % user.pk, avatar)
        return user

settings.py

SOCIALACCOUNT_ADAPTER = 'main.s3utils.SocialAccountAdapter'

The signal for creating the profile on sign up in my models was left the same, just added an SocialAccountAdapter!

4

1 回答 1

3

我为Facebook提供者做了同样的任务。allauth提供了以另一种方式实现这一目标的可能性。我认为您不需要每次用户登录您的系统时都获取头像。如果是,那么您可以像这样覆盖类:

from allauth.socialaccount.adapter import DefaultSocialAccountAdapter

class SocialAccountAdapter(DefaultSocialAccountAdapter):
    def save_user(self, request, sociallogin, form=None):
        user = super(SocialAccountAdapter, self).save_user(request, sociallogin, form)

        url = sociallogin.account.get_avatar_url()

        avatar = download_file_from_url(url)  # here you should download file from provided url, the code is below
        if avatar:
            profile = user.user_profile  # access your profile from user by correct name
            profile.user_avatar.save('avatar%d.jpg' % user.pk, avatar)

        return user

您应该将此行添加到您的配置中SOCIALACCOUNT_ADAPTER = 'path-to-your-adapter.SocialAccountAdapter'

因此,此代码将仅在新的社交帐户注册过程中被调用,获取头像 url,下载并保存在您的User模型中。

import requests
import tempfile

from django.core import files

def download_file_from_url(url):
    # Stream the image from the url
    try:
        request = requests.get(url, stream=True)
    except requests.exceptions.RequestException as e:
        # TODO: log error here
        return None

    if request.status_code != requests.codes.ok:
        # TODO: log error here
        return None

    # Create a temporary file
    lf = tempfile.NamedTemporaryFile()

    # Read the streamed image in sections
    for block in request.iter_content(1024 * 8):

        # If no more file then stop
        if not block:
            break

        # Write image block to temporary file
        lf.write(block)

    return files.File(lf)
于 2015-06-26T23:21:28.690 回答