Проблема
В django проектах для авторизації через соціалки використовую python-social-auth. Виникла ситуація, що не у всіх в профілі у facebook є email. Вірніше, я до цього не відразу дійшов. На пошту сипались листи про 500 помилку при реєстрації через fb: email field can not be null
(ну чи щось таке). Я думав, це або бага в коді в мене, або неправильно запитую дані у фб, от він і не повертає пошти. Випадково дізнався, що можлива ситуація, коли email просто не вказаний – от вам і причина.
Рішення
Я ще до цього користувався django-allauth
, але він якось не зайшов через те, що ключі та ідентифікатори зберігались в базі, тож при кожному розгорненні проекту треба було заповнювати ці поля. Але! Там така ситуація, коли соціалка не повернула пошти вже була передбачена: просто показувати юзеру форму, щоб він ввів дані і далі продовжувати вже по накатаній схемі. Хоча в PSA все ж зовсім по іншому, але загальна картина була зрозуміла.
Код
В PSA є pipeline
!
Виявляється, там можна не тільки тягнути аватар з соціалки, а й цілком реалізувати наш кейс. Далі: код.
pipeline.py
@partial
def require_email(strategy, details, user=None, is_new=False, *args, **kwargs):
if user and user.email:
return
elif is_new and not details.get('email'):
email = strategy.request_data().get('email')
if email:
details['email'] = email
else:
current_partial = kwargs.get('current_partial')
return strategy.redirect(
'{0}?partial_token={1}'.format(
reverse('app:require_email'),
current_partial.token
))
Є такий декоратор: partial
. Він створює об'єкт з даними та токен до нього, за який в майбутньому можна доступитись. Тобто основний момент: десь зберегти вже поточні дані, перервати виконання pipelines
, користувача перекинути на сторінку з формою, де він залишає свій email. Юзер заповнює форму і все відсилаємо назад на ту саму url
: /social/facebook
(замість social
у вас може бути щось інше). А вже python-social-auth сам розпізнає токен і забере потрібні йому дані.
Тож все, що нам тут треба: взяти токен і зробити редірект на сторінку де буде форма з полем для e-mail.
Далі view
:
def require_email(request):
strategy = load_strategy()
partial_token = request.GET.get('partial_token')
partial = strategy.partial_load(partial_token)
return render( request, 'require_email.html', {
'email_required': True, 'partial_backend_name': partial.backend, 'partial_token': partial_token
}
)
За токеном беремо дані, зокрема там є бекенд. Це все передаємо в шаблон:
template.html
<form action="{% url "social:complete" backend=partial_backend_name %}" method="POST">{% csrf_token %}
<input name="partial_token" type="hidden" value="{{ partial_token }}">
<input autofocus="" id="email" name="email" type="email" value="">
<button type="submit">Register</button>
</form>
Ну і все що нам треба: поле для e-mail. В hidden
передаємо токен, і це все посилаємо на подальше опрацювання social-auth
, саме тут потрібен був бекенд, щоб відіслати за потрібною адресою:
action="{% url "social:complete" backend=partial_backend_name %}"
Ось і все! Профіт!
Робочий django додаток з прикладом є на github https://github.com/dima-kov/psa-email-required
Висновки
Все робиться доволі легко!
Додумався не сам! Дякую великодушним користувачам stackowerflow, виявляється в python-social-auth
є репо з прикладами, тільки там трохи заплутано. Тому й вирішив оформити в репозиторій, щоб в майбутньому мати під рукою.
І на codeguida написав, можливо, комусь життя полегшиться)
Keep calm and continue to code hard!
Ще немає коментарів