kintoneのアプリレコードにpythonで登録する方法（レコード1件）

# kintoneのアプリレコードにpythonで登録する方法（レコード1件）

## 最初に

　kintoneのアプリのデータを自動で一括取得したり、登録したりしたい状況があるかと思います。kintoneではREST APIが用意されており、いろいろなプログラム言語等からデータを簡単に取得したり登録したりすることができます。
　そこで、本記事ではpythonを使ってkintoneのアプリにレコードを1件登録する方法をご紹介します。  
  
【コンセプト】
* 本記事だけで完結
* 標準モジュールだけで記述
（exe化を見越して軽量に）

kintone公式のkintone APIの使い方の説明に従います。
'https://cybozu.dev/ja/kintone/docs/rest-api/records/add-record/'

pythonを使ったkintoneのREST APIの操作解説一覧  
'https://qiita.com/guitar_ell_r/items/e76a2134b9d59b19ae61'

【解説動画】'https://youtu.be/OoHvtmpp020'
<iframe width="560" height="315" src="https://www.youtube.com/embed/OoHvtmpp020" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

---

## 環境

* windows11
* python 3.11.1

---

## 準備するもの

* kintoneアプリ
* 上記のレコード追加、編集の権限があるログイン名とパスワード

---
今回は下記フィールドが配置されたアプリを用意します。

|フィールド名|フィールドコード|
|---|---|
|文字列（1行）|文字列__1行_|
|文字列（複数行|文字列__複数行|
|数値|数値|
|日時|日時|
|チェックボックス|チェックボックス|
|ドロップダウン|ドロップダウン|
|リンク|リンク|
|テーブル|テーブル|

（テーブルには文字列（１行）：文字列__1行__0　と　数値：数値_0を設定します）

を用意しました。それぞれ後ろの文字列はフィールドコードになっています。 
   
上記と同様のアプリのテンプレートは下記にあります。  
'https://github.com/guitar-ell-r/kintoneAPI_pythonTIPS/blob/master/002_record_post_one/kintone_templete_002_record_post_one.zip'  
画面の中段あたりの右側あたりに「Download」のボタンをクリックするとテンプレートのzipファイルがダウンロードされます。このままテンプレートとしてアプリ作成のときに読み込むと今回の環境と同様のアプリを作ることができます。

---

## pythonとkintoneの通信のイメージ

![通信のイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118247/eae1f5bc-060c-2b33-ea53-5e562c8118ce.png)

　kintoneに送るREST APIに必要な情報を手順に従ってフォーマットを整えてkintoneに送信します。正しいフォーマットで送信されるとkintoneからJSONでデータが返ってきます。

## プログラム

```python 002_record_post_one.py
import base64
import urllib.request
import json
```

必要なモジュールをインポートします。
すべてpythonの標準モジュールです。

```python 002_record_post_one.py
DOMAIN = "domain"## ドメイン　自環境のものを入れる
LOGIN = "login"## ログイン名　自環境のものを入れる
PASS = "pass"## パス　自環境のものを入れる
appno = 21## 取得したいアプリのアプリNo
```

ログイン名とパスワードはそれぞれ、ご自分の環境のものを入力してください。  

アプリ番号は私の環境では21となります。上記とともに適宜書き換えてください。

次に、kintoneのREST APIを使うためのuriを設定します。  

kintoneの公式にレコード登録（1件）の場合は下記をuriとしてください、と書いてあります。  

'https://(サブドメイン名).cybozu.com/k/v1/record.json'  
このサブドメイン名の部分がDOMAINに相当するので、uriを下記のように作成します。

```python 002_record_post_one.py
uri = "https://" + DOMAIN + ".cybozu.com/k/v1/record.json"
```

kintoneのREST APIのルールで、ログイン名とパスワードを使った文字列をbase64でエンコードしたものをヘッダに記載して送信する、というものがあります。

'https://developer.cybozu.io/hc/ja/articles/201941754-kintone-REST-API%E3%81%AE%E5%85%B1%E9%80%9A%E4%BB%95%E6%A7%98'

　（パスワード認証の部分）

正確には　（ログイン名）:（パスワード）の文字列をbase64でエンコードしたもの、になります。  間にコロンが入っていることに注意してください。  
これに従ってAUTHという変数を作成します。

```python 002_record_post_one.py
AUTH = base64.b64encode((LOGIN + ":" + PASS).encode())
```

base64モジュールを使ってエンコードします。

次にリクエストのヘッダを作成します。  
下記にkintone公式の説明を引用します。  

>(2) パラメータを JSON形式で送信する場合（HTTP リクエストのリクエストボディに JSON データをセットする場合）  
>リクエストのヘッダとボディの例  
>リクエストヘッダ  
>GET /k/v1/record.json HTTP/1.1  
>Host: example.cybozu.com:443  
>X-Cybozu-Authorization: QWRtaW5pc3RyYXRvcjpjeWJvenU=  
>Authorization: Basic QWRtaW5pc3RyYXRvcjpjeWJvenU=  
>Content-Type: application/json  
>Content-Type に application/json を指定して下さい。  
 指定しない場合は JSON が解釈できないため、実行時エラーとなります。  

これをpythonでコーディングすると下記ようになります。  

```python 002_record_post_one.py
headers = {
    "Host":DOMAIN + ".cybozu.com:443",
    "X-Cybozu-Authorization":AUTH,
    "Content-Type": "application/json",
}
```

次にリクエストのボディを作成します。  
こちらもkintone公式を引用します。

>ボディ
{  
  "app": 1,  
  "record": {  
    "文字列1行": {  
      "value": "  

(一部抜粋) 'https://cybozu.dev/ja/kintone/docs/rest-api/records/add-record/'

ボディは汎用的になるように内部から作っていきます。  
これから作成するプログラムのように作ることでkintoneのフィールドが増えても簡単に対応できます。  
  
最初にフィールドの部分のbodyを作成するために空のリストを作成します。  
このリストにフィールドの情報を追加していき、最後に辞書型にして結合するようにします。

```python 002_record_post_one.py
## フィールド部分のbodyのリスト
body_fields = []
```

最初に文字列１行のフィールドについて登録したい文字列をルールに従って作成します。  
作成した辞書をbody_fieldsに追加していきます。

```python 002_record_post_one.py
## 文字列１行
temp_body = {
    "文字列__1行_":{"value":"1件登録"}
}
body_fields.append(temp_body)
```

文字列（複数行）です。複数行なので２行の文字列を追加してみます。

```python 002_record_post_one.py
## 文字列複数行
temp_body = {
    "文字列__複数行_":{"value":"1行目\n2行目"}
}
body_fields.append(temp_body)
```

このように`temp_body`に追加したいフィールドについて記述し、`body_fields`に追加していくことで、一見複雑なJSON形式の記述を少し簡単にすることができます。  

続いて数値です。

```python 002_record_post_one.py
## 数値
temp_body = {
    "数値":{"value":"17"}
}
body_fields.append(temp_body)
```

日時になります。日時は世界の標準時になるので、日本の時刻にするには下記のように`+9:00`とすることで日本の時刻に変換することができます。

```python 002_record_post_one.py
## 日時 日本の時刻にするには9時間プラスする
temp_body = {
    "日時":{"value":"2023-02-11T18:08:24+09:00"}
}
body_fields.append(temp_body)
```

チェックボックスは複数選択することができるので、`"value"`はリストにします。余談ですが、チェックボックスを登録したくないときは、下記のコード自体書かなければいいのですが、チェックボックスを何も選択しない状態にしたい、という場合は空のリストを登録するとうまくいきます。`"value":[]`とすると空のチェックボックスになります。こちらはレコード更新のときにも説明させていただきます。

```python 002_record_post_one.py
## チェックボックス
temp_body = {
    "チェックボックス":{"value":["sample1", "sample2"]}
}
body_fields.append(temp_body)
```

ドロップボックスです。

```python 002_record_post_one.py
## ドロップボックス
temp_body = {
    "ドロップダウン":{"value":"sample1"}
}
body_fields.append(temp_body)
```

リンクです。リンクの形式でない文字列の場合はkintoneとの通信はうまくいきますが、レコードとしては登録されない、という処理になります。

```python 002_record_post_one.py
## リンク　web
temp_body = {
    "リンク":{"value":"https://cybozu.co.jp/"}
}
body_fields.append(temp_body)
```

最後にテーブルです。テーブルは入れ子構造ですのでややこしいですが、下記のように整理して書くと理解しやすくなると思います。  
`"テーブル":{"value":`の後ろのリストがテーブルの行に相当します。そして各行についてそれぞれのフィールドの値を登録していく、という形になります。

```python 002_record_post_one.py
## テーブル
temp_body = {
    "テーブル":{"value":
            [
                {"value":
                    {"文字列__1行__0":{"value":"1行目"},
                    "数値_0":{"value":1}
                    }
                },
                {"value":
                    {"文字列__1行__0":{"value":"2行目"},
                    "数値_0":{"value":2}
                    }
                }
            ]
            }
}
body_fields.append(temp_body)
```

ここまででbodyのフィールドの個々の部分の記述は終了です。最後にこれらを辞書型として結合しておきます。`body_field`という辞書型の変数に、これまで登録した`body_fields`を結合します。  
具体的には空の辞書`body_field = {}`を作成し、これに`body_fields`を1個ずつ辞書型として結合していきます。

```python 002_record_post_one.py
## 全てのフィールドを辞書型として結合する
body_field = {}
for item_dict in body_fields:
     body_field = {**body_field, **item_dict}
```

次に`"record:"の部分を作ります。kintone公式のボディの作り方を再掲載します。

>ボディ
{  
  "app": 1,  
  "record": {  
    "文字列1行": {  
      "value": "  

(一部抜粋) 'https://cybozu.dev/ja/kintone/docs/rest-api/records/add-record/'

ここまでで`"record":{}"`の中かっこの中身は`body_field`で作成済ですので、下記のように`record`部分を作成します。

```python 002_record_post_one.py
## record部分のbody　フィールドの内容を辞書型で結合
body_record = {"record":body_field}
```

次に`{"app":1}`の部分を作成します。

```python 002_record_post_one.py
## アプリ番号部分のbody
body_app = {
    "app":appno
}
```

ここまでで、`body`のパーツを全て作成しました。最後に全てを結合します。

```python 002_record_post_one.py
## body全体を辞書型として作成する
body = {**body_app, **body_record}
```

これで、kintoneに送信するデータを全て作成しました。これらの情報からkintoneに送信するリクエストを作成します。

```python 002_record_post_one.py
## リクエスト作成
req = urllib.request.Request(
            url=uri, ## url
            data=json.dumps(body).encode(), ## body 
            headers=headers, ## header
            method="POST", ## POST
            )
```

pythonの標準モジュールurllibを使ってリクエストを作成します。  
こちらの記事を参考に作成しました。

'https://qiita.com/hoto17296/items/8fcf55cc6cd823a18217'  

リクエスト作成に必要なこれまでの情報、uri、body(data)、headersに加えて、どのような方法で送るのかを設定します。  
こちらも公式説明に「HTTPメソッド」はPOSTと書いてあるのでその通りにします。

次にkintoneにこのリクエストを送信します。

```python 002_record_post_one.py
try:
    response = urllib.request.urlopen(req)
except urllib.error.URLError as e:
        if hasattr(e, "reason"):
            res_error = (
                "We failed to reach a server." + "\n" +
                "Reason: " + e.reason + "\n"
            )
            print(res_error)
        elif hasattr(e, 'code'):
            res_error = (
                'The server couldn\'t fulfill the request.' + "\n" +
                'Error code: ', e.code + "\n"
            )
            print(res_error)
else:
    res_dict = json.load(response)
    print(res_dict)
```

tryで囲みましたが、リクエスト送信部分は下記です。

```python 002_record_post_one.py
response = urllib.request.urlopen(req)
```

エラーの補足については下記のサイトをほぼ同じように使わせてもらっています。  
'https://docs.python.org/ja/3/howto/urllib2.html'  
（エラーをラップする　の「その2」）

送信に成功すると結果がres_dictに入ってきます。  
jsonで返ってくるので、使いやすいように辞書型に変換して格納します。

下記に全てのコードを掲載します。  
git hubにも掲載しています。  
'https://github.com/guitar-ell-r/kintoneAPI_pythonTIPS/blob/master/002_record_post_one/002_record_post_one.py'

```python python 002_record_post_one.py
## kintoneのアプリにレコードを1件登録する
## https://cybozu.dev/ja/kintone/docs/rest-api/records/add-record/
import base64
import urllib.request
import json

## 自分の環境のものを入力することを忘れずに！
DOMAIN = "domain"## kintoneのドメイン
LOGIN = "login"## ログイン名
PASS = "password"## パスワード
appno = 21## 取得したいアプリのアプリNo

uri = "https://" + DOMAIN + ".cybozu.com/k/v1/record.json"

## https://developer.cybozu.io/hc/ja/articles/201941754-kintone-REST-API%E3%81%AE%E5%85%B1%E9%80%9A%E4%BB%95%E6%A7%98
## パスワード認証　の部分
## LOGIN と PASSを「：」でつないでbase64でエンコード
AUTH = base64.b64encode((LOGIN + ":" + PASS).encode())

## ヘッダ作成
headers = {
    "Host":DOMAIN + ".cybozu.com:443",
    "X-Cybozu-Authorization":AUTH,
    "Content-Type": "application/json",
}
## body作成



## フィールド部分のbodyのリスト
body_fields = []

## 文字列１行
temp_body = {
    "文字列__1行_":{"value":"1件登録"}
}
body_fields.append(temp_body)

## 文字列複数行
temp_body = {
    "文字列__複数行_":{"value":"1行目\n2行目"}
}
body_fields.append(temp_body)

## 数値
temp_body = {
    "数値":{"value":"17"}
}
body_fields.append(temp_body)

## 日時 日本の時刻にするには9時間プラスする
temp_body = {
    "日時":{"value":"2023-02-11T18:08:24+09:00"}
}
body_fields.append(temp_body)

## チェックボックス
temp_body = {
    "チェックボックス":{"value":["sample1", "sample2"]}
}
body_fields.append(temp_body)

## ドロップボックス
temp_body = {
    "ドロップダウン":{"value":"sample1"}
}
body_fields.append(temp_body)

## リンク　web
temp_body = {
    "リンク":{"value":"https://cybozu.co.jp/"}
}
body_fields.append(temp_body)

## テーブル
temp_body = {
    "テーブル":{"value":
            [
                {"value":
                    {"文字列__1行__0":{"value":"1行目"},
                    "数値_0":{"value":1}
                    }
                },
                {"value":
                    {"文字列__1行__0":{"value":"2行目"},
                    "数値_0":{"value":2}
                    }
                }
            ]
            }
}
body_fields.append(temp_body)

## 全てのフィールドを辞書型として結合する
body_field = {}
for item_dict in body_fields:
     body_field = {**body_field, **item_dict}

## record部分のbody　フィールドの内容を辞書型で結合
body_record = {"record":body_field}

## アプリ番号部分のbody
body_app = {
    "app":appno
}

## body全体を辞書型として作成する
body = {**body_app, **body_record}
print(body)


## リクエスト作成
req = urllib.request.Request(
            url=uri, ## url
            data=json.dumps(body).encode(), ## body 
            headers=headers, ## header
            method="POST", ## POST
            )
## リクエスト送信　結果受け取り
try:
    response = urllib.request.urlopen(req)
except urllib.error.URLError as e:## エラーが生じた場合は補足する
    # https://docs.python.org/ja/3/howto/urllib2.html tryの参考
    if hasattr(e, "reason"):
        res_error = (
            "We failed to reach a server." + "\n" +
            "Reason: " + e.reason + "\n"
        )
        print(res_error)
    elif hasattr(e, 'code'):
        res_error = (
            'The server couldn\'t fulfill the request.' + "\n" +
            'Error code: ', e.code + "\n"
        )
        print(res_error)
else:
    res_dict = json.load(response)
    print(res_dict)
```