序章

このチュートリアルでは、DjangoとReactを使用してTo-Doアプリケーションを構築します。

Reactは、SPA(シングルページアプリケーション)を開発するためのJavaScriptフレームワークです。 堅実なドキュメントとその周りの活気に満ちたエコシステムがあります。

Djangoは、Web開発の一般的な方法を簡素化するPythonWebフレームワークです。 Djangoは信頼性が高く、一般的な開発ニーズをサポートする安定したライブラリの活気に満ちたエコシステムも備えています。

このアプリケーションでは、Reactはフロントエンドまたはクライアント側のフレームワークとして機能し、ユーザーインターフェースを処理し、Django RESTフレームワーク(DRF)を使用して構築されたAPIであるDjangoバックエンドへのリクエストを介してデータを取得および設定します。

このチュートリアルを終了すると、完全に機能するアプリケーションができあがります。

注:このチュートリアルのソースコードはGitHubで入手できます。

警告:このチュートリアルで提供されるコードは教育目的であり、本番環境での使用を目的としたものではありません。

このアプリケーションを使用すると、ユーザーはタスクを作成して、それらを完了または未完了としてマークできます。

前提条件

このチュートリアルに従うには、次のことを行う必要があります。

  1. Python3のローカルプログラミング環境をインストールしてセットアップします
  2. Node.jsをインストールし、ローカル開発環境を作成します

このチュートリアルはPythonv3.9.1で検証されました pip v20.2.4、Django v3.1.6、 djangorestframework v3.12.2、 django-cors-headers v3.7.0、ノードv15.8.0、 npm v7.5.4、React v17.0.1、および axios v0.21.0。

ステップ1—バックエンドを設定する

このセクションでは、新しいプロジェクトディレクトリを作成し、Djangoをインストールします。

新しいターミナルウィンドウを開き、次のコマンドを実行して新しいプロジェクトディレクトリを作成します。

  1. mkdir django-todo-react

次に、ディレクトリに移動します。

  1. cd django-todo-react

次に、を使用してPipenvをインストールします pip:

  1. pip install pipenv

注:インストールによっては、次を使用する必要がある場合があります pip3 それ以外の pip.

そして、新しい仮想環境をアクティブ化します。

  1. pipenv shell

Pipenvを使用してDjangoをインストールします。

  1. pipenv install django

次に、という新しいプロジェクトを作成します backend:

  1. django-admin startproject backend

次に、新しく作成されたバックエンドディレクトリに移動します。

  1. cd backend

と呼ばれる新しいアプリケーションを起動します todo:

python manage.py startapp todo

移行を実行します。

python manage.py migrate

そして、サーバーを起動します。

python manage.py runserver

案内する http://localhost:8000 Webブラウザで:

この時点で、Djangoアプリケーションのインスタンスが正常に実行されていることがわかります。 終了したら、サーバーを停止できます(CONTROL+C また CTRL+C).

登録する todo 応用

バックエンドのセットアップが完了したので、登録を開始できます。 todo Djangoが認識できるようにインストールされたアプリとしてのアプリケーション。

を開きます backend/settings.py コードエディタでファイルを作成し、追加します todoINSTALLED_APPS:

backend / settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo',
]

次に、変更を保存します。

の定義 Todo モデル

モデルを作成して、 Todo アイテムはデータベースに保存する必要があります。

を開きます todo/models.py コードエディタでファイルを作成し、次のコード行を追加します。

todo / models.py
from django.db import models

# Create your models here.

class Todo(models.Model):
    title = models.CharField(max_length=120)
    description = models.TextField()
    completed = models.BooleanField(default=False)

    def _str_(self):
        return self.title

上記のコードスニペットは、Todoモデルの3つのプロパティについて説明しています。

  • title
  • description
  • completed

The completed プロパティは、タスクのステータスです。 タスクはいつでも完了するか、完了しないかのいずれかです。 あなたが作成したので Todo モデルの場合、移行ファイルを作成する必要があります。

  1. python manage.py makemigrations todo

そして、変更をデータベースに適用します。

  1. python manage.py migrate todo

CRUD操作がで動作することを確認するためにテストできます Todo Djangoがデフォルトで提供する管理インターフェースを使用して作成したモデル。

を開きます todo/admin.py コードエディタでファイルを作成し、次のコード行を追加します。

todo / admin.py
from django.contrib import admin
from .models import Todo

class TodoAdmin(admin.ModelAdmin):
    list_display = ('title', 'description', 'completed')

# Register your models here.

admin.site.register(Todo, TodoAdmin)

次に、変更を保存します。

管理者インターフェースにアクセスするには、「スーパーユーザー」アカウントを作成する必要があります。 ターミナルで次のコマンドを実行します。

  1. python manage.py createsuperuser

スーパーユーザーのユーザー名、電子メール、およびパスワードを入力するように求められます。 管理ダッシュボードにログインするために必要になるため、覚えておくことができる詳細を必ず入力してください。

サーバーをもう一度起動します。

  1. python manage.py runserver

案内する http://localhost:8000/admin Webブラウザで。 そして、以前に作成したユーザー名とパスワードでログインします。

作成、編集、および削除できます Todo このインターフェースを使用するアイテム:

このインターフェースを試した後、サーバーを停止できます(CONTROL+C また CTRL+C).

ステップ2—APIを設定する

このセクションでは、DjangoRESTフレームワークを使用してAPIを作成します。

をインストールします djangorestframeworkdjango-cors-headers Pipenvの使用:

  1. pipenv install djangorestframework django-cors-headers

追加する必要があります rest_frameworkcorsheaders インストールされているアプリケーションのリストに移動します。 を開きます backend/settings.py コードエディタでファイルを作成し、 INSTALLED_APPSMIDDLEWARE セクション:

backend / settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'rest_framework',
    'todo',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
]

次に、これらのコード行をの下部に追加します backend/settings.py ファイル:

backend / settings.py
CORS_ORIGIN_WHITELIST = [
     'http://localhost:3000'
]

django-cors-headers は、CORSルールが原因で通常発生するエラーを防ぐPythonライブラリです。 の中に CORS_ORIGIN_WHITELIST コード、ホワイトリストに登録しました localhost:3000 アプリケーションのフロントエンド(そのポートで提供される)がAPIと対話するようにするためです。

作成 serializers

フロントエンドが受信したデータを処理できるように、モデルインスタンスをJSONに変換するシリアライザーが必要になります。

作成する todo/serializers.py コードエディタでファイルします。 を開きます serializers.py ファイルを作成し、次のコード行で更新します。

todo / serializers.py
from rest_framework import serializers
from .models import Todo

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'description', 'completed')

このコードは、操作するモデルとJSONに変換するフィールドを指定します。

ビューの作成

を作成する必要があります TodoView のクラス todo/views.py ファイル。

を開きます todo/views.py コードエディタでファイルを作成し、次のコード行を追加します。

todo / views.py
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import TodoSerializer
from .models import Todo

# Create your views here.

class TodoView(viewsets.ModelViewSet):
    serializer_class = TodoSerializer
    queryset = Todo.objects.all()

The viewsets 基本クラスは、デフォルトでCRUD操作の実装を提供します。 このコードは、 serializer_class そしてその queryset.

を開きます backend/urls.py コードエディタでファイルを作成し、内容を次のコード行に置き換えます。

backend / urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from todo import views

router = routers.DefaultRouter()
router.register(r'todos', views.TodoView, 'todo')

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

このコードは、APIのURLパスを指定します。 これは、APIの構築を完了する最後のステップでした。

これで、CRUD操作を実行できます。 Todo モデル。 routerクラスを使用すると、次のクエリを実行できます。

  • /todos/ -すべてのリストを返します Todo アイテム。 CREATEREAD ここで操作を実行できます。
  • /todos/id -シングルを返します Todo を使用したアイテム id 主キー。 UPDATEDELETE ここで操作を実行できます。

サーバーを再起動しましょう:

  1. python manage.py runserver

案内する http://localhost:8000/api/todos Webブラウザで:

あなたはできる CREATE インターフェイスを使用した新しいTodoアイテム:

Todoアイテムが正常に作成されると、正常な応答が表示されます。

実行することもできます DELETEUPDATE 特定の操作 Todo を使用したアイテム id 主キー。 アドレス構造を使用する /api/todos/{id} と提供します id.

追加 1 URLに移動して、Todoアイテムを調べます。 id 「1」の。 案内する http://localhost:8000/api/todos/1 Webブラウザで:

これで、アプリケーションのバックエンドの構築が完了しました。

ステップ3—フロントエンドの設定

アプリケーションのバックエンドが完成したので、フロントエンドを作成し、作成したインターフェイスを介してバックエンドと通信させることができます。

まず、新しいターミナルウィンドウを開き、に移動します django-todo-react プロジェクトディレクトリ。

フロントエンドを設定するために、このチュートリアルはCreateReactAppに依存します。 使用するにはいくつかのアプローチがあります create-react-app. 1つのアプローチは使用することです npx パッケージを実行してプロジェクトを作成するには:

  1. npx create-react-app frontend

Create React Appを使用してReactプロジェクトをセットアップする方法を読むと、このアプローチの詳細を学ぶことができます。

プロジェクトが作成されたら、新しく作成されたものに変更できます frontend ディレクトリ:

  1. cd frontend

次に、アプリケーションを起動します。

  1. npm start

Webブラウザが開きます http://localhost:3000 デフォルトのCreateReactApp画面が表示されます。

次に、インストールします bootstrapreactstrap ユーザーインターフェイスツールを提供します。

  1. npm install bootstrap@4.6.0 reactstrap@8.9.0 --legacy-peer-deps

注:遭遇する可能性があります unable to resolve dependency tree React、Bootstrap、およびReactstrapのバージョンに応じてエラーが発生します。

改訂時の最新バージョン popper.js 非推奨になり、React17+と競合します。 これは既知の問題であり、 --legacy-peer-deps インストール時のオプション。

開ける index.js コードエディタで追加します bootstrap.min.css:

フロントエンド/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

この手順で問題が発生した場合は、ブートストラップを追加するための公式ドキュメントを参照してください。

開ける App.js コードエディタで、次のコード行を追加します。

フロントエンド/src/App.js
import React, { Component } from "react";

const todoItems = [
  {
    id: 1,
    title: "Go to Market",
    description: "Buy ingredients to prepare dinner",
    completed: true,
  },
  {
    id: 2,
    title: "Study",
    description: "Read Algebra and History textbook for the upcoming test",
    completed: false,
  },
  {
    id: 3,
    title: "Sammy's books",
    description: "Go to library to return Sammy's books",
    completed: true,
  },
  {
    id: 4,
    title: "Article",
    description: "Write article on how to use Django with React",
    completed: false,
  },
];

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewCompleted: false,
      todoList: todoItems,
    };
  }

  displayCompleted = (status) => {
    if (status) {
      return this.setState({ viewCompleted: true });
    }

    return this.setState({ viewCompleted: false });
  };

  renderTabList = () => {
    return (
      <div className="nav nav-tabs">
        <span
          className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
          onClick={() => this.displayCompleted(true)}
        >
          Complete
        </span>
        <span
          className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
          onClick={() => this.displayCompleted(false)}
        >
          Incomplete
        </span>
      </div>
    );
  };

  renderItems = () => {
    const { viewCompleted } = this.state;
    const newItems = this.state.todoList.filter(
      (item) => item.completed == viewCompleted
    );

    return newItems.map((item) => (
      <li
        key={item.id}
        className="list-group-item d-flex justify-content-between align-items-center"
      >
        <span
          className={`todo-title mr-2 ${
            this.state.viewCompleted ? "completed-todo" : ""
          }`}
          title={item.description}
        >
          {item.title}
        </span>
        <span>
          <button
            className="btn btn-secondary mr-2"
          >
            Edit
          </button>
          <button
            className="btn btn-danger"
          >
            Delete
          </button>
        </span>
      </li>
    ));
  };

  render() {
    return (
      <main className="container">
        <h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
        <div className="row">
          <div className="col-md-6 col-sm-10 mx-auto p-0">
            <div className="card p-3">
              <div className="mb-4">
                <button
                  className="btn btn-primary"
                >
                  Add task
                </button>
              </div>
              {this.renderTabList()}
              <ul className="list-group list-group-flush border-top-0">
                {this.renderItems()}
              </ul>
            </div>
          </div>
        </div>
      </main>
    );
  }
}

export default App;

このコードには、4つのアイテムのハードコードされた値が含まれています。 これらは、アイテムがバックエンドからフェッチされるまでの一時的な値になります。

The renderTabList() 関数は、表示されるアイテムのセットを制御するのに役立つ2つのスパンをレンダリングします。 完了タブをクリックすると、完了したタスクが表示されます。 不完全タブをクリックすると、不完全なタスクが表示されます。

変更を保存し、Webブラウザでアプリケーションを確認します。

タスクの追加や編集などのアクションを処理するには、モーダルコンポーネントを作成する必要があります。

まず、作成します components のフォルダ src ディレクトリ:

  1. mkdir src/components

次に、を作成します Modal.js ファイルを作成し、コードエディタで開きます。 次のコード行を追加します。

フロントエンド/src/components/Modal.js
import React, { Component } from "react";
import {
  Button,
  Modal,
  ModalHeader,
  ModalBody,
  ModalFooter,
  Form,
  FormGroup,
  Input,
  Label,
} from "reactstrap";

export default class CustomModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeItem: this.props.activeItem,
    };
  }

  handleChange = (e) => {
    let { name, value } = e.target;

    if (e.target.type === "checkbox") {
      value = e.target.checked;
    }

    const activeItem = { ...this.state.activeItem, [name]: value };

    this.setState({ activeItem });
  };

  render() {
    const { toggle, onSave } = this.props;

    return (
      <Modal isOpen={true} toggle={toggle}>
        <ModalHeader toggle={toggle}>Todo Item</ModalHeader>
        <ModalBody>
          <Form>
            <FormGroup>
              <Label for="todo-title">Title</Label>
              <Input
                type="text"
                id="todo-title"
                name="title"
                value={this.state.activeItem.title}
                onChange={this.handleChange}
                placeholder="Enter Todo Title"
              />
            </FormGroup>
            <FormGroup>
              <Label for="todo-description">Description</Label>
              <Input
                type="text"
                id="todo-description"
                name="description"
                value={this.state.activeItem.description}
                onChange={this.handleChange}
                placeholder="Enter Todo description"
              />
            </FormGroup>
            <FormGroup check>
              <Label check>
                <Input
                  type="checkbox"
                  name="completed"
                  checked={this.state.activeItem.completed}
                  onChange={this.handleChange}
                />
                Completed
              </Label>
            </FormGroup>
          </Form>
        </ModalBody>
        <ModalFooter>
          <Button
            color="success"
            onClick={() => onSave(this.state.activeItem)}
          >
            Save
          </Button>
        </ModalFooter>
      </Modal>
    );
  }
}

このコードは CustomModal クラスとそれはから派生したモーダルコンポーネントをネストします reactstrap 図書館。

このコードは、次の形式で3つのフィールドも定義しました。

  • title
  • description
  • completed

これらは、バックエンドのTodoモデルのプロパティとして定義したものと同じフィールドです。

The CustomModal 受け取る activeItem, toggle、 と onSave 小道具として:

  1. activeItem 編集するTodoアイテムを表します。
  2. toggle モーダルの状態を制御するために使用される関数です(つまり、モーダルを開いたり閉じたりします)。
  3. onSave Todoアイテムの編集された値を保存するために呼び出される関数です。

次に、をインポートします CustomModal コンポーネントを App.js ファイル。

再訪 src/App.js コードエディタでファイルを作成し、内容全体を次のコード行に置き換えます。

フロントエンド/src/App.js
import React, { Component } from "react";
import Modal from "./components/Modal";

const todoItems = [
  {
    id: 1,
    title: "Go to Market",
    description: "Buy ingredients to prepare dinner",
    completed: true,
  },
  {
    id: 2,
    title: "Study",
    description: "Read Algebra and History textbook for the upcoming test",
    completed: false,
  },
  {
    id: 3,
    title: "Sammy's books",
    description: "Go to library to return Sammy's books",
    completed: true,
  },
  {
    id: 4,
    title: "Article",
    description: "Write article on how to use Django with React",
    completed: false,
  },
];

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewCompleted: false,
      todoList: todoItems,
      modal: false,
      activeItem: {
        title: "",
        description: "",
        completed: false,
      },
    };
  }

  toggle = () => {
    this.setState({ modal: !this.state.modal });
  };

  handleSubmit = (item) => {
    this.toggle();

    alert("save" + JSON.stringify(item));
  };

  handleDelete = (item) => {
    alert("delete" + JSON.stringify(item));
  };

  createItem = () => {
    const item = { title: "", description: "", completed: false };

    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  editItem = (item) => {
    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  displayCompleted = (status) => {
    if (status) {
      return this.setState({ viewCompleted: true });
    }

    return this.setState({ viewCompleted: false });
  };

  renderTabList = () => {
    return (
      <div className="nav nav-tabs">
        <span
          className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
          onClick={() => this.displayCompleted(true)}
        >
          Complete
        </span>
        <span
          className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
          onClick={() => this.displayCompleted(false)}
        >
          Incomplete
        </span>
      </div>
    );
  };

  renderItems = () => {
    const { viewCompleted } = this.state;
    const newItems = this.state.todoList.filter(
      (item) => item.completed === viewCompleted
    );

    return newItems.map((item) => (
      <li
        key={item.id}
        className="list-group-item d-flex justify-content-between align-items-center"
      >
        <span
          className={`todo-title mr-2 ${
            this.state.viewCompleted ? "completed-todo" : ""
          }`}
          title={item.description}
        >
          {item.title}
        </span>
        <span>
          <button
            className="btn btn-secondary mr-2"
            onClick={() => this.editItem(item)}
          >
            Edit
          </button>
          <button
            className="btn btn-danger"
            onClick={() => this.handleDelete(item)}
          >
            Delete
          </button>
        </span>
      </li>
    ));
  };

  render() {
    return (
      <main className="container">
        <h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
        <div className="row">
          <div className="col-md-6 col-sm-10 mx-auto p-0">
            <div className="card p-3">
              <div className="mb-4">
                <button
                  className="btn btn-primary"
                  onClick={this.createItem}
                >
                  Add task
                </button>
              </div>
              {this.renderTabList()}
              <ul className="list-group list-group-flush border-top-0">
                {this.renderItems()}
              </ul>
            </div>
          </div>
        </div>
        {this.state.modal ? (
          <Modal
            activeItem={this.state.activeItem}
            toggle={this.toggle}
            onSave={this.handleSubmit}
          />
        ) : null}
      </main>
    );
  }
}

export default App;

変更を保存し、Webブラウザでアプリケーションを確認します。

編集して保存しようとした場合 Todo アイテム、あなたはを表示するアラートを受け取ります Todo アイテムのオブジェクト。 保存または削除をクリックすると、 Todo アイテム。

注::ReactおよびReactstrapのバージョンによっては、コンソールエラーが発生する場合があります。 改訂時、 Warning: Legacy context API has been detected within a strict-mode tree.Warning: findDOMNode is deprecated in StrictMode. 既知問題です。

次に、前のセクションで作成したDjangoAPIと相互作用するようにアプリケーションを変更します。 最初のターミナルウィンドウに戻り、サーバーが実行されていることを確認します。 実行されていない場合は、次のコマンドを使用します。

  1. python manage.py runserver

注:このターミナルウィンドウを閉じた場合は、に移動する必要があることに注意してください。 backend ディレクトリを作成し、仮想Pipenvシェルを使用します。

バックエンドサーバー上のAPIエンドポイントにリクエストを送信するには、次のJavaScriptライブラリをインストールします。 axios.

2番目のターミナルウィンドウで、 frontend ディレクトリとインストール axios:

  1. npm install axios@0.21.1

次に、 frontend/package.json コードエディタでファイルを作成し、 proxy:

フロントエンド/package.json
[...]
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:8000",
  "dependencies": {
    "axios": "^0.18.0",
    "bootstrap": "^4.1.3",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "react-scripts": "2.0.5",
    "reactstrap": "^6.5.0"
  },
[...]

プロキシは、APIリクエストをトンネリングするのに役立ちます http://localhost:8000 Djangoアプリケーションがそれらを処理する場所。 これなしで proxy、フルパスを指定する必要があります。

axios.get("http://localhost:8000/api/todos/")

proxy、相対パスを指定できます。

axios.get("/api/todos/")

注:プロキシをアプリケーションに登録するには、開発サーバーを再起動する必要がある場合があります。

再訪 frontend/src/App.js ファイルを作成し、コードエディタで開きます。 このステップでは、ハードコードされたものを削除します todoItems バックエンドサーバーへのリクエストからのデータを使用します。 handleSubmithandleDelete

を開きます App.js ファイルを作成し、次の最終バージョンに置き換えます。

フロントエンド/src/App.js
import React, { Component } from "react";
import Modal from "./components/Modal";
import axios from "axios";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewCompleted: false,
      todoList: [],
      modal: false,
      activeItem: {
        title: "",
        description: "",
        completed: false,
      },
    };
  }

  componentDidMount() {
    this.refreshList();
  }

  refreshList = () => {
    axios
      .get("/api/todos/")
      .then((res) => this.setState({ todoList: res.data }))
      .catch((err) => console.log(err));
  };

  toggle = () => {
    this.setState({ modal: !this.state.modal });
  };

  handleSubmit = (item) => {
    this.toggle();

    if (item.id) {
      axios
        .put(`/api/todos/${item.id}/`, item)
        .then((res) => this.refreshList());
      return;
    }
    axios
      .post("/api/todos/", item)
      .then((res) => this.refreshList());
  };

  handleDelete = (item) => {
    axios
      .delete(`/api/todos/${item.id}/`)
      .then((res) => this.refreshList());
  };

  createItem = () => {
    const item = { title: "", description: "", completed: false };

    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  editItem = (item) => {
    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  displayCompleted = (status) => {
    if (status) {
      return this.setState({ viewCompleted: true });
    }

    return this.setState({ viewCompleted: false });
  };

  renderTabList = () => {
    return (
      <div className="nav nav-tabs">
        <span
          onClick={() => this.displayCompleted(true)}
          className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
        >
          Complete
        </span>
        <span
          onClick={() => this.displayCompleted(false)}
          className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
        >
          Incomplete
        </span>
      </div>
    );
  };

  renderItems = () => {
    const { viewCompleted } = this.state;
    const newItems = this.state.todoList.filter(
      (item) => item.completed === viewCompleted
    );

    return newItems.map((item) => (
      <li
        key={item.id}
        className="list-group-item d-flex justify-content-between align-items-center"
      >
        <span
          className={`todo-title mr-2 ${
            this.state.viewCompleted ? "completed-todo" : ""
          }`}
          title={item.description}
        >
          {item.title}
        </span>
        <span>
          <button
            className="btn btn-secondary mr-2"
            onClick={() => this.editItem(item)}
          >
            Edit
          </button>
          <button
            className="btn btn-danger"
            onClick={() => this.handleDelete(item)}
          >
            Delete
          </button>
        </span>
      </li>
    ));
  };

  render() {
    return (
      <main className="container">
        <h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
        <div className="row">
          <div className="col-md-6 col-sm-10 mx-auto p-0">
            <div className="card p-3">
              <div className="mb-4">
                <button
                  className="btn btn-primary"
                  onClick={this.createItem}
                >
                  Add task
                </button>
              </div>
              {this.renderTabList()}
              <ul className="list-group list-group-flush border-top-0">
                {this.renderItems()}
              </ul>
            </div>
          </div>
        </div>
        {this.state.modal ? (
          <Modal
            activeItem={this.state.activeItem}
            toggle={this.toggle}
            onSave={this.handleSubmit}
          />
        ) : null}
      </main>
    );
  }
}

export default App;

The refreshList() APIリクエストが完了するたびに呼び出される関数は再利用可能です。 Todoリストを更新して、追加されたアイテムの最新のリストを表示します。

The handleSubmit() 関数は、作成操作と更新操作の両方を処理します。 パラメータとして渡されたアイテムに id、それからそれはおそらく作成されていないので、関数はそれを作成します。

この時点で、バックエンドサーバーが最初のターミナルウィンドウで実行されていることを確認します。

  1. python manage.py runserver

注:このターミナルウィンドウを閉じた場合は、に移動する必要があることに注意してください。 backend ディレクトリを作成し、仮想Pipenvシェルを使用します。

そして、2番目のターミナルウィンドウで、 frontend ディレクトリを作成し、フロントエンドアプリケーションを起動します。

  1. npm start

今あなたが訪問するとき http://localhost:3000 あなたのウェブブラウザで、あなたのアプリケーションはあなたがすることを可能にします READ, CREATE, UPDATE、 と DELETE タスク。

これで、Todoアプリケーションのフロントエンドとバックエンドが完成しました。

結論

この記事では、DjangoとReactを使用してTo-Doアプリケーションを構築しました。 あなたはこれを djangorestframework, django-cors-headers, axios, bootstrap、 と reactstrap ライブラリ。

Djangoについて詳しく知りたい場合は、Djangoトピックページで演習とプログラミングプロジェクトを確認してください。

Reactの詳細については、 React.js シリーズのコーディング方法をご覧になるか、Reactトピックページで演習やプログラミングプロジェクトを確認してください。