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!
I have done the same task for Facebook
provider. allauth
gives possibility to achive this in another way. I think you don't need to get avatar every time user logins in your system. If yes then you can override class like this:
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
You should add this line to your config: SOCIALACCOUNT_ADAPTER = 'path-to-your-adapter.SocialAccountAdapter'
.
As result this code will be called only during new socialaccount registration process, fetch avatar url, download it and save in your User
model.
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)