Firebase: Cloud Firestore と Cloud Storage をPythonから使う

# はじめに
- FirebaseのCloud FirestoreとCloud StorageについてPythonから使ったときの方法についてメモを残しておきます。
- CSVファイルを用意しておいてpythonからアップロードを行ったり、ダウンロードを行ったりなどの際にご利用ください。

# 目次
- [準備](#準備)
    - SDKの追加
    - 認証ファイルの用意
- [Cloud Firestoreを使う](#cloud-firestoreを使う)
    - ライブラリのimport
    - 認証情報の初期化
    - データを取得
    - データを追加
    - データを削除
- [Cloud Storageを使う](#cloud-storageを使う)
    - ライブラリのimport
    - 認証情報の初期化
    - データを取得
    - データを追加
    - データを削除
- まとめ

# 準備
- まず、SDKと認証ファイルを用意します。

### SDKの追加
- 下記のコマンドを実行して、SDKを追加します。

```bash:
pip install firebase-admin # firebase firestore
pip install --upgrade google-cloud-storage # cloud storage
```

### 認証ファイルの用意
- プログラムからfirebaseにアクセスするために使用する認証ファイルです。
- 以下の手順でjsonファイルを取得します。

> https://firebase.google.com/docs/admin/setup?hl=ja より
>> 1. Firebase コンソールで、[設定] > [サービス アカウント] を開きます。
1. [新しい秘密鍵の生成] をクリックし、[キーを生成] をクリックして確定します。
1. キーを含む JSON ファイルを安全に保管します。

# Cloud Firestoreを使う
- pythonからfirestoreを使う方法についてまとめます。

### ライブラリのimport
- まず、使用するライブラリ類をimportします。

```python:import
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
```

### 認証情報の初期化
- 先ほど用意したjsonの認証ファイルを用いてプログラムからfirebaseにアクセスできるようにします。
- 以下のコードで初期化を行います。

```python:initialize
cred = credentials.Certificate('<pass_to_json_file>')
default_app = firebase_admin.initialize_app(cred)
```
- **`<pass_to_json_file>`の部分はjsonファイルへのパス(文字列)に置き換えてください。**
- **初期化はfirebaseのデータにアクセスする前に必ず行いましょう。**
    - 初期化を行わないとエラーが出てデータを取得できません。

- 以下のように`cred`と`default_app`をグローバル変数として確保しておき、初期化関数を用意することで少し便利になるかもしれません。

```python:initialize2
cred = None
default_app = None

def initializeFirebaseAPP():
	global cred
	global default_app
	cred = credentials.Certificate("< pass_to_json_file >")
	default_app = firebase_admin.initialize_app(cred)
```

#### 自分は利用しなかったのですが、環境変数を利用してjsonファイルへのパスを通すこともできます。

1. (ターミナルや端末で)環境変数を通します。
2. この場合の初期化は引数を指定せずに行います。

```bash:export
export GOOGLE_APPLICATION_CREDENTIALS="<pass_to_json_file>"
```

```python:initialize
default_app = firebase_admin.initialize_app()
```

### Firestoreからデータを取得
- **単一のドキュメント**を指定して、内容を取得する例です。
- `get`を使います。
- このとき、`doc_ref`の指すdocumentが存在しないと例外が投げられます。
    - この例外は`google.cloud.exceptions.NotFound`なので、必要に応じて`try`で対処しましょう
    - `doc_ref`に対して、`exists`メソッドを利用することで、documentが存在するかを確認できます。

```python:getFirestoreData
def getFirestoreData(collectionName, documentName):
	db = firestore.client()
	doc_ref = db.collection(collectionName).document(documentName)

	doc = doc_ref.get()
	print(u'Document data: {}'.format(doc.to_dict()))
```

- コレクション直下の**ドキュメントを全て**取得するには`collection`に対して`stream`を使います。

```python:getFirestoreData2
def getFirestoreData2(collectionName):
	db = firestore.client()
	docs = db.collection(collectionName).stream()

	for doc in docs:
		print(u'{} => {}'.format(doc.id, doc.to_dict()))
```

### Firestoreからデータを取得(再帰的利用)
- ドキュメント内のコレクションにアクセスするには、documentに対してcollectionメソッドを利用します。
- 直感通りの操作でできますね。

```python:getFirestoreData3
def getFirestoreData3(col_name, doc_name, col_name2):
	db = firestore.client()
	doc_ref = db.collection(col_name).document(doc_name)
	col_ref2 = doc_ref.collection(col_name2)
```

- 現在のところ、pythonではドキュメントのサブコレクション一覧を取得することはできません。
- もし、サブコレクション一覧を取得したいのであれば他の言語を併用しましょう。

- 例えば、rubyで書くのであれば以下の通りです。

```ruby:getCollectionList.rb
require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new project_id: "<project_id>", keyfile: "<path_to_json>"
doc_ref = firestore.doc "<col_name>/<doc_name>"

doc_ref.cols do |col|
	puts col.collection_id
end
```

### Firestoreへデータを追加
- コレクション名とドキュメント名を指定して辞書型データを登録する例です。

```python:uploadData2Firestore
def uploadData2Firestore(collectionName, documentName, data):
	db = firestore.client()
	doc_ref = db.collection(collectionName).document(documentName)
	doc_ref.set(data)
```

### Firestoreのデータを更新
- すでにあるドキュメント上のフィールドを更新する方法です。

```python:update_data
def update_data(collectionName, documentName, key, value):
    db = firestore.client()
    doc_ref = db.collection(collectionName).document(documentName)

    doc_ref.update({key: value})
```

### Firestoreからデータを削除
- コレクション名とドキュメント名を指定してデータを削除する例です。

```python:deleteFirestoreDocument
def deleteFirestoreDocument(collectionName, documentName):
	db = firestore.client()
	db.collection(collectionName).document(documentName).delete()
```

---

# Cloud Storageを使う
### ライブラリのimport
- まず、使用するライブラリ類をimportします。

```python:import
import firebase_admin
from firebase_admin import credentials
from google.cloud import storage
```

### 認証情報の初期化
- 先ほど用意したjsonの認証ファイルを用いてプログラムからfirebaseにアクセスできるようにします。
- 以下のコードで初期化を行います。

```python:initialize
storage_client = storage.Client.from_service_account_json('<pass_to_json_file>')

```
- **`<pass_to_json_file>`の部分はjsonファイルへのパスに置き換えてください。**
- **ここで初期化した変数はこの後利用していきます。**

#### 自分は利用しなかったのですが、環境変数を利用してjsonファイルへのパスを通すこともできます。

1. (ターミナルや端末で)環境変数を通します。
2. この場合の初期化は引数を指定せずに行います。

```bash:export
export GOOGLE_APPLICATION_CREDENTIALS="<pass_to_json_file>"
```

```python:initialize
storage_client = storage.Client()
```

### Cloud Storageからデータを取得
- blobのurlを指定して、データをファイルにダウンロードする例です。

```python:download_blob
def download_blob(src_blob_url, dst_file):
	bucket_name = '<bucket_name>'
	bucket = storage_client.get_bucket(bucket_name)

	blob = bucket.blob(src_blob_url)
	storage_client.download_blob_to_file(blob, dst_file)

	print('Blob {} downloaded to {}.'.format(src_blob_url, dst_file))
```

### Cloud Storageへデータを追加
- ファイル名とデータを指定して登録する例です。

```python:upload_blob
def upload_blob(file_name, data_sorce):
	bucket_name = '<bucket_name>'
	bucket = storage_client.get_bucket(bucket_name)
	blob = bucket.blob(file_name)
	blob.upload_from_filename(filename=data_sorce)

	if file_name in bucket.list_blobs():
		print("cloud storage: upload success")
	else:
		print("cloud storage: upload failed")
```

### Cloud Storageからデータを削除
- blobのurlを指定してデータを削除する例です。

```python:delete_blob
def delete_blob(blob_url):
	bucket_name = '<bucket_name>'
	bucket = storage_client.get_bucket(bucket_name)
	blob = bucket.blob(blob_url)

	blob.delete()
```

# まとめ
- 環境変数を利用しないやり方があまり情報としてなかったのでまとめてみました。
- 実際に運用する上では環境変数を利用した方が安全なのかもしれませんが...

- 以下、よくみたサイトです。
    - [Cloud Firestore を使ってみる](https://firebase.google.com/docs/firestore/quickstart?hl=ja)
    - [Cloud Firestore にデータを追加する](https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja)
    - [Cloud Firestore でデータを取得する](https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja)
    - [Cloud Firestore: python APIリファレンス](https://googleapis.dev/python/firestore/latest/index.html)
- 誤植等ありましたらご連絡いただければ幸いです。