Django OAuthについて（自分用メモ）

### OAuthとは
GoogleやTwitterなどを使って認証を行う仕組み

### 導入方法
pip install django-allauthを実行する
[公式ドキュメント1](https://django-allauth.readthedocs.io/en/latest/)
[公式ドキュメント2](https://django-allauth.readthedocs.io/en/latest/configuration.html)
[公式ドキュメント3](https://django-allauth.readthedocs.io/en/latest/providers.html)→ここにLINEなどいろいろな設定方法が乗っている
settings.pyのINSTALLED_APPに以下を追加する
```python:settings.py
INSTALLED_APP={
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google' #googleの認証
}

#ログイン処理時に認証で行うクラスにallauthを追加する
AUTHENTICATION_BACKENDS=(
    'django.contrib.auth.backends.ModelBackend',#デフォルトの認証
    'allauth.account.auth_backends.AuthenticationBackend' #allauthの認証

SITE_ID=1 #django_siteテーブル上のどのサイトを認証に用いるか（基本は１）
ACCOUNT_EMAIL_REQUIRED=True #認証にメールアドレスが必要か（デフォルトはFalse）
ACCOUNT_UNIQUE_EMAIL=True #メールアドレスがユニークかどうか
ACCOUNT_USERNAME_REQUIRED=False #認証にユーザ名が必要か（デフォルトはTrue）
ACCOUNT_AUTHENTICATION_METHOD='email' #何を認証に利用するか(email,username,username_emailから選択）
SOCIALACCOUNT_PROVIDERS #プロバイダーごとの設定を記述する
SOCIALACCOUNT_PROVIDERS={
    'google':{
        'SCOPE':[ #Google APIで何を取得するか
            'profile',
            'email',
        ],
        'AUTH_PARAMS':{
            'access_type':'online', # offlineでのアクセスをする場合はofflineni設定する(offlineとは、ネットにつながっていないことではなく、ユーザがいない状態（バッチなど）のこと)
    }}}
```

### Google Cloud Platformにプロジェクトを登録する
プロジェクトを登録してシークレットキーを発行して、それを用いてOAuthを利用する
[Google Cloud Platform](https://console.cloud.google.com/?h1=JA)
[設定方法はこちらの動画で](https://www.udemy.com/course/python-django-web/learn/lecture/25225420#reviews)

### 手順

pip install django-allauthをいれた仮想環境下でマイグレートする
管理画面にいくとSOCIALACCOUNTSとSITESというテーブルが追加されている

1.SITESのSitesにいきexample.comのDomain nameをexample.comから127.0.0.1に変更する
2.Social applicationsにProviderをgoogleに、NameをOauth Appなどに、Client idとSecret keyに先ほどのGoogleCloudPlatformで入手した文字列をいれる、Sitesに127.0.0.1を追加して保存

Social accountsの欄に実際にGoogleを使ってログインしたユーザの情報などが入ってくる

### OAuthへの画面遷移

urls.pyに追加する
```python:urls.py
path('oauth_accounts/',include('allauth.urls')) #サインアップなんかの画面も出てしまう

#またはピンポイントにgoogleのurlだけを入れたい場合
from allauth.socialaccount.providers.google.urls import urlpatterns as google_url
path('oauth_accounts/',include(google_url)),
```
[OAuthへの画面の遷移を行うリンクを作成する](https://django-allauth.readthedocs.io/en/latest/templates.html#social-account-tags)
```python:
{%load socialaccount %}
<a href="{% provider_login_url 'google' %}">グーグル認証する</a> #Googleへのリンクを貼り付ける
```

### バッチ処理

活用方法としては
コマンドでDjangoのDB情報をcsvに書くことができたりするので
定期的にコマンドを実行できる環境があれば更新されるDB情報を
定期的にファイルにとりだす。などといったことができる

アプリの直下にmanagementというフォルダを作成し、さらになかにcommandsというフォルダを作成する。
そのなかにsample.pyというコマンドようのファイルを用意する。
```python:sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    
    def handle(self,*args,**options):
        print('バッチ処理１')
```
ターミナルにて
```
python manage.py sample
```
を実行するとバッチ処理１と表示される

### 引数をとるバッチ処理

```python:sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    
    def add_arguments(self,parser):
        parser.add_argument('name') #第１引数
        parser.add_argument('age') #第２引数
        parser.add_argument('--birthday')
    
    def handle(self,*args,**options):
        name= options['name'] #options[]でadd_argumentで格納した値を取り出す
        age=options['age']
        birthday=options['birthday']
        print(f'name={name},age={age},birthday={birthday}')      

```
```python：ターミナルで実行
python manage.py sample Taro 20 --birthday 2000-01-01

#実行結果
name=Taro,age=20,birthday=2000-01-01
```

### バッチ処理（いろんなオプション）
```python:sample
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = 'ユーザ情報を表示するバッチです'
    #python manage.py help sampleで説明文の表示ができる
    
    def add_arguments(self,parser):
        parser.add_argument('name',type=str,help='名前') #typeを指定するとその型でないとエラーになる
        parser.add_argument('age',type=int) 
        parser.add_argument('--birthday',default='2020-01-01') #デフォルト値を設定（入力しない場合はこれがはいる）
        parser.add_argument('three_words',nargs=3) #nargs=3とすると３つの引数を求められ、リスト型で格納される
        parser.add_argument('--active',action='store_true')
        #store_trueとすると引数が存在する時はTrueを返す。store_falseとすると引数が存在する時はFalseを返す
        parser.add_argument('--color',choices=['Blue','Red','Yellow']) #格納できる値を指定する。これ以外を格納しようとするとエラーになる
    
    def handle(self,*args,**options):
        # print('バッチ処理１')
        name= options['name']
        age=options['age']
        birthday=options['birthday']
        three_words=options['three_words']
        active=options['active']
        color=options['color']
        print(type(age))
        print(f'name={name},age={age},birthday={birthday},three_words={three_words}')
        print(active)
        if color=='Blue':
            print('青')
        elif color=='Red':
            print('赤')
        elif color =='Yellow':
            print('黄')
```
```
python manage.py sample Taro 12 a b c --active --color Blue

#実行結果
<class 'int'>
name=Taro,age=12,birthday=2020-01-01,three_words=['a', 'b', 'c']
True
青
```

### csvファイルにDB情報を書き出す処理
```python:export_orders.py
from django.core.management.base import BaseCommand
from stores.models import Orders
from ecsite_project.settings import BASE_DIR
from datetime import datetime
import os
import csv

class Command(BaseCommand):
    
    #useridで絞り込み
    def add_arguments(self,parser):
        parser.add_argument('--user_id',default='all')
    
    def handle(self,*args,**options):
        orders = Orders.objects
        user_id=options['user_id']
        if user_id=='all':
            orders=orders.all()
        else: #--user_idが入力された時はそれで絞り込む
            orders=orders.filter(user_id=user_id)
        file_path=os.path.join(BASE_DIR,'output','orders',f'orders_{datetime.now().strftime("%Y%m%d%H%M%S")}_{user_id}')
        with open(file_path, mode='w',newline='\n',encoding='utf-8') as csvfile:
            fieldnames=['id','user','address','total_price']
            writer=csv.DictWriter(csvfile,fieldnames=fieldnames)
            writer.writeheader()
            for order in orders:
                writer.writerow({
                    'id':order.id,
                    'user':order.user,
                    'address':order.address,
                    'total_price':order.total_price,
                })
```

実際にかきだされるcsvファイル
```csv
id,user,address,total_price
1,dev1@example.com,583-0861 東京都 西浦1576-13,135000
2,dev1@example.com,583-0861 東京都 西浦1576-13,135000
```