pyhaya’s diary

プログラミング、特にPythonについての記事を書きます。Djangoや機械学習などホットな話題をわかりやすく説明していきたいと思います。

データベースの学習環境をDockerで作った話

データベースの勉強をするときに書いたクエリを実際に試すことはとても重要ですよね。しかし、私はこれまで書いたクエリを試すための環境構築が面倒で下のサイトを利用して SQL を実行していました。
www.db-fiddle.com

このサイトはとても便利なのですが、毎回 CREATE TABLE から始めなければならなくてこれはこれで面倒なのと、やはりローカルに入れて勉強した方が深いところまで学ぶことができるのではないかと感じたので試してみることにしました。

動作環境

動作確認は WSL 内で行っています。

  • Windows 10 Home
  • WSL2 (Ubuntu 18.04)
  • Docker 19.03.13
  • docker-compose 1.27.4

Docker image の取得

PostgreSQL は DockerHub に公式イメージが公開されているのでそれを使います。バージョンに特にこだわりはないのと、最近 PostgreSQL の大規模なバク修正があったと聞いたので latest を使います。

news.mynavi.jp

$ docker pull postgres:latest

docker-compose.yaml の作成

毎回コンテナを起動するときにいろいろ指定するのは面倒なので全部 docker-compose.yaml に書いておきます。

version: '3'

services:

  db:
    image: postgres
    container_name: db
    restart: always
    environment:
      POSTGRES_PASSWORD: your-password
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./sql:/home

コンテナデータを永続化するためにホストのディレクトリ(./data)をコンテナ内でデータベース情報の入るディレクトリ(/var/lib/postgresql/data)にマウントします。また、SQLファイルを入れるディレクトリ(./sql)もマウントしています。

構築した環境を使う

準備はできたのでコンテナを起動します。

$ docker-compose up -d
$ docker-compose ps  # コンテナが無事起動しているか確認

Name              Command              State    Ports
-------------------------------------------------------
db     docker-entrypoint.sh postgres   Up      5432/tcp

コンテナが無事に起動出来たら学習用のデータベースを作りましょう。

$ docker exec -it db /bin/bash
root@92913b69db39:/# createdb -U postgres testdb
root@92913b69db39:/# psql -U postgres -d testdb
psql (13.1 (Debian 13.1-1.pgdg100+1))
Type "help" for help.

testdb=# \q
root@92913b69db39:/# exit
$ 

今回は単に学習用なのでユーザーは postgres を使ってしまいます。

次に、このデータベースにテーブルを作成する SQL を実行します。

create_table.sql

CREATE TABLE testtable (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

テーブルを作成するには

$ docker exec -it db psql -U postgres -d testdb -f /home/create_table.sql

を実行します。CREATE TABLE という出力が出れば成功です。

最後に、SQL を実行するコマンドの効率化をしましょう。毎回実行するたびに上のコマンドを実行するのは面倒なのでシェルスクリプトを作ります。

runner.sh

#!/bin/bash                                                                                                                                                               
docker exec -it db psql -U postgres -d testdb -f /home/$1

chmod +x runnser.sh で実行権限を付与しておけば、

$ ./runner.sh create_table.sql

のように実行することができます。これで勉強がはかどりますね!


Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

Python + Docker でデータ解析環境の管理

今回は実験データ自体の解析の話ではなく、どのような環境で解析を行ったのかという「環境」の管理について書きたいと思います。大学などで研究をしていて論文としてその成果をまとめる場合、関連するデータや解析プログラムは所属する大学もしくは研究室に整理して保存しておくことが一般的です。これはもちろん、論文に示されているデータがどのように解析されているのかあとから確認することができるようにしておくためです。

実験データの解析を例えば Python で行っている場合、解析の再現性を担保するためにはデータファイルのみならず解析を行っている環境(ライブラリのバージョンなど)も重要になってきます。そこで、この記事では Docker を使ってこの環境の管理をしたという話をします。

動作環境

  • Windows10 Home
  • WSL2 (Ubuntu 18.04)

管理したい環境

以下のような環境で実験データの解析を行うことを目指して環境を作ります

Docker image の作成

FROM jupyter/base-notebook:python-3.8.6

COPY ./requirements.txt .

RUN pip install -r requirements.txt

EXPOSE 8888

base image としては jupyter notebook のイメージが Docker Hub にあるのでこれを使います。タグは latest のものを使うと Docker Hub が更新されたときに中身が変わってしまうので python-3.8.6 を指定します。

解析に使うライブラリは requirements.txtバージョンを明記して書いておきます。

docker-compose.yaml の作成

実際にこのコンテナを運用するときには、ホストから Jupyter Notebook を編集するためにポートフォワーディングをしたり、解析データをマウントしたりします。これらはコンテナを起動するときに指定することは出来ますが、いちいち書くのは面倒なので docker-compose を使います。

version: "3"                                                                                                                                                                                               
services:                                                                                                                                                                                                  
  my-jupyter:                                                                                                                                                                                           
    build: .                                                                                                                                                                                               
    image: my-jupyter:1.0.0                                                                                                                                                                             
    ports:                                                                                                                                                                                                 
      - 8888:8888                                                                                                                                                                                          
    volumes:                                                                                                                                                                                               
      - .:/home/jovyan/                                                                                                                                                                                    
    command: jupyter lab 

コンテナの実行

コンテナを動かすためには、

docker-compose up

を実行します。もしも Docker image がビルドされていないときにはビルドから開始します。http://localhost:8888にアクセスしてJupyter Lab が起動すれば成功です。

GCP の Credential の読み込み

Google Cloud Platform では認証情報をJSON形式などでダウンロードすることで外部のアプリケーションから BigQuery などのサービスにアクセスすることができます。しかし、認証情報を読み込ませるのに苦労したので解決までのメモを残しておきます。

環境

何をやろうとしていたか

Go を用いて BigQuery にアクセスをしようと考えてました。そのためにGCPから認証情報が入ったJSONをダウンロードした

どこで詰まったのか

ドキュメントにあるようにJSONまでのパスを環境変数にセットしました。

export GOOGLE_APPLICATION_CREDENTIALS=/home/.../credential.json

さあ、これでアクセスできると考えプログラムを走らせたところ

Response: {
  "error": "invalid_grant",
  "error_description": "Token has been expired or revoked."
}

。。。いや、今発行したばかりなのに期限切れとか取り消されたとかなんだよ。試しに、.bashrcにこの環境変数をセットして試してみましたがダメでした。echo $GOOGLE_APPLICATION_CREDENTIALSで確認するとちゃんとセットした値が出てくるので環境変数には正しく設定されているんだよな。。。

どう解決したか

最終的に、これはプロジェクトのルートディレクトリに .env を作ってそこに

GOOGLE_APPLICATION_CREDENTIALS=/home/.../credential.json

とセットし、main()の中で.envファイルを読み込むとエラーがでなくなりました。なぜかはわかりません。


GCPの教科書

GCPの教科書

Django で Chart.js を使ってグラフを描く

DjangoPython を使うことで簡単に Web アプリケーションを作ることができることから非常に人気のあるフレームワークです。しかし、高度なアニメーションなどは Python だけでは表現することが難しく、JavaScript の力を借りることが多い印象を受けます。この記事では、グラフ描画でよく用いられる Chart.js を Django のプロジェクト内で使う方法について紹介したいと思います。特に、Python 側で処理したデータを JavaScript に渡して、Chart.js で描画するという部分について書きます。

開発環境

Django と Chart.js を結ぶ django-chartjs というライブラリがありますが、正直ここまでやらなくていいという感じを受けたので今回はこのライブラリ無しで実装を進めます。

準備

Django プロジェクトの準備はこれまでに何度か書いていますので、割愛します。詳しく知りたい方は以下の記事の「準備」までを参考にして作ってみてください。
pyhaya.hatenablog.com

Chart.js を使ってみる

ひとまず、Django の存在は最初にはできるだけ意識せず、HTML と JavaScript を使って適当なグラフを作ってみます。

{% load static %}
<html lang="ja">
    <header>
        <meta charset="utf-8">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
        <script type="text/javascript" src="{% static 'visualize/js/draw.js' %}" defer></script>
    </header>
    <body>
        <div style="width: 50%; heigh: 50%;">
            <canvas id="graph"></canvas>
        </div>
    </body>
</html>

グラフだけを表示する HTML です。JavaScript のコードは

var ctx = document.getElementById("graph");

var myLineChart = new Chart(ctx, {
  type: "line",
  data: {
    labels: [
        "Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug.",
        "Sep.", "Oct.", "Nov.", "Dec."
    ],
    datasets: [
      {
        data: [1, 2, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121],
        borderColor: "salmon",
        backgroundColor: "rgba(0,0,0,0)",
      },
    ],
  },
  options: {
    title: {
      display: false,
    },
    scales: {
      yAxes: [
        {
          ticks: {
            suggestedMax: 130,
            suggestedMin: 0,
            stepSize: 10,
            callback: function (value, index, values) {
              return value;
            },
          },
        },
      ],
    },
  },
});

x軸を labels に書いて、y軸の値を dataset に書いて、それ以外の軸の設定を options に書くというチュートリアルに載っていそうなコードです。

Django からデータを渡してグラフを描画する

では次に、Django からデータをもらってグラフを書くということをしてみます。これはユーザーがなにか操作をしてグラフを更新するような状況で、グラフにプロットされている値が不変でない状況です。

Django では HTML には context という形でデータを渡すことができます。そのため、JavaScript のコードを HTML 内の script タグの中に全部入れるという解決策がまず思い浮かびます。しかし、JavaScript のコードが長くなると、ソースコードがごちゃごちゃになってしまいます。そこで、データを JavaScript の変数に受け渡すところだけ script タグ内に入れるようにすればコードはスッキリします。

{% load static %}
<html lang="ja">
    <header>
        <meta charset="utf-8">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
        <script type="text/javascript" src="{% static 'visualize/js/draw.js' %}" defer></script>
        <script type="text/javascript">var data = {{ data }}</script>
    </header>
    <body>
        <div style="width: 50%; heigh: 50%;">
            <canvas id="graph"></canvas>
        </div>
    </body>
</html>

var data = {{ data }}Django から JavaScript へデータの受け渡しが行われます。

var ctx = document.getElementById("graph");

var myLineChart = new Chart(ctx, {
  type: "line",
  data: {
    labels: [
        "Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug.",
        "Sep.", "Oct.", "Nov.", "Dec."
    ],
    datasets: [
      {
        data: data,  // ここで data を使っている
        borderColor: "salmon",
        backgroundColor: "rgba(0,0,0,0)",
      },
    ],
  },
  options: {
    title: {
      display: false,
    },
    scales: {
      yAxes: [
        {
          ticks: {
            suggestedMax: 130,
            suggestedMin: 0,
            stepSize: 10,
            callback: function (value, index, values) {
              return value;
            },
          },
        },
      ],
    },
  },
});

肝心の Python コードは

views.py

from django.shortcuts import render
from django.views import View

# Create your views here.
class Index(View):
    def get(self, request):
        context = {"data": [i*i for i in range(12)]}
        return render(request, "visualize/index.html", context)

です。これで画面をリフレッシュすると先程のグラフと同様のグラフが表示されます。
f:id:pyhaya:20200930222012p:plain

GraphQL から GitHub API を叩く

最近、GitHub API を使ってリポジトリ情報を取ることが多いのですが、REST API より GraphQL を使ったほうが必要な情報だけを一気に取れるということを耳にし、試してみることにしました。

作業環境

Python の requests パッケージを使って GitHub API の graphql エンドポイントを叩きます。

準備

GraphQL を使う場合には Access Token が必須になるので発行しておきます。

docs.github.com

Query を作成

GraphQL はクエリの書き方が特殊なので REST API を普段使う人にとっては最初とっつきにくい感じを受けます。最初に指定できるのは Query という分類をされているもので、GitHub API のドキュメントに一覧が載っています。

developer.github.com

この中から欲しい情報を選ぶのが最初のステップです。ここでは user にしてみましょう。上のリンクの一番下に userの説明があります。変数として login のみを指定できるようですね。これは String 型です。試しに私の情報を取ってみます。

{
  user(login: "Hayashi-Yudai")
}

指定できました。これだけでは実行しても指定した情報が足りずエラーが出てしまいますので情報を追加していきます。userUser型を返すと書いてある(user の横に青文字で User と書いてある)のでリンクをクリックして User 型がどのような情報をもっているのか見てみます。

commitCommentsfollowers など色々あります。issuesというのがあるのでこれを取ってみましょう。引数として firstが取れるのでこれを10にして最初の10個を取ってみます。

{
  user(login: "Hayashi-Yudai") {
    issues(first: 10)
  }
}

issues の中には edgesnodesなどが入っています。こんな調子でどんどん深くまでたどっていくと、

{
  user(login: "Hayashi-Yudai") {
    issues(first: 10) {
      nodes {
        body
      }
    }
  }
}

というクエリを書くことができます。一番深いところにある body は型が String なのでこれ以上深いところには何もありません。

Query を実行する

では最後に上で作成したクエリを Python で実行してみます。

import requests
import os

headers = {"Authorization": f"Bearer {os.environ.get('GITHUB_TOKEN')}"}


def run(query): 
    request = requests.post(
        'https://api.github.com/graphql', 
        json={'query': query}, 
        headers=headers
    )
    if request.status_code == 200:
        return request.json()
    else:
        raise Exception(f"Query failed to run by returning code of {request.status_code}. {query}")

       
query = """
{
  user(login: "Hayashi-Yudai") {
    issues(first: 10) {
      nodes {
        body
      }
    }
  }
}
"""

result = run(query)

GitHub Access Token は私の場合には環境変数に指定しているので os.environ.getで取ってきてヘッダに入れています。result の中身を見てみると

{'data': {
  'user': {
    'issues': {
      'nodes': [
        {'body': 'When I run some codes, text garbling happened. I tried hello world program by\r\n\r\n* Python3\r\n* Java\r\n* C\r\n* C++ \r\n\r\nbut, only in C and C++, text garbling happened.'},
        {'body': 'In HTML, the width of `th` component is assigned like "5%", but it does not reflected in the page.'},
        {'body': 'add regularization and batch-normalization to transposed convolution layer'},
        {'body': 'add the pooling layer'},
        {'body': 'create UNet by components I prepared.'},
        {'body': 'To training UNet model, I should change input images to one-hot representation'},
        {'body': 'create training session'},
        {'body': 'To evaluate the model, I should set metrics such that accuracy or loss'},
        {'body': 'show training result as images'},
        {'body': 'To split training and validation of the model, I should give the is_training parameter when running the session.'}
      ]
    }
  }
}}

これを見てみると user -> issues -> nodes -> body という構造になっていてクエリで書いた構造と同じ構造でレスポンスが返ってきていることが分かります。

まとめ

GraphQL を使うと自分のほしい要素だけを取り出せるのでレスポンスがREST API よりスッキリしますね。さらに、上の例ですと、Issue の一覧からそれぞれの body を一回のクエリで取れていますが、REST API ですと users/Hayashi-Yudai/eventsで イベントの一覧を取ってからその中の type: "IssueEvent" のものを取ってそれぞれに対して対応する URL にリクエスト投げて。。。と非常にリクエスト回数が多くなります。アプリケーションの中で使うときにはレスポンスの改善に非常に効果がありそうです。

Django でログインページを作る

PythonフルスタックWebフレームワークとして有名な Django を使ってログインページを簡単に作ってみます。

開発環境

準備

雛形の生成

Django のプロジェクトをセットアップします。

$ mkdir myapp
$ cd myapp
$ django-admin startproject config .
$ python manage.py startapp myauth
$ python manage.py startapp appmain
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser  # ユーザー名とパスワードを設定する

これらを実行することで以下のようなディレクトリ構成が得られます。

$ tree -L 2
.
├── appmain    # 内部ページ
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   ├── views.py
├── config    # プロジェクト全体の設定
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3
├── manage.py
└── myauth    # 今回ログインページをつくるところ
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    ├── models.py
    ├── tests.py
    └── views.py
基本的な設定

config/settings.py

# ...
LOGIN_REDIRECT_URL = "/"  # ログイン後にどこに飛ばすか
LOGIN_URL = "/myauth"  # ログインページの URLを設定

# ...

INSTALLED_APPS = [
    "myauth.apps.MyauthConfig",  # <- 追加
    "appmain.apps.AppmainConfig",  # <- 追加
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

# ...

config/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("appmain.urls")),
    path("myauth/", include("myauth.urls")),
]

appmain/urls.py

from django.urls import path
from appmain import views

urlpatterns = [path("", views.index, name="index")]

myauth/urls.py

from django.urls import path
from myauth import views

app_name = "myauth"
urlpatterns = [path("", views.Login.as_view(), name="login")]
内部ページの実装

ログインしたあとに飛ぶページを作ります。これは今回の主題ではないので簡単に作ります。上の appmain/urls.py に設定したように、ルートページとして views.index を表示するようにしているので、これを作ります。

appmain/views.py

from django.shortcuts import render
from django.contrib.auth.decorators import login_required


@login_required
def index(request):
    return render(request, "appmain/index.html")

呼ばれたら index.html を表示するだけです。シンプルですね。

appmain/templates/appmain/index.html

<html>
  <head></head>
  <body>This is inner page</body>
</html>

Django では HTML ファイルは慣習的に (app名)/templates/(app名)/ ディレクトリに入れるということになっていますので少しパス名が長くなります。

ログインページの実装

まずはログイン情報を入力するフォームを作ります。

myauth/forms.py

from django.contrib.auth.forms import AuthenticationForm


class LoginForm(AuthenticationForm):
    def __init__(self, *args, **kwargs):
        super(LoginForm, self).__init__(*args, **kwargs)

Django には認証用のフォームを作るためのクラスが用意されているのでそれを継承すれば良いです。このフォームをビューに組み込みます。

myauth/views.py

from django.contrib.auth.views import LoginView

from myauth.forms import LoginForm


class Login(LoginView):
    form_class = LoginForm
    template_name = "myauth/index.html"

これでログインページの実装は9割方終わりです。あとは HTML ファイルを作ります。

myauth/templates/myauth/index.html

<html>
  <head>
    <title>Login Page</title>
  </head>
  <body>
    <h2>Login</h2>
    <form method="post" action="{% url 'myauth:login' %}">
      {% csrf_token %}
      {{ form.username }}
      {{ form.password }}
      {% if form.errors %}
      <p>ユーザー名またはパスワードが一致しません</p>
      {% endif %}
      <input type="submit" value="login"/>
    </form>
  </body>
</html>

ビューの中でフォームを取り込んでいるので、HTML内では LoginFormの持つ変数を使うことができます。この記事では明示的にフォーム内で変数を定義することはしていませんが、AuthenticationFormusernamepasswordを定義しているのでそれを HTML 内で {{ form.username }}のようにして使うことができます。

github.com

フォームのアクションとして myauth:loginを指定しています。これは「myauth」という名前空間内の「login」というビューにフォームの内容を送信するということを指定しています。「myauth」の urls.py 内を見直してみると

path("", views.Login.as_view(), name="login")

という記述があります。myauth:loginlogin は、この name="login" を指しています。

実行してみる

では、今まで実装してきたコードを試してみます。

$ python manage.py runserver

として http://localhost:8000/myauth にアクセスしてみます。

f:id:pyhaya:20200922202208p:plain

CSS は何も書いていないので非常にシンプルですがきちんとログインページが出来ています。一番最初に管理者としてユーザーを1人登録していますのでそのユーザーのユーザー名とパスワードを入力すれば内部ページに飛べます。間違っていれば ユーザー名またはパスワードが一致しません という文字が表示されるはずです。

Python から PostgreSQL を操作する

Python はデータの解析で使われることが多く、様々な記事で解析の方法や結果の可視化手法が紹介されています。多くの記事では構造化データを扱う例として CSV を pandas を使って解析していますよね。しかし、扱うデータが非常に多い場合にはデータベースを叩いてほしいデータを取り出して解析するということも往々にして行われています。この記事ではデータベースとして有名な PostgreSQL を、Python から操作する方法について紹介したいと思います。

動作環境

データベースの準備

PostgreSQLUbuntu へのインストールは以下の記事を参考にしました。

wiki.debian.org

Debian用の記事ですが apt install を使うだけなので問題はありませんでした。無事インストール出来たか確認します。

psql -V
# psql (PostgreSQL) 12.4 (Ubuntu 12.4-0ubuntu0.20.04.1)

上のようにバージョンがちゃんと表示されていればOKです。次にデータベースを1つ作ります。

createdb mydb
psql mydb  # 接続確認
mydb=>\l  # 存在するデータベースのリストを表示

                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
 mydb      | pyhaya   | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 |
 postgres  | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 |
 template0 | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres

テーブルは Python で作ることもできますが、1回しか実行しないものなので SQL でやっておきます。

CREATE TABLE repo (
  id INTEGER,
  node_id VARCHAR(100),
  name VARCHAR(100),
  full_name VARCHAR(100),
  owner VARCHAR(100),
  private BOOLEAN,
  PRIMARY KEY(id)
);

このテーブルは後に GitHub API を使ってリポジトリの情報を取ることを想定しています。APIの使い方は下のリンクを参考にしてください。
docs.github.com

この SQL を実行するには

mydb=>\i create_table.sql

とします。

Python の準備

データベースと通信するためにパッケージを入れます。

pip install psycopg2

Python から PostgreSQL を操作する

import psycopg2
import dotenv
import os
import requests
from requests.auth import HTTPBasicAuth


def get_repos():
    auth = HTTPBasicAuth(
        os.environ.get("GITHUB_USERNAME"), os.environ.get("GITHUB_TOKEN")
    )
    data = requests.get("https://api.github.com/users/(USER_NAME)/repos", auth=auth)
    data = data.json()

    return data[0]


def connect(user, dbname, password):
    return psycopg2.connect(
        " user=" + user + " dbname=" + dbname + " password=" + password
    )


if __name__ == "__main__":
    dotenv.load_dotenv()

    user = "pyhaya"
    dbname = "mydb"
    password = os.environ.get("PG_PASSWORD")

    data = get_repos()

    ID = data["id"]
    node_id = data["node_id"]
    full_name = data["full_name"]
    owner = data["owner"]["login"]
    private = data["private"]

    query = (
        "INSERT INTO repo (id, node_id, full_name, owner, private) VALUES"
        + f" ({ID}, '{node_id}', '{full_name}', '{owner}', {private});"
    )

    with connect(user, dbname, password) as conn:
        with conn.cursor() as cur:
            cur.execute(query)
            conn.commit()

データベースへの接続のために with を使っています。これを使わずに操作の終了後にコネクションを connect.close()のようにすることもできますが、withを使ったほうが安全なのでこちらを使うようにします。

ちゃんとデータベースに入ったか確認してみます。

mydb=> SELECT id, node_id, full_name, owner, private FROM repo;

    id     |             node_id              |          full_name          |     owner     | private
-----------+----------------------------------+-----------------------------+---------------+---------
 251618774 | MDEwOlJlcG9zaXRvcnkyNTE2MTg3NzQ= |    pyhaya/analysis_memo     |    pyhaya     | f
(1 row)

ちゃんと入っていることが確認できました。