django all-auth でEmailAddress matching query does not exist.が出る

django all-auth EmailAddress matching query does not exist.
django all-auth EmailAddress matching query does not exist.

 

DoesNotExist at /rest-auth/login/

EmailAddress matching query does not exist.

Djangoのall-authを使用してAPI開発をしている時に起こったエラーです。

DjangoのUserモデルは変更していない、きちんとユーザーの登録もしているにも関わらずエラーが発生してしまうということになってしまったのです。

settings.pyの

ACCOUNT_EMAIL_REQUIRED = True

 

をFalseにするとなんとなくわかるのですが、

 

assert (
    not self.AUTHENTICATION_METHOD == self.AuthenticationMethod.EMAIL
) or self.EMAIL_REQUIRED

ここの部分でエラーが発生してしまうのです。

そうこうしていってみていってみると、

これはDjangoのデフォルトで入っているUserモデルの他の部分も使用してUserを管理しているためであることが原因です。

 

具体的なaccount_emialaddressテーブルを見てもらうとわかりますが、Emailはこちらの方でも管理されるようになっています。

そのため、まずここにも認証に使用するEmailが存在することが必要になるのです。

 

次に、ここにメールアドレスが追加されたとしても、アカウント作成時にEmailを使用した2段階認証を設定していると、

verifiedにフラグを立てる(1を設定する)必要があります。

 

 

こうして、実際にログインしてみると、

 

きちんとキーが発行されてくるというわけですね。

 

 

Django created_at などでTruncateをかける方法

DjangoでTruncateをかける方法について解説します。使い方としては、created_atなどのDatetimeFieldを使用している場合で、月別、日別、年別のデータが欲しい時などに使用できるかと思います。こういった時は大体グラフで表示すると思うのですが、今回はグラフ描画の方法については置いておきます。

 

例えば、以下のようにやりたいとします。

{

date: ‘2022年1月1日’,

count: 3,

date: ‘2022年1月2日’,

count: 1,

date: ‘2022年1月3日’,

count: 0,

date: ‘2022年1月4日’,

count: 5,

}

modelの構造が、例えば以下のような形で

class Message(models.Model):
    title = models.CharField(verbose_name='メッセージ件名', null=True, blank=True, max_length=400)
    content = models.TextField(verbose_name='メッセージ本文')
    created_at = models.DateTimeField(verbose_name='登録日', auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name='更新日', auto_now=True)

Messageの件数をcreated_at、つまり登録日でGroup化して取得したいという場合です。

DjangoではTruncというものが用意されている。詳しくは公式ドキュメントを参照してください。

 

さて、こういった状況で、まずは月別データの取得ですが、基本的にはどのTruncを使用する場合も同じです。Model.annotate().values(Trunc).annotate()という記述をすることで目的を達成することができます。

下記に先立ってTruncに使用するものをimportする必要があります。

from django.db.models.functions import TruncMonth, TruncDay, TruncYear

 

  • 日別データの取得
messages = self.queryset.annotate(
            day=TruncDay('created_at')
        ).values('day').annotate(count=Count('pk'))

 

  • 月別データの取得
messages = self.queryset.annotate(
            day= TruncMonth('created_at')
        ).values('day').annotate(count=Count('pk'))

 

  • 年別データの取得
messages = self.queryset.annotate(
            day= TruncYear('created_at')
        ).values('day').annotate(count=Count('pk'))

 

 

このように記述をすると、下記のようにデータを取得することができます(下記は、日別データの取得になります)。

[{"day":"2021年12月30日","count":8},{"day":"2021年12月31日","count":1}]

 

なお、上記はDatetimeFieldをdatetime.datetime.strftimeで文字列に変換しています。

        for message in messages:
            message['day'] = datetime.datetime.strftime(message['day'], '%Y年%m月%d日')

 

グラフにて描画する(実は今回はvuetifyとvue-chartjsを使用しています)ためには、API側で日付の操作をしておいた方が楽なので(フロント側で行ってもいいのですが、Pythonの方が楽なため)上記のような実装になっています。

 

もし誰かのお役に立てれば幸いです。

 

====================================================

株式会社ハードコアでは、営業ツールとして麻波25を開発中です。

こういった機能が欲しいなどご要望があればお気軽にご連絡ください。

営業ツール:麻波25
営業ツール:麻波25

FlutterでPOSTデータを投げてデータ登録する

前回、FlutterでAPIを叩いてデータを取得するという記事を掲載しました。今回は、FlutterでPOSTデータを送信してデータ登録を行うところをやってみたいと思います。

前回のソースからの続きなので、前回分も参照して行われると分かりやすいと思います。

=> 前回の記事はこちらから

 

1.main.dart の変更

まずは、データ登録のための画面遷移を行いたいと思いますので、前回のソース(main.dart)を改修します。

import 'dart:async'; //非同期処理用
import 'dart:convert'; //httpレスポンスをJSON形式に変換用

import 'package:flutter/material.dart'; //google提供のUIデザイン
import 'package:http/http.dart' as http;

// 変更①
import 'post.dart';

void main() {
  runApp(MaterialApp(
    // 変更②router追加
    initialRoute: '/',
    routes: {
      '/': (context) => HomePage(),
      '/postpage': (context) => PostPage(),
    },
  ));
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late Map data;
  late List userData;

  Future getData() async {
    final response =
        await http.get(Uri.parse("<GET API URI>"));

    final body = json.decode(response.body);
    final articles = body.map((dynamic item) => item).toList();
    setState(() {
      userData = articles;
    });
  }

  @override
  void initState() {
    super.initState();
    getData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("sample API"),
          backgroundColor: Colors.green,
        ),
        body: ListView.builder(
          itemCount: userData == null ? 0 : userData.length,
          itemBuilder: (BuildContext context, int index) {
            return Card(
              child: Row(
                children: <Widget>[
                  CircleAvatar(
                    child: Text(
// 変更③
                      "${userData[index]["event_name"]}",
                      style: TextStyle(color: Colors.black),
                    ),
                  ),
 // 変更④
                  Text(
                    "${userData[index]["event_name"]} ${userData[index]["date_and_time"]}",
                    textAlign: TextAlign.center,
                  ),
                  // 変更⑤画面遷移用のボタンを追加
                  RaisedButton(
                    onPressed: () {
                      Navigator.pushNamed(context, '/postpage');
                    },
                    child: Text(
                      '参加する',
                    ),
                    color: Colors.green,
                  ),
                ],
              ),
            );
          },
        ));
  }
}す

少し解説していきます。

 

// 変更① 
import 'post.dart';

この部分は、新たに作成するページ(CLASS)を読み込んでいます。

 

// 変更②router追加 
initialRoute: '/', 
routes: 
{ '/': (context) => HomePage(), 
  '/postpage': (context) => PostPage(), 
}, 
)
);

この部分は見ればなんとなくわかるかと思いますが、routerの役割に変更しました。

 

child: Text(
                      "${userData[index]["event_name"]}",
                      style: TextStyle(color: Colors.black),
                    ),

変更③と④は、出力データを変更しました(見栄えの問題なのであまり気にしなくてもいいかも)。

// 画面遷移用のボタンを追加
                  RaisedButton(
                    onPressed: () {
                      Navigator.pushNamed(context, '/postpage');
                    },
                    child: Text(
                      '参加する',
                    ),
                    color: Colors.green,
                  ),

変更⑤の部分はボタンクリックで画面遷移を行うように変更しました。

ここでちょっとだけ覚えておきたいのは、routerを変更①のように記述した場合には、Navigator.pushではなく、Navigator.pushNamedにする必要があるということです。

 

一旦このようにmain.dartを変更したら、変更①のあたりでエラーが生じていると思います。

それもそのはずで、まだpost.dartにclassを作成していないからです。

それではここかからが本題で、内容に入っていきたいと思います。

 

2.post.dartを作成

まず、post.dartというファイルを作成します。

 

お約束ですが、importするモジュールを呼び出します。

import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;こ

これはmain.dartと一緒です。

 

先にソース全体をお見せしたいと思います。

import 'dart:convert';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class PostPage extends StatelessWidget {
  // Formを使うために必ず必要
  final _formKey = GlobalKey<FormState>();
  // データを取得するために必要
  // String email = '';
  final EmailFieldController = TextEditingController();

  // 画面を作っていきます。
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Post Page'),
        ),
        body: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              TextFormField(
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  filled: true,
                  icon: Icon(Icons.person),
                  hintText: '名前を入力してください。',
                  labelText: '名前',
                ),
                controller: EmailFieldController,
                // validator: (value) {
                //   if (value is Null) {
                //     return '必須です。';
                //   }
                //   return null;
                // },
                // 三項演算子だとこうなる
                validator: (value) =>
                    value is Null || value.isEmpty ? '必須です' : null,
                // onSaved: (value) => () {
                //   // var email = value!;
                //   print('$value');
                // },
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16.0),
                child: RaisedButton(
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      // post APIを投げる
                      final String url = "<YOUR POST API URI>";
                      Map<String, String> headers = {
                        'content-type': 'application/json'
                      };
                      String body =
                          json.encode({'email': EmailFieldController.text});
                      http.Response resp = await http.post(Uri.parse(url),
                          headers: headers, body: body);
                      if (resp.statusCode != 201) {
                        int statusCode = resp.statusCode;
                        var content = "Failed to post $statusCode";
                        return;
                      }
                      var content = resp.body;
                    }
                  },
                  child: Text('Submit'),
                ),
              ),
            ],
          ),
        ));
  }
}

 

初学の頃に陥りやすいところも記述しています。

・フォームの入力値の取得

例えば、

入力値を取得するためにプロパティを設定する以下の部分

// データを取得するために必要
  // String email = '';

こんな感じで記述をすることが多いのではないでしょうか。

これだと入力値を取得することができません。

入力値を取得するためには、

final EmailFieldController = TextEditingController();

と記述して、TextEditingControllerを使用するのです。そうすると、値を取得する時には、

EmailFieldController.text

として、変数名.textで取得することができるようになります。

※ EmailなのにTextEditingControllerで取得しています。ツッコミはあると思いますが、適宜の方法に置き換えてください。

・validation部分の書き方

// validator: (value) {
                //   if (value is Null) {
                //     return '必須です。';
                //   }
                //   return null;
                // },

 

公式サイトでもこのような記述がされています。

ですが、このくらいであれば三項演算子で記述した方がソースが短くて可読性も上がるかと思います。

 

・レスポンスの受け取り

if (resp.statusCode != 201) {
                        int statusCode = resp.statusCode;
                        var content = "Failed to post $statusCode";
                        return;
                      }
                      var content = resp.body;

レスポンスの受け取りでステータスをどのように返却するのかは仕様によって変わってくると思います。その場合は、ステータスコードを書き換える必要があると思います。

 

3.その他注意点

開発するときのバージョンには注意してください。

現在のdart、Flutterのバージョン(2021/6/27)ではNull Safetyにより、以下の書き方はエラーとなります。

validator: (value){
                  if (value.isEmpty){
                    return '必須です';
                  }
                  return null;
                },

この場合、valueがnullの可能性があるのです。そのため、if文の中の条件式を以下のようにする必要があります。

validator: (value) {
                   if (value is Null) {
                     return '必須です。';
                   }
                   return null;
                 },

 

 

同じことがonPressの際にも生じます。

if (_formKey.currentState.validate()) {
                      // post APIを投げる

これだと、_formKey.currentStateでエラーになると思います。

条件式を以下のように変更します。

if (_formKey.currentState!.validate()) {
                      // post APIを投げる

単純に「!」を入れただけですが、これでNull Safetyになるということです。

swiftを経験されたことがあればなんとなくわかるかと思います。

 

 

 

こうすると、うまくいくかと思います。

後は、登録時の挙動(画面遷移など)を記述していけば問題なく出来上がりますね。

 

4.全体APP

こんな感じで画面が出ていればOKかなと思います。

株式会社ハードコアFlutter GETデータ画面
株式会社ハードコアFlutter POSTデータ画面
株式会社ハードコアFlutter POSTデータ画面
株式会社ハードコアFlutter POSTデータ画面

 

 

Flutter を使ったアプリ開発(APIでデータ取得とその表示)

IOS、Androidアプリを一括で開発するという要求に応えてくれるFlutter。

弊社ではこのFlutterを使用してスマホ対応のアプリ開発を行っています。

 

今回は、そんなFlutterを使用したアプリ開発、特にAPIでデータを取得し表示するところ(いわゆるget)の部分をご紹介していきます。

 

まずは、必要なライブラリーを入れていきます。

公式サイトはこちら

 

 

私の場合は、flutterへのパスを通していないので、展開先から呼び出します。

/Users/<USRNAME>/development/flutter/bin/flutter pub add http

pabspec.ymlに以下が追加されていれば成功です。

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  http: ^0.13.3

次に、必要なモジュール群を読み込みます。

import 'package:flutter/material.dart'; 
import 'package:http/http.dart' as http; // API通信
import 'dart:async'; //非同期処理用
import 'dart:convert'; //httpレスポンスをJSON形式に変換用

 

そしたら後は書いていくだけです。今回はmain.dartに記述していきます。

まずは、お決まりの

void main() {
  // main.dartは基本これだけ
  runApp(MaterialApp(
    home: HomePage(),
  ));
}

ルーティングについては別箇所に記述する方法もありますが、今回はAPIを試すだけなので一旦これで。

 

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

void mainで呼ばれているHomePageクラスを定義しています。ここでは更に_HomePageStateを見にいくようにしました。

class _HomePageState extends State<HomePage> {
  late Map data;
  late List userData;

  Future getData() async {
    //Future xxx async{} という記法
    final response =
        await http.get(Uri.parse("<リクエストを投げるURL>"));
    final body = json.decode(response.body);
        // Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'Map<dynamic, dynamic>' エラーに対処
    final articles = body.map((dynamic item) => item).toList();
    setState(() {
      userData = articles;
    });
  }

  // 非同期処理は、デフォルトでは呼び出し元は処理の完了を待ちませんが、
  // await キーワードをつけると完了を待つことができる。
  @override
  void initState() {
    super.initState();
    getData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("sample API"),
          backgroundColor: Colors.green,
        ),
        body: ListView.builder(
          //スクロール可能な可変リストを作る
          itemCount: userData == null ? 0 : userData.length, //受け取る数の定義
          itemBuilder: (BuildContext context, int index) {
            //ここに表示したい内容をindexに応じて表示させる
            return Card(
              //cardデザインを定義:material_design
              child: Row(
                children: <Widget>[
                  CircleAvatar(
                      child: Text(
// 今回はevent_nameとdate_and_timeを表示 ApI responseに応じて変換してください。
                    "${userData[index]["event_name"]} ${userData[index]["date_and_time"]}",
                    style: TextStyle(color: Colors.black),
                  )
                      )
                ],
              ),
            );
          },
        ));
  }
}

 

 

長くなってしまいましたが、こんな感じで進めていきます。

+++ 補足 +++

API開発をしていると、日本語が文字化けをするという問題が生じてしまうことがあります。

この問題を解決するためには、APIレスポンスを受け取る側(今回はFlutter側)で対応するケースと、API側(今回はDjangoのRestFrameWork)で対応するケースとがあります。

Flutter側で対応しようとすると毎度同じようなことでソースを書かないといけないので、API側で対応するのが良いかと思います。

ちなみに、DjangoのRestFrameWorkで日本語も文字化けが起こる時には、以下のように対応します。

from rest_framework.renderers import JSONRenderer

class UTF8CharsetJSONRenderer(JSONRenderer):
    charset = 'utf-8'

こんなファイルをアプリ側などにおき、

settings.pyに以下を追加あるいは変更します。

'DEFAULT_RENDERER_CLASSES': (
    '<YOUR APP PATH>.<上記のファイル名>.UTF8CharsetJSONRenderer',
)

 

これで日本語の文字化けもなく表示されるようになります。

 

 

今回は、FlutterからAPIで情報を取得する方法について書いていきました。

次回は、FlutterからAPI経由でPOSTするケースについて書いていこうと思います。

 

 

 

 

リアルタイムに物体認識を行う。

こんにちは、今日は動画からリアルタイムに物体の認識・検出を行なっていきたいと思います。

 

今回の動画は水中映像です。この水中映像から魚だけを検出させるようにしてみました。

以下の2点については今後の修正次第です。

・カメラから近い魚の認識は比較的できているが、遠いものはできていない

→これは、単純に機械学習させたときの画像ファイルが、遠い魚を除いたものだったからです。そのため、遠い魚は魚ではない旨の判断になっています。解消するためには、きちんと遠い魚の画像も学習させることで検出が可能になります。

 

・近い魚でも検出できていないものがある。

→これは、今回のモデルの正解率が85%-89%と比較的低いものだったから起こった現象です。正解率を向上させることでより判定の精度を上げていくことができるようになります。

 

 

Pythonでは、カメラを起動させたりすることも簡単に行うことができるので(他の言語はあまり知りませんが)、カメラを起動させてリアルタイムで物体の検出を行うことができます。

 

問題はこの先です。

 

物体の検出ができても、「そうなんだ」で終わることがほとんどですが、そこから様々な方向へ展開することができます。

例えば、

・面積を求めて大きさを計算させる

・その物体の個数を計測する

・在庫管理に活かす

などなど考えると枚挙にいとまがないでしょう。

 

 

さて、では今回はこの辺で!

 

【 チャット機能を実装しよう 】Python Djangoでチャットを実装する

おはようございます。

ASUKARUが配信するプログラミング動画学習サイトのASPY(アスピー)に動画が追加されました。今回の動画は、最近流行りのチャットをPythonのWEBフレームワークであるDjangoを使用して作ってしまおう!という動画です。

 

【 動画サイトはこちらから 】

 https://aspy.jp/member/movie/4

 

 

 

DjangoはPythonで書かれているので、Pythonでできる事がほとんどできます。

このモジュールはPythonのWEBSOCKETというソケット通信を行う事で、双方向のチャットを可能にしています。

動画の中では、このチャット機能を使ってテキストマイニングやAIへの繋げ方などは解説をしていませんがこのようなチャット機能を使う事で今後のサービスの拡充にも繋がってくるかと思います。

 

それでは、チャットを実装できるようにみんなでやっていきましょう!

【 Youtube 】

Youtubeにも最初のサンプル動画を上げています。

MacにMysqlclientをインストールする

現在Djangoの開発をしており、ここではWindowsとMacと両方を使用して開発をしているのですが、
今回Macでmysqlclietnをインストールする時に嵌ってしまったので記事にしておきます。

出現エラーは、
(error: command ‘gcc’ failed with exit status 1)
こんな感じでした。

様々試してみたのですが、なかなかうまくいかず、結局公式サイトへ。

 

で、

PyCharmで仮想環境(venv)で作業をしていたので、そのままPyCharmのterminalから、以下のコマンドを

$ echo 'export PATH="/usr/local/opt/mysql-client/bin:$PATH"' >> ~/.bash_profile
$ export PATH="/usr/local/opt/mysql-client/bin:$PATH"
$ pip install mysqlclient

 

 

これで解消できました。

他の記事では、

 

LDFLAGS=-L/usr/local/opt/openssl/lib pip install mysqlclient

 

なども紹介されていますが、でもこれgccエラーではないですね。

かなりmysqlclientについて勉強になりました。まぁ、SSL等も必要ということですね。

では、開発を行なっていきたいと思います。

 

Djangoで開発用サーバーにChromeなどでアクセスできないときの対処方法

こんにちは。

佐賀県はコロナの影響もそれほど大きくはないですが、全国的には大問題になっているところですね。

さて、そんな中ですが、DjangoというPythonでWEBアプリを開発するフレームワークがあるのですが、こちらで開発をしているときに、開発用サーバーにChromeなどでアクセスすることができないということがありましたので解決方法をあげておきたいと思います。

 

たとえば、こんな感じになります。

Djangoの開発用サーバーを立ち上げるコマンドである、

python manage.py runserver

を叩いたときの症状です。

Djangoで開発用サーバーを立ち上げてもブラウザからアクセスできない
Djangoで開発用サーバーを立ち上げてもブラウザからアクセスできない

 

このまま開発用サーバーである、127.0.0.1:8000にChromeなどでアクセスをしても下記の画像の通りにしか表示されないのではないでしょうか。

 

Djangoの開発用サーバーでアクセスしたときのブラウザの表示
Djangoの開発用サーバーでアクセスしたときのブラウザの表示

 

 

これは、Chromeなどではhttpsアクセスが標準化しているためにアクセスができないということになってしまっています。

今のところ、私の手元にあるブラウザではほとんどがアクセスすることができません。

 

というわけで、これをhttps化してしまえばOKということです。

サーバーにデプロイするときには、もちろんSSL証明などを取得してSSL化には対応するかと思います。ここでは、ローカル環境(DockerやVagrantなどを使用しないケース)での話を前提にしております。単純に、これだけのためにDockerの環境を導入するのは・・・と思っていらっしゃる方へ向けての解決策になります。

 

 

【 解決策 】

これを解決するための方法としては単純なんですが、django-sslserverというのをインストールしてあげれば大丈夫です!

 

pip install django-sslserver

 

 

簡単ですね。

 

そして、このライブラリを使うことをDjango側へ教えてあげる必要があります。

 

settings.pyを開いて、INSTALLED_APPSのところに追加してください。

INSTALLED_APPS = [
    'accounts.apps.AccountsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'sslserver', # 追加
]

Djangoではお約束ですね。

 

でもこれだけでは開発用サーバーがSSL化されません。

SSLで開発用サーバーを立ち上げるためには、以下のコマンドでサーバーを立ち上げてください。

 

python manage.py runsslserver

 

これでSSL化されたサーバーを立ち上げることができたかと思います。

Django SSL化を対応
Django SSL化を対応

 

 

ただし、これはあくまで開発用サーバーを立ち上げるときの話なので、本番環境ではきちんとSSL証明書を取得してそれをNginxなりApacheなどに読み込ませてくださいね

 

 

Wagtail PythonでCMSを実装してみる

今回、CMSの調査をしていまして、出てきたWagtailを使ってみました。その際の簡単な説明だけ残しておこうと思います。

まずプロジェクトであがってきたのはDjangoを使用してCMSを搭載させれるかどうかということでした。そのためのパッケージもあるようです。ですが、調べていたらどうやらWagtailの方がいいのかどうかということでいったん使い勝手的なところ調査しました。

以下、説明していきます。

 

1.インストール

まずはインストールからですが、これは問題ないのではないかと思います。ただ、私が勘違いしていたのははじめにDjangoのプロジェクトを設定してからWagtailをいれると思っていましたが、公式ドキュメントをみて「あれ??」ということで単純にPythonの仮想環境を作成してそこにWagtailを落としてきました。

********************

2.プロジェクト作成

次に、プロジェクト作成です。ここでもちょっとはまりました。

こちらのサイトをみていたのですが、

wagtail start mysite mysite

 

これでは作成できませんでした。

以下のようなエラーが返ってきました。

Creating a Wagtail project called cms
CommandError: Destination directory 'C:\cms\cms' does not exist, please create it first.

 

そこで、DjangoでもLaravel でもなんでもそうかと思いますが、使えるコマンドを確認することができるので、試してみることに。

(myvenv) C:\cms>wagtail
Type 'wagtail help <subcommand>' for help on a specific subcommand.

Available subcommands:

    start               Creates the directory structure for a new Wagtail project.
    updatemodulepaths   Update a Wagtail project tree to use Wagtail 2.x module paths

 

きちんと出てきてくれましたね。

プロジェクトを作成するためには、どうやら

wagtail start <プロジェクト名>

 

を叩く必要があるようです。

 

Wagtail_start_command
Wagtail_start_command

すると、写真のようなファイル群が作成されます。

では、ここから様々なことをやっていきたいと思います。

 

って、あれ?フォルダの中身とかほとんどDjangoみたいなものではないですか。

しかもDockerファイルまで生成してくれています。ありがたい。

 

 

いったん中身をいじるのに専念したいので

続きはまた次回。