DjangoでGoogleアカウントのアバターを扱えるようにした

以前python-social-authというライブラリを使ってGoogleアカウントでOAuth認証ができるようになりました。

https://hodalog.com/oauth-authentication-in-dja/

デフォルトのままでもいい感じなのですが、アバター画像も引っ張ってこれるようにしたいなぁと思い、実装方法を調べたのでまとめます。

実装方法

実装には、python-social-authのパイプライン機能を使います。

python-social-authのパイプライン機能

パイプラインというのは、一つのTCPコネクションで複数のHTTPリクエストをさばくことです。

python-social-authではパイプラインのメカニズムを利用することで、OAuth認証の間で独自に定義した関数などをねじ込むことができます。

実装手順

あらかじめpython-social-authをインストールし、OAuthが使えるようにしておきます。インストール方法と使い方はこちら↓

https://hodalog.com/oauth-authentication-in-dja/

以下、アバターを使うための設定です。

settings.pyの設定

settings.pyに下記を追加します。

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
    'your_application.users.pipeline.get_avatar', # <- pipeline.pyというファイルのget_avatarメソッドを指定
)

your_app.users.pipeline.get_avatar で、Djangoのアプリケーションにあるpipeline.pyというファイルのパスと、pipeline.puで定義したget_avatarという関数(メソッド)を指定しています。

ファイル名やメソッド名は自由に決めて大丈夫です。

pipeline.pyで独自関数の定義

今回は your_appusers ディレクトリの中の pipeline という指定の仕方をしているので、その場所にファイルをおきます。

your_app/
├── __init__.py
.....
├── users
│   ├── __pycache__
│   └── pipeline.py # <- こんな感じ
.....

pipeline.pyの中身は次のようにしました。

def get_avatar(backend, strategy, details, response,
        user=None, *args, **kwargs):
    url = None
    if backend.name == 'facebook':
        url = "http://graph.facebook.com/%!s(MISSING)/picture?type=large"%!r(MISSING)esponse['id']
    if backend.name == 'twitter':
        url = response.get('profile_image_url', '').replace('_normal','')
    if backend.name == 'google-oauth2':
        url = response['image'].get('url')
    if url:
        user.profile.avatar = url
        user.save()

実装の予定はありませんが、念のためtwitterとfacebookのアバターも拾えるようにしています。

user.profile.avatar = url でProfileモデルのavatarという属性にurlが記録されるようにしています。当然、Profileモデルが作られていることが前提になります。

ちなみに私は次のようなモデルを作成しています。

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    gender = models.CharField(max_length=20, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    location = models.CharField(max_length=30, blank=True)
    favorite_words = models.CharField(max_length=50, blank=True)
    avatar = models.URLField(max_length=200, blank=True)

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

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

これでOAuth認証によるユーザー作成に合わせ、アバターのURLが自動でDBに保存されます。

保存できれば、あとはテンプレートで出力するだけです。

...
{%!i(MISSING)f user.profile.avatar %!}(MISSING)
  <img src="{{ user.profile.avatar }}" alt="avatar" width="40" height="40" class="avatar">
{%!e(MISSING)lse %!}(MISSING)
  <span class="nav-link">Hello, {{ user.username }}</span>
{%!e(MISSING)ndif %!}(MISSING)
...

まとめ

python-social-authのパイプラインを利用して、アバター画像を引っ張ってこれるようになりました。やったね!