[Python] Djangoチュートリアル - WebAPIサーバの簡単構築方法

## この記事について

PythonのWebアプリケーションフレームワーク「Django」についてのチュートリアル記事です。今回は「**Django REST framework**」を使ったWebAPIと、それを使った簡単なWebアプリケーションを構築します。具体的にはこちらの記事の初心者向け実践記事となります。

- [Django REST Frameworkを使って爆速でAPIを実装する](https://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8)

Djangoの機能の細かい説明は省略します。まずは実用的なアプリケーションを完成させ、疑問点等についてはWebや参考リンク等で各々確認するスタイルでお願いします。


 - [前回記事 [Python] Djangoチュートリアル - 汎用業務Webアプリを最速で作る](https://qiita.com/okoppe8/items/54eb105c9c94c0960f14) 

執筆環境

|||
|:--|:--|
|OS|**Windows 7**|
|Python|**3.6.3**|
|Django|**2.0.5**|
|django-rest-framework|**0.1.0**|


## この記事で作るもの

### システム概要
![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/b9bea287-bcf4-64e5-1ed9-421d4c2ac5d2.png)

- IoTシステムをイメージしています。Raspberry Piのようなセンサーデバイスが気温と湿度の情報をWebAPIに送信します。
- 集計情報をグラフ表示するWebアプリケーションを同じサーバで公開します。データの取得はWebAPI経由で行います。
- WebAPI、WebアプリケーションへのアクセスはID/Passwordで認証させます。デバイスからWebAPIへの認証は簡易利用を想定してBasic認証とします。

### 画面イメージ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/f5181d41-08b9-3c8d-5f80-3662a5ff6bf1.png)

## 用語解説

※初心者向けの解説です。正確な内容については参考リンクで再確認してください。

### WebAPI ってなに？

Webアプリケーションはブラウザからしか使えない。…そんなふうに考えていた時期が俺にもありました。

WebAPIとは、Webアプリケーションを「ブラウザ以外」からでも利用できるようにするための技術です。

通常のWebページは画面に表示するのが目的なので、本来の目的のデータに加えてレイアウト情報やアイコン画像など人間が理解しやすくするためのデータが大量に含まれます。WebAPIでは通信するデータを純粋にデータのみに限定し、表示や加工処理をクライアントのアプリケーションに任せます。

当初はGoogleマップのようにページ全体を再読み込みさせずに追加データを次々と表示させる技術として使われました。画面とデータを分けて管理できること、データの管理単位を分割し複数のWebAPIを組み合わせて利用できるメリットが評価され、現在ではスマホアプリやIoTデバイスのデータアクセス方法として利用され社会に欠かせないものになりました。

ExcelやFileMakerのようなアプリケーションの最新版にもWebAPI連携機能が搭載されており以前よりカジュアルな利用が可能になっています。

参考：[WebAPIについての説明](https://qiita.com/busyoumono99/items/9b5ffd35dd521bafce47)

参考：[5分で分かるWebAPI | NTT Communications Developer Portal](https://developer.ntt.com/ja/blog/5%E5%88%86%E3%81%A7%E5%88%86%E3%81%8B%E3%82%8BWebAPI)


### REST ってなに？

WebAPIの共通仕様です。

WebAPIで公開するリソースは、エンドポイントと呼ばれるURLを持ちます。
エンドポイントに対して特定のHTTPメソッドでアクセスすると、リソースに対する特定の操作の意味を持ちます。例としては以下のような感じです。


```
# メソッド # エンドポイント
POST /users # 新規作成
GET  /users # ユーザ一覧の取得
GET  /users/1 # ユーザID = 1のユーザ情報を取得
PUT  /users/1 # ユーザID = 1のユーザ情報を更新
DELETE /users/1 # ユーザID = 1のユーザ情報を削除
```

通常のWebアプリケーションでは使わないPUT、DELETEを利用しているのがポイントです。送受信するデータのフォーマットはXMLとJSONが使えますが、現在ではJSONが主流になっています。

参考：[REST入門 基礎知識](https://qiita.com/TakahiRoyte/items/949f4e88caecb02119aa)

### Django ってなに？

PythonのWebアプリケーションフレームワークです。
CakePHPやRuby on Rails のPython版と思ってください。

詳しくは[前回のリンク集](https://qiita.com/okoppe8/items/54eb105c9c94c0960f14)を参照してください。

Djangoでは「Django REST Framework」というパッケージが提供されています。
WebAPIを実際に作ろうとするとかなり手間がかかるのですが、このパッケージを利用することで素人でも簡単にRESTなWebAPIを公開することができます。


参考：[Django REST Frameworkを使って爆速でAPIを実装する](https://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8)

## サンプルコード

完成品のアプリケーションをGithubに公開しました。
https://github.com/okoppe8/Django-REST-Framework-easy-sample

Git-Cloneもしくはダウンロード後、プロジェクトのディレクトリに移動して以下のコマンドを順に実行してください。

※Windows用 Macでは適切に読み替えてください。

```
python -m venv env
env\Scripts\activate
pip install -r requirements.txt
manage.py migrate
manage.py prepare_dummydata
manage.py createsuperuser 
manage.py runserver
```

最後の`runserver`でWebアプリケーションが起動するので、ブラウザより`http://localhost:8000`にアクセスしてください。ログイン画面ではcreatesuperuserで入力したID/PASSを使います。

### herokuへのデプロイ方法

サンプルコードはherokuにアップロードしてインターネット上で試すことができます。こちらに丁寧にやりかたが乗ってますので手順通りに進めてみましょう。

- [Django Girls Tutoria - デプロイ！](https://djangogirlsjapan.gitbooks.io/workshop_tutorialjp/deploy/)

ただし以下の２点が異なるので適切に修正してください。

- Procfile の「mysite」は自分で付けたプロジェクト名に変える。サンプルのままなら「project」となる。

```py:Procfile
web: gunicorn project.wsgi
```

- チュートリアル内のpsycopg2のバージョンは「2.5.4」を指定しているが、最新版「2.7.4」に置き換える。

```py:requirements.txt
psycopg2==2.7.4

```

## アプリケーションの作成

### 作業概要

#### プロジェクト名等について

サンプルコードではアプリケーションに以下のように名前を付けています。
流用する場合は具体的な名前に置き換えてください。

|||
|:--|:--|
|プロジェクト|**project**|
|アプリケーション|**api**|
|モデル|**Log**|

#### 作業手順

この記事では以下の手順で作業をすすめます。

|手順|作業内容|作業ファイル| 
|:--:|:--|:--|
|**1**|プロジェクト作成|
|**2**|設定ファイル編集|project/settings.py
|**3**|モデル作成|api/models.py
|**4**|データベース作成|
|**5**|管理アプリによる動作確認|api/admin.py
|**6**|ビュー作成|app/views.py
|**7**|ルーティング設定編集|project/urls.py
|**8**|テンプレート作成|templates/index.html
|**9**|データ登録を検証|

### 手順１．プロジェクト作成

pythonをインストールしたマシンの作業用ディレクトリで以下のコマンドを実行します。
※Windows用なのでMacOSでは適切に読み替えてください。

```
mkdir project
cd project
python -m venv env
env\Scripts\activate
pip install django django-rest-framework django-filter
django-admin startproject project .
manage.py startapp api
```

さらに手動でプロジェクト直下に`templates`ディレクトリを追加し、空ファイル`index.html`を作成してください。実行後、ディレクトリは以下の状態になったはずです。

```
project
│  manage.py
├─app
│  │  __init__.py
│  │  admin.py
│  │  apps.py 
│  │  models.py
│  │  tests.py
│  │  views.py
│  ├─migrations
│    └__init__.py
├─project
│  │  __init__.py
│  │  settings.py
│  │  urls.py
│  └─ wsgi.py
└─templates   #追加
   └index.html #追加
      
```

この構成が最終形となります。自動出力されるファイルを除き、この後でファイルを作ることはありません。

#### 追加パッケージの説明

pip install で django以外に追加パッケージをインストールしました。  各パッケージの内容は以下の通りです。

|パッケージ名|説明|
|:--|:--|
|[django-rest-framework](http://www.django-rest-framework.org/)|DjangoでRestfulなWebAPIを構築する|
|[django-filter](https://django-filter.readthedocs.io/en/master/)|WebAPIに検索機能を追加する|

### 手順２．設定ファイル編集

プロジェクトの設定ファイル「`project/settings.py`」の編集を行います。
まずINSTALLED_APPSにパッケージとアプリケーションの設定を追加します。

```py:project/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  #追加
    'django_filters',  #追加
    'api',             #追加
]
```

TEMPLATESのDIRSに`os.path.join(BASE_DIR,'templates')`という文字列を追加します。先ほど追加した'templates'ディレクトリが認識されます。

```py:project/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')], #変更
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```

次にタイムゾーンの設定を探して、日本時間に置き換えてください。

```py:project/settings.py
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'ja-JP' # 上書き

TIME_ZONE = 'Asia/Tokyo' # 上書き
```

さらにファイルの最後に以下の設定を追記してください。

```py:project/settings.py

# 以下、追加

# rest_framework 設定
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

# 管理画面のログインを通常ログインとして流用する
LOGIN_URL = '/admin/login/'
LOGOUT_REDIRECT_URL = '/'

```

以上で設定ファイルの編集は完了です。

### 手順３．モデル作成

データモデルを定義します。`api/models.py`を開いて以下のコードを追加してください。

```py:api/models.py
from django.db import models


# Create your models here.
class Log(models.Model):
    created_at = models.DateTimeField(
        verbose_name='登録時間',
        auto_now_add=True,
    )
    temperature = models.FloatField(
        verbose_name='気温',
        blank=True
    )
    humidity = models.FloatField(
        verbose_name='湿度',
        blank=True
    )

    # 以下は管理サイト上の表示設定
    def __str__(self):
        return self.created_at

    class Meta:
        verbose_name = '採取データ'
        verbose_name_plural = '採取データ'
```

シンプルに登録時間、気温、温度のみのモデルとしました。デバイスによってはクライアント側でタイムゾーンの指定をするのは面倒なことがあるので、登録時間はサーバ側で自動的につけるようにしています。

### 手順４．データベース作成

データベースを作成します。以下のマイグレーションコマンドを実行してください。デフォルトの設定ではプロジェクト直下にsqliteのファイルが作成されます。

```
manage.py makemigrations
manage.py migrate
```

#### ★ ダミーデータの投入方法

サンプルコード内にダミーデータの投入スクリプトを用意しています。2018年5月1日～7日の東京の気温・湿度のデータを直近７日分のデータとして登録します。使い方は以下の通りです。

１．サンプルコード内のディレクトリ`api/fixtures`と`api/management`をapiディレクトリ下にコピーする

２．以下のコマンドを実行する

```
manage.py prepare_dummydata
```

### 手順５．管理アプリの動作確認

Djangoのプロジェクトに付属する管理アプリケーションの設定を行います。

以下のファイルを編集してください。

```py:api/admin.py

from django.contrib import admin

from .models import Log


@admin.register(Log)
class LogAdmin(admin.ModelAdmin):
    pass

```

コマンドラインより以下のコマンドを入力し管理者ユーザーを作成します。

```
manage.py createsuperuser
```

コマンドラインより以下のコマンドでアプリケーションを起動します。

```
manage.py runserver
```

起動後、ブラウザで`http://localhost:8000/admin`にアクセスします。
ログイン画面が表示されるので、createsuperuserで指定したIDとPasswordを使ってログインします。サイト管理アプリではユーザー管理のほか、データベースの直接参照・編集が可能です。先ほど投入したダミーデータを確認してみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/6b3d2ec2-950e-e35a-fa43-37313146e7e3.png)


管理アプリでは新規ユーザーの登録が可能です。実際に運用する場合はこちらからユーザーの追加をしてください。

**ユーザー追加時の注意**

このシステムではサイト管理者用のログイン機構を一般ユーザー用として流用しています。ユーザーを追加するときは「スタッフ権限」に必ずチェックを入れてください。入れないとログインできません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/a6eeb5ae-c441-9ce7-02d6-544469d8b51f.png)


### 手順６．ビュー作成

WebAPIとWebアプリの本体となるビューの設定を行います。`api/views.py`を開いて以下のコードを入力してください。

```py:api/views.py
import django_filters
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
from rest_framework import serializers
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated

from .models import Log


class LogSerializer(serializers.ModelSerializer):
    class Meta:
        model = Log
        fields = '__all__'


class LogFilter(django_filters.FilterSet):
    class Meta:
        model = Log
        fields = {'created_at': ['gte', ], }


class LogViewSet(viewsets.ModelViewSet):
    queryset = Log.objects.all().order_by("created_at")

    serializer_class = LogSerializer
    filter_class = LogFilter

    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)


class MainView(LoginRequiredMixin, TemplateView):
    template_name = 'index.html'
```

各クラスの役割を簡単に説明します。

・Serializer（ModelSerializer）

  入出力の定義。ModelSerializerでは指定したモデルでの出力、入力チェックを暗黙的に定義する。
  
・FilterSet

  django-filterの設定クラス。検索キーと検索条件を指定する。この設定をすると以下のURLで「指定した時間以降に作られたデータ」という条件の検索ができる。
  
  `http://<endpoint>/?<fieldname>__gte=YYYY-MM-DD%20HH:mm:ss`

・ViewSet（ModelViewSet）

  WebAPIの定義本体。
  ModelViewSetでは自身かSerializerで指定したモデルに対応するWebAPIが６種類同時に有効になる。

|役割|エンドポイント|HTTPメソッド|
|:--|:--|:--|
|リソースの取得（複数）|リソース名/|GET|
|リソースの作成|リソース名/|POST|
|リソースの取得（個別）|リソース名/id/|GET|
|リソースの更新（全部）|リソース名/id/|PUT|
|リソースの更新（一部）|リソース名/id/|PATCH|
|リソースの削除|リソース名/id/|DELETE|
  
・TemplateView
  
  ブラウザで表示する画面の定義。LoginRequiredMixinを継承することでアクセス時に認証をかけている。

Serializer等の詳細な情報については以下のリンクを参照してください。

参考：[[Django REST Framework] Serializer の 使い方 をまとめてみた](http://note.crohaco.net/2018/django-rest-framework-serializer/)

参考：[Django REST Frameworkを使って爆速でAPIを実装する](https://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8)

参考：[Django REST framework カスタマイズ方法 - チュートリアルの補足]([https://qiita.com/okoppe8/items/c58bb3faaf26c9e2f27f])

参考：[Django REST framework で django-filter を使う](https://qiita.com/okoppe8/items/77f7f91f6878e3f324cc)

### 手順７．ルーティング設定編集

作成したViewセットに対してルーティングの設定を行います。
project/urls.pyに以下のコードを入力してください。

```py:project/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers

from api.views import LogViewSet
from api.views import MainView

router = routers.DefaultRouter()
router.register(r'logs', LogViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
    path('', MainView.as_view()),
]

# ログイン画面のタイトルを変更
admin.site.site_header = '気温と湿度の変化'

```

これでルートディレクトリでindex.htmlを公開、/api/logs/をエンドポイントとしてWebAPIが公開されます。

#### ★ Browserble API でAPIの動作確認

Django REST Frameworkでは、WepAPIの動作をWeb画面から確認するためのツールが付属しています。runserver でアプリケーションを起動し、`http://localhost:8000/api/logs`にアクセスしてください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/672217a7-06bc-c8d4-a3f8-d2ab3243273f.png)

Debug設定が有効の時にかぎりますが、このように公開されているAPIをWeb画面で動作確認することができます。Google Chrome拡張のPOSTMANと同等の機能です。現在公開されている機能がわかりやすいというメリットがあります。

Django-Filterが有効になっているので、画面上部に「フィルタ」ボタンが表示されています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/e5122437-ce65-8e84-d2d3-3f03d20e03eb.png)

検索クエリも画面上から試すことができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/e7eb45ab-6882-3884-fdbc-6cb228dbcdba.png)


### 手順８．テンプレート作成

テンプレートファイル`templates/index.html`にデータ閲覧用のアプリケーションを記述します。今回テンプレートの機能は殆ど利用していませんが、専用タグを埋め込みことでログインユーザー等の情報を表示させることができます。

```html:templates/index.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>気温・湿度の変化</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="#">気温・湿度の変化</a>
    <button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#Navber" aria-controls="Navber"
            aria-expanded="false"
            aria-label="ナビゲーションの切替">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="Navber">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item">
                <a class="nav-link" href="{% url 'admin:index' %}">管理サイト</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{% url 'admin:logout' %}">ログアウト</a>
            </li>
        </ul>
    </div>
    <!-- /.navbar-collapse -->
</nav>
<div class="container">
    <div class="container">
        <canvas id="myChart"></canvas>
    </div>
    <div class="row mt-3">
        <div class="col-12">
            <form>
                <fieldset class="form-group">
                    <legend class="text-center">表示期間</legend>
                    <div class="text-center">
                        <div class="form-check form-check-inline">
                            <label class="form-check-label">
                                <input type="radio" class="form-check-input" name="days" id="term_4"
                                       value="7">
                                １週間分
                            </label>
                        </div>
                        <div class="form-check form-check-inline">
                            <label class="form-check-label">
                                <input type="radio" class="form-check-input" name="days" id="term_3"
                                       value="3">
                                ３日分
                            </label>
                        </div>
                        <div class="form-check form-check-inline">
                            <label class="form-check-label">
                                <input type="radio" class="form-check-input" name="days" id="term_1"
                                       value="1"
                                       checked>
                                １日分
                            </label>
                        </div>
                    </div>
                </fieldset>
            </form>
        </div>
    </div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/locale/ja.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.0/underscore-min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js" charset="utf-8"></script>
<script>

    var chart;

    // グラフ描画
    function drawChart(data) {
        var ctx = document.getElementById("myChart").getContext('2d');

        if (chart) {
            chart.destroy();
        }

        chart = new Chart(ctx, {
            type: 'line',
            data: {

                labels: data[0],
                datasets: [{
                    label: '気温',
                    data: data[1],
                    yAxisID: "y-axis-1",
                    fill: false,
                    backgroundColor: 'rgb(255, 99, 132)',
                    borderColor: 'rgb(255, 99, 132)',
                }, {
                    label: '湿度',
                    data: data[2],
                    yAxisID: "y-axis-2",
                    fill: false,
                    backgroundColor: "rgb(54, 162, 235)",
                    borderColor: "rgb(54, 162, 235)",
                },]
            },
            options: {
                scales: {
                    yAxes: [
                        {
                            // 気温軸（左）
                            id: "y-axis-1",
                            type: "linear",
                            position: "left",
                            ticks: {
                                max: 50,
                                min: -30,
                                stepSize: 10
                            },
                        }, {
                            // 湿度軸（右）
                            id: "y-axis-2",
                            type: "linear",
                            position: "right",
                            ticks: {
                                max: 100,
                                min: 0,
                                stepSize: 25
                            },
                        }],
                    xAxes: [{
                        type: 'time',
                        time: {
                            displayFormats: {
                                hour: 'D日H時'
                            }
                        }
                    }]
                },
                tooltips: {
                    callbacks: {
                        title: function (tooltipItem, data) {
                            return moment(tooltipItem[0].xLabel).format('YYYY-MM-DD HH:mm')
                        }
                    }
                }
            }
        })
    }

    // データ読込
    function reload() {

        // 指定時間を取得
        var days = $('input[name=days]:checked').val();
        // 1日、３日、７日前の時間を設定
        var param = moment().subtract(days, 'days').format('YYYY-MM-DD HH:mm:ss')

        $.getJSON('/api/logs?created_at__gte=' + param)
            .done(function (source) {
                let data = new Array(3);
                data[0] = _.pluck(source, 'created_at');
                data[1] = _.pluck(source, 'temperature');
                data[2] = _.pluck(source, 'humidity');
                drawChart(data);
            })
    };

    $(document).ready(function () {
        // ラジオボタンのイベントリスナー
        $('input[name="days"]:radio').on('change', function () {
            reload();
        });
        // 初期化
        reload();
    });

</script>
</body>
</html>
```

Webアプリケーションを起動して`http://localhost:8000`にアクセスしてください。グラフが表示されれば成功です。表示されているデータはJQueryのAjax処理にてWebAPIより取得されています。以下、利用したライブラリの参考リンク。

グラフ表示 Chart.js
参考：[chart.js入門](https://qiita.com/kazu56/items/17acf7ad2d87d6b28173)

配列処理 underscore.js
参考：[遅すぎたUnderscore.js入門 - 全体像](https://qiita.com/hp0me/items/72d80dc166aace2759dc)

時間処理 moment.js
参考：[Moment.jsを使う](https://qiita.com/osakanafish/items/5ef636bbcb2c3ef94953)

ファイルを更新したら`manage.py runserver`で起動して、ブラウザで`http://localhost:8000`にアクセスしましょう。問題なければグラフが表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/7493c03e-62fd-e72b-ea1a-4686f79877a3.png)


### 手順９．クライアント作成

データの参照の方が完了したので、登録の方を試してみたいと思います。
pythonで以下のサンプルコードを作りました。

IDとPasswordを`createsuperuser`で登録したものに変えて実行してください。
画面をリロードしてデータが追加されていれば成功です。

IDとパスワードは管理アプリケーションから追加したユーザーのものでもＯＫです。herokuなどで利用している場合はurlを変更してください。

```py:data_post.py
import base64
import json
import urllib.request

# 変数は必要に応じて外部化してください。
url = "http://localhost:8000/api/logs/"
method = "POST"
headers = {"Content-Type": "application/json", }

user = "user"
password = "password"

temperature = 20
humidity = 80

# PythonオブジェクトをJSONに変換する
obj = {"temperature": temperature, "humidity": humidity, }
json_data = json.dumps(obj).encode("utf-8")

credentials = ('%s:%s' % (user, password))
encoded_credentials = base64.b64encode(credentials.encode('ascii'))

# httpリクエストを準備してPOST
request = urllib.request.Request(url, data=json_data, method=method, headers=headers)
request.add_header('Authorization', 'Basic %s' % encoded_credentials.decode("ascii"))

with urllib.request.urlopen(request) as response:
    response_body = response.read().decode("utf-8")
```

curlコマンドではこんな感じです。

```curl
curl -v -u user:password -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{"temperature":30, "humidity":54}" http://localhost:8000/api/logs/
```

※WindowsではJson内のダブルクォーテーションをエスケープする必要がある。

```curl
curl -v -u user:password -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{\"temperature\":30, \"humidity\":54}" http://localhost:8000/api/logs/
```


一応Raspberry Pi で動かすことを想定して pythonで作成してみましたが、Basic認証が使えるならどんなツールでも大丈夫のはずです。せっかく作ってみたので私もソニーの[MESH 温度湿度タグ](http://meshprj.com/jp/tag/MESH-100TH-TemperatureAndHumidity.html)でしばらく運用してみます。これは別記事としてまとめたいと思います。