Django Social Auth: now with images
16 августа 2011
В рамках текущего проекта возникла задача при регистрации пользователей через социальную сеть кроме их контактных данных получать еще и изображение. Небольшое расследование показало, что каждая сеть практикует свой метод отдачи изображений, что в результате вылилось в следующий код, который вызывается в обработчике сигнала pre_update:
from social_auth.signals import pre_update @receiver(pre_update) def update_person_details(sender, **kwargs): person = kwargs.get('user') details = kwargs.get('details') … load_person_avatar(sender, person, kwargs.get('response')) … def load_person_avatar(sender, person, info): image_url = None if sender.name == 'vkontakte-oauth2': vk_response = info.get('response') if vk_response: image_url = vk_response.get('user_photo') # If photo is absent user_photo is absent too elif sender.name == 'odnoklassniki': image_url = info.get('pic_2') if 'stub' in image_url: # No real image image_url = None elif sender.name == 'mailru-oauth2': if info.get('has_pic'): image_url = info.get('pic_big') elif sender.name == 'twitter': image_url = info.get('profile_image_url') if not 'default_profile' in image_url: image_url = image_url.replace('_normal', '_bigger') else: # No real image image_url = None elif sender.name == 'yandex-oauth2': image_url = info.get('userpic') elif sender.name == 'facebook': image_url = 'http://graph.facebook.com/%s/picture?type=large' % info.get('id') if image_url: try: image_content = urlopen(image_url) # Facebook default image check if sender.name == 'facebook' and 'image/gif' in str(image_content.info()): return image_name = default_storage.get_available_name(person.avatar.field.upload_to + '/' + str(person.id) + '.' + image_content.headers.subtype) person.avatar.save(image_name, ContentFile(image_content.read())) person.save() except Exception: pass # Here we completely do not care about errors
Примечания:
- В качестве backend для Вконтакте был выбран OAuth2 вариант — он в отличие от OpenAPI предоставляет информацию об изображении
- В качестве библиотеки для поля изображения мы испольуем sorl.thumbnail
Ответы на: Django Social Auth: now with images
Willi пишет:
22 августа, 2011
Здорово спасибо больше это иммнно то что мне нужен. Я только не понял куда вставить надо
krvss пишет:
22 августа, 2011
Без разницы 🙂 У меня это в файле модели пользователя — там все остальные операции с пользователями лежат, так что место подходящее.
maxmoriss пишет:
22 августа, 2011
Не сохраняются аватарки (вобще ничего не происходит), не могу понять почему, возможно проблема в этих строчках:
image_name = str(person.id) + '.' + image_content.headers.subtype
person.avatar.save(image_name, ContentFile(image_content.read()))
person.save()
Код полнстью тут: https://gist.github.com/1163408
krvss пишет:
23 августа, 2011
max,
1. Как объявлено поле avatar в модели?
2. Что говорит трассировка — какие значения принимают image_url, image_name и image_content?
maxmoriss пишет:
24 августа, 2011
Поле avatar объявлено так:
avatar = models.FileField(upload_to=’avatars’, blank=True, null=True, default=»avatars/no_avatar.png», verbose_name=u»Аватар»)
Сори за дурацкий вопрос, как протрассировать image_content ?
Я попробовал заполнить другое поле, и ничего, такое чувство что сигнал не принимается…
krvss пишет:
24 августа, 2011
Трассировать как обычно — через pdb:
import pdb; pdb.set_trace() в том месте кода, которое хочется посмотреть. Лучше всего поставить перед image_content = urlopen(image_url). Как код остановится — p image_url
max, если ты прочитал то, что я написал и не понял — значит тебе срочно нужно читать про трассировку кода — это must 🙂 Не знать это на первых порах — нормально, потом — большой грех.
max пишет:
29 августа, 2011
Сигнала нет!
Пробовал socialauth_registered, все отлично перехватывается.
Можно ли данный функционал перенести в другой сигнал (я смотрю там параметры разные…)
krvss пишет:
29 августа, 2011
Очень странно — оба этих сигнала вызываются из одной функции, более того — сначала вызывается pre_update, в social_auth.backends.__init__.py, метод update_user_details:
signal_response = lambda (receiver, response): response
kwargs = {'sender': self.__class__, 'user': user,
'response': response, 'details': details}
changed |= any(filter(signal_response, pre_update.send(**kwargs)))
# Fire socialauth_registered signal on new user registration
if is_new:
changed |= any(filter(signal_response,
socialauth_registered.send(**kwargs)))
В твоем коде https://gist.github.com/1163408 — update_person_details не вызывается?
Переносить куда-то не советую, советую разобраться 🙂 Потому что больше нареканий не было ни у кого, а более правильного места нет.
Willi пишет:
23 августа, 2011
Привет Стас ещё раз. Спасибо за ответ и проделанню работу. Попробовал вставить на разные место но у меня всё таки не получилось вставить этот скрипт. Я просто получаю 500 Internal Server Error Я ещё новичок в Django но socialauth я успешно установил. Это строка
load_person_avatar(sender, person, kwargs.get(‘response’))
надо где-то в Backend вставить? У меня стандартный модел ползователя от Django (Django 1.2) и профил. Может мне надо писать user.save не person.save?
или тут неправильно выбрал куда картика вставляю (Я sorl.thumbnail утсановил)
image_name = default_storage.get_available_name(person.avatar.field.upload_to + ‘/’ + str(person.id) + ‘.’ + image_content.headers.subtype)
person.avatar.save(image_name, ContentFile(image_content.read()))
Подскажешь что-нубудь или вставишь пример в Коммите?
krvss пишет:
23 августа, 2011
Willi,
Вставлять в этот код ничего не надо — он лучший пример, так как взят из живого проекта, где постоянно работает 🙂
Я не совсем понял один момент — если у тебя стандартный профиль, то где же объявлено поле avatar?
Тебе нужно
1. Завести свою модель для пользователя — как это сделать написано, например, здесь: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
2. Указать эту новую модель в настройках django-social-auth: CUSTOM_USER_MODEL = модель
3. Для поля аватар нужно либо использовать встроенный тип ImageField, либо ImageField из sorl.thumbnail.
Вот пример объявления поля:
class Person(User):
avatar = ImageField(upload_to='upload/avatar/%Y/%m/%d', verbose_name=u'Моя фотография', blank=True, null=True)
Willi пишет:
29 августа, 2011
Спасибо за подсказку теперь всё заработало. Декоратор @receiver не работал в Django 1.2 скопровал туда папку dispatch от Django 1.3 и всё зароботало (Мне просто надо 1.2) Тоже сначало на сообразил что надо быловставить этот from django.core.files.base import ContentFile и from django.dispatch import receiver
Спасибо Стас очень помог и тоже чему-то новому научился.
krvss пишет:
29 августа, 2011
Круто, чо 🙂
max пишет:
30 августа, 2011
Победил я эту проблему! Сел, повнимательнее присмотрелся и заметил что urlopen у меня как-то криво импортируется, собственно в этом и было дело.
Сейчас c Facebook и Одноклассников все прекрасно загружается, а вот из Вконтакте почему-то не хочет… У тебя работает? Может они API поменяли или еще чего…
Александр пишет:
21 ноября, 2011
Привет, спасибо за код
Создал модель, как описано выше
Сделал CUSTOM_USER_MODEL= ‘app.CustomUser’ в settings.py
Добавил бекенд для юзера
Насколько я понимаю штатный пользователь теперь должен автоматически подменяться кастомным
class CustomUser(User):
avatar = ImageField(upload_to=’upload/avatar/%Y/%m/%d’, verbose_name=u’Моя фотография’, blank=True, null=True)
objects = UserManager()
, в котором уже есть поле avatar: person = kwargs.get(‘user’). Но нет его, может что-то неправильно делаю?
krvss пишет:
21 ноября, 2011
А где его нет? 🙂
Когда ты добавляешь кастомную модель, в результате в БД будут две таблицы, одна из них джанговская стандартная, а вторая — твой CustomUser (кстати syncdb не забыл сделать?)
Александр пишет:
21 ноября, 2011
Syncdb сделал, в результате появилась вторая таблица, в которой уже есть поле avatar, однако при создании пользователя заполняется старая таблица, новая остается пустой
krvss пишет:
21 ноября, 2011
Если ты имеешь в виду создание пользователя при авторизации через DSA, то нужно еще сделать настройку SOCIAL_AUTH_USER_MODEL = ‘myapp.CustomUser’
Александр пишет:
21 ноября, 2011
ага, сделал. Написал также бекенд:
class CustomUserModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = self.user_class.objects.get(username=username)
if user.check_password(password):
return user
except self.user_class.DoesNotExist:
return None
def get_user(self, user_id):
try:
return self.user_class.objects.get(pk=user_id)
except self.user_class.DoesNotExist:
return None
@property
def user_class(self):
if not hasattr(self, ‘_user_class’):
self._user_class = get_model(*settings.CUSTOM_USER_MODEL.split(‘.’, 2))
if not self._user_class:
raise ImproperlyConfigured(‘Could not get custom user model’)
return self._user_class
Появляется ошибка:
AssertionError: ForeignKey(None) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string ‘self’
Создание пользователя что ли нужно описывать в бекенде?
krvss пишет:
21 ноября, 2011
backend’a я не делаю. Кстати, загляни в пример, там по-моему кастомная модель используется тоже и все ок.
Александр пишет:
22 ноября, 2011
В примере ведь не написано SOCIAL_AUTH_USER_MODEL = ‘app.CustomUser’
А когда его пишешь в сетинг, сразу валится ошибка:
class UserSocialAuth(models.Model):
File «/home/alex/Envs/coposition/local/lib/python2.7/site-packages/social_auth/models.py», line 27, in UserSocialAuth
user = models.ForeignKey(User, related_name=’social_auth’)
File «/home/alex/Envs/coposition/local/lib/python2.7/site-packages/django/db/models/fields/related.py», line 822, in __init__
assert isinstance(to, basestring), «%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r» % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
AssertionError: ForeignKey(None) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string ‘self’
krvss пишет:
22 ноября, 2011
Магия 🙂 Я убрал комментарий с SOCIAL_AUTH_USER_MODEL = ‘app.CustomUser’ в примере, сделал syncdb на всякий случай — и все окей, работает без ошибок.
tenoclock пишет:
6 декабря, 2012
Друзья, простите, но я просто не понимаю откуда брать «info». Что-то протормаживается у меня очень. Расскажите, пожалуйста.
krvss пишет:
7 декабря, 2012
info определяется в функции def update_person_details(sender, **kwargs) — обработчике сигнала pre_update от Django Social Auth. Она там в коде выше. На всякий случай уточняю, что сама по себе функция load_person_avatar работать не будет 🙂
Nabu пишет:
25 февраля, 2014
Если кто забредет сюда:
следует добавить в settings.py
VK_EXTRA_DATA = [‘photo_big’]
И дальше работать уже с этим адресом
image_url = vk_response.get(‘photo_big’)
Т.к. размеры картинки отдаваемой вконтактом по умолчанию больше похожи на издевку…