doramochi blog

doramochi blog

趣味で学んだプログラミングの知識を綴っていきます

DjangoとReactを使ったアプリケーションを作ってみる③(動画一覧を表示)

間が空いてしまったが、練習として色々作ってみる。
今回は動画の一覧を表示してみる。

準備編:
DjangoとReactを使ったアプリケーションを作ってみる①(Django側の準備) - doramochi blog
DjangoとReactを使ったアプリケーションを作ってみる②(React側の準備) - doramochi blog

素材の準備

まずは練習に使用する動画と画像をダウンロード。
今回はNHKクリエイティブ・ライブラリーの素材を使用させていただく。
publicフォルダの配下に「video」「img」フォルダを作成。

適当に画像と動画をダウンロードして各フォルダに配置する。


モデル修正とDBの準備

前回適当に値を入れてしまったので素材に合わせてデータを追加する。
また、前回作成したモデルにはサムネイル画像のパスを登録する項目がなかったので追加する。

モデルの修正

models.pyを下記のように修正する。
「path」としていた部分を「video_path」に修正。
さらに「img_path」も追加する。
修正が完了したら「makemigrations」「migrate」コマンドを実行してDBへ反映。

from django.conf import settings
from django.db import models
from django.utils import timezone

class Video(models.Model):
    title = models.CharField(max_length=30)
    description = models.TextField(max_length=300)
    video_path = models.CharField(max_length=60)
    img_path = models.CharField(max_length=60)
    datetime = models.DateTimeField(blank=False, null=False)
    number_of_views = models.IntegerField(blank=True, default=0)

合わせてserialize.pyも修正しておく。

from rest_framework import serializers
from .models import Video

class VideoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Video
        fields = ('id',
                  'title',
                  'description',
                  'video_path',
                  'img_path',
                  'datetime',
                  'number_of_views')

DBの準備

下記のようにデータを登録する。

フロントエンド側の修正

Material UIのインストール

今回はMaterial UIのCardを使用してみる。
mui.com
そのため、まずはMaterial UIのインストール。

npm install @mui/material @emotion/react @emotion/styled

Component作成

上記カードを1つのComponentとして作成する。
下記のように「src」の配下に「\components」フォルダ。「\components」フォルダの配下に「VideoCard」フォルダを作成。
「VideoCard」フォルダ配下に「VideoCard.js」ファイルを作成する。

Material UIのサンプルに記載されているコードをとりあえずコピペ。

必要な部分を変更していく。
修正点は以下の通り。
・画像のパスを変更
・DBから取得した動画タイトルを表示
・DBから取得したdescriptionを表示
・不要な記述削除
DBからの値はpropsで受け取るようにする。
また、画像ファイルは

{`${process.env.PUBLIC_URL}/img/${props.img_path}`}

と指定することでpublic配下のファイルを参照することが出来る。
今回はpublicの下にimgフォルダを作成しているので上記の様にしている。

動画や画像はpublic以外に配置することもできるが、ビルドの対象となってしまうので注意が必要。
数が多くなったり、サイズが大きい場合は「npm start」や「npm run build」などを実行したときにメモリ不足でエラーとなる場合がある。

修正したコードは以下の通り。

import * as React from 'react';
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';

const VideoCard = (props) => {
  return (
    <Card sx={{ maxWidth: 345 }}>
      <CardMedia
        component="img"
        height="140"
        image={`${process.env.PUBLIC_URL}/img/${props.img_path}`}
        alt="test"
      />
      <CardContent>
        <Typography gutterBottom variant="h5" component="div">
        {props.title}
        </Typography>
        <Typography variant="body2" color="text.secondary">
        {props.description}
        </Typography>
      </CardContent>
    </Card>
  );
};

export default VideoCard;

App.jsの修正

作成したVideoCard Componentを呼び出す。
Componentの呼び出しはImportしてdivなどと同じように呼び出すだけ。

import VideoCard from './components/VideoCard/VideoCard';

function App() {
  return (
    <div>
      <VideoCard />
    </div>
  );
}

今回は引数で動画の情報を渡すので以下のように修正する。

import './App.css';
import axios from 'axios';
import React, { useState, useEffect } from 'react';
import VideoCard from './components/VideoCard/VideoCard';

function App() {
  const [posts, setPosts] = useState([])

  useEffect(() => {
    axios
    .get('http://localhost:8000/api/home/')
    .then(res=>{setPosts(res.data);})
    .catch(err=>{console.log(err);});
  }, []);

  return (
    <div className="App">
      <h1>動画サイト</h1>
      {posts.map(post => (
        <div>
        <VideoCard title={post.title} img_path={post.img_path} video_path={post.video_path} description={post.description}/>
        </div>
      ))}
    </div>
  );
}

export default App;

コード修正後、DjangoとReactを起動してhttp://localhost:3000/へアクセスすると下記のように表示される。

DjangoとReactを使ったアプリケーションを作ってみる②(React側の準備)

前回の続き。今回はReact側の準備を行っていく。
React初心者のため、とりあえずDjango側からデータを受け取るところまでの作成を目標とする。

Django側の準備:
Djangoを使ってみる①(環境構築~プロジェクト作成) - doramochi blog

Reactプロジェクトの作成

Node.jsはインストール済みの状態からスタートのため、手順は省略。
下記の様なコマンドを実行し、バージョンが表示されればインストール不要。

インストールされていない場合は、下記からダウンロード可
nodejs.org

django_react」フォルダ直下にて下記コマンドを実施。
create-react-appがインストールされていない場合、インストールするかどうかを聞かれるので「y」を押して実行。

npx create-react-app frontend

問題がなければ、下記のようにプロジェクトの作成が実行される。


インストールが終わると下記の様な構成になっている。

作成されたフォルダへ移動し、サーバを起動

cd frontend
npm start

http://localhost:3000にアクセスして、下記のようなページが表示されればOK。

frontend/src/App.jsの編集

今回はaxiosというパッケージを使用するのでインストールしておく。

npm install axios


ソースコードを見てみると、App.jsというファイルに先ほどのページの描画処理が記述されているのでとりあえずこれを編集してみる。
データを取得し、postsに格納。postsの中身を展開して表示という処理。

import './App.css';
import axios from 'axios';
import React, { useState, useEffect } from 'react';

function App() {
  const [posts, setPosts] = useState([])

  useEffect(() => {
    axios
    .get('http://localhost:8000/api/home/')
    .then(res=>{setPosts(res.data);})
    .catch(err=>{console.log(err);});
  }, []);

  return (
    <div className="App">
      {posts.map(post => (
        <div key={post.id}>
          <h1>{post.title}</h1>
          <p>{post.description}</p>
        </div>
      ))}
    </div>
  );
}

export default App;


再度React側、Django側両方のサーバーを起動し、http://localhost:3000/へアクセス。
下記のように表示されれば無事連携完了。

次回からはReactを勉強して体裁を整えていく。

DjangoとReactを使ったアプリケーションを作ってみる①(Django側の準備)

基本的な環境構築は以前まとめているので省略。
今回はReactとの接続において必要な処理をメインに記載していく。

環境構築参考:
Djangoを使ってみる①(環境構築~プロジェクト作成) - doramochi blog
Djangoを使ってみる②(アプリケーションの作成) - doramochi blog

プロジェクト作成~起動確認

今回は「django_react」というプロジェクトを作成し、「home」というアプリケーションを作成。
python manage.py runserver 0.0.0.0:8000」など適当なコマンドを実行し、下記画面が起動することを確認する。

パッケージのインストール

djangorestframework、django-cors-headersというパッケージをインストールしておく

conda install -c conda-forge djangorestframework
conda install -c conda-forge django-cors-headers

モデルの作成

今回はYoutubeみたいなものを作ってみようと思い、とりあえず下記の様なモデルを作成しておく。

class Video(models.Model):
    title = models.CharField(max_length=30)
    description = models.TextField(max_length=300)
    path = models.CharField(max_length=60)
    datetime = models.DateTimeField(blank=False, null=False)
    number_of_views = models.IntegerField(blank=True, default=0)

マイグレーション実行。

python manage.py makemigrations home
python manage.py migrate

動作確認用のサンプルデータとして1つデータを追加しておく。
今回は管理画面を使わず、DB Browser for SQLiteを使用してDBを編集。


django_react/settings.pyの編集

settings.pyの「INSTALLED_APPS 」の部分に下記を追加。
合わせて「CORS_ORIGIN_WHITELIST」も追加。
CORSに関しては
なんとなく CORS がわかる...はもう終わりにする。 - Qiita
などを参照。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework', #追加
    'corsheaders', #追加
    'home', #追加
]
 #追加
CORS_ORIGIN_WHITELIST = (
    'http://localhost:3000',
)

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', #追加
]

django_react/urls.pyの編集

homeのurls.pyを参照するように下記を追記。

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/home/', include('home.urls')), #追加
]

home/urls.pyの編集

とりあえずviews.pyの「ListVideo」を参照するようにしておく。

urlpatterns = [
    path('', views.ListVideo.as_view()) #追加
]

home/serializer.pyの作成

DBのデータをJSON形式に変換するためのクラスを追加しておく。

from rest_framework import serializers
from .models import Video

class VideoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Video
        fields = ('id',
                  'title',
                  'description',
                  'path',
                  'datetime',
                  'number_of_views')

home/views.pyの作成

ListVideoクラスを作成。
モデルの中身をすべて取得し、idが指定された場合クエリを実行しフィルタリング。
その結果をJSON形式で返すという処理を記載。

from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Video
from home.serializer import VideoSerializer

class ListVideo(APIView):
    def get(self, request):
        video = Video.objects.all()
        req_id = self.request.query_params.get('id')
        if req_id:
            video = video.filter(id=req_id)
        serializer = VideoSerializer(video, many=True)

        return Response(serializer.data)

動作確認

サーバー起動後、「http://localhost:8000/api/home/」にアクセスし、下記のような画面が表示されればDjango側の準備は一旦終わり。

IDを指定した場合、下記のようになる。
http://localhost:8000/home/?id=0にアクセスした場合

http://localhost:8000/api/home/?id=1にアクセスした場合

DataTablesとDjango(model)の連携

前回(Djangoを使ってみる⑤(DBの中身を表示) - doramochi blog)DBの中身を表示してみたが、あのままでは少し味気ないので他の方法を試してみる。

DataTablesとDjango(model)の連携

DataTablesについて

今回使用するDataTables:DataTables | Table plug-in for jQuery

・ページング機能
・ソート機能
・検索機能
などが搭載されたテーブルを手軽に使うことが出来る。

データ量が多い場合はServer-side processingを使うと良い。
今回データ量は少ないがServer-side processingを使ってやってみる。

django-datatables-viewのインストール

DjangoでServer Side Processingする場合は、 django-datatables-viewを使う。下記コマンドでインストールしておく。

pip install django-datatables-view

HTMLテンプレートの作成

必要な処理は以下の通り
①DataTablesを使うのに必要なファイルを読み込む

<link rel="stylesheet" href="https://cdn.datatables.net/t/bs-3.3.6/jqc-1.12.0,dt-1.10.11/datatables.min.css"/>
    <script src="https://cdn.datatables.net/t/bs-3.3.6/jqc-1.12.0,dt-1.10.11/datatables.min.js"></script>

②テーブルを作成する

<table id="book" class="table table-bordered">
        <thead>
            <tr><th>本の名前</th>
                <th>作者</th>
                <th>出版社</th>
                <th>価格</th>
            </tr>
        </thead>
    </table>

③作成したテーブルにDataTablesを適用する

<script>
    $('#book').DataTable({
        オプションを記載
     });
</script>

オプションは公式に詳しく載っているので必要なものを記載しておく。
オプション:https://datatables.net/reference/option/

今回はServer-side processing(ajax)を使用するので下記のように設定する。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>書籍一覧</title>
    <link rel="stylesheet" href="https://cdn.datatables.net/t/bs-3.3.6/jqc-1.12.0,dt-1.10.11/datatables.min.css"/>
    <script src="https://cdn.datatables.net/t/bs-3.3.6/jqc-1.12.0,dt-1.10.11/datatables.min.js"></script>
</head>
<body>
    <table id="book" class="table table-bordered">
        <thead>
            <tr><th>本の名前</th>
                <th>作者</th>
                <th>出版社</th>
                <th>価格</th>
            </tr>
        </thead>
    </table>
<script>
    $('#book').DataTable({
        deferRender: true,
        serverSide: true,
        processing: true,
        orderCellsTop: true,
        responsive: true,
        ajax: {
          "url": "/data/",
          "type": "GET",
        },
        columnDefs: [
          {targets: 0, data: 'book_name'},
          {targets: 1,  data: 'author' },
          {targets: 2,  data: 'publisher' },
          {targets: 3,  data: 'price' },
        ]
    });
</script>
</body>
</html>

views.pyの編集

先ほど設定したテーブルにmodelのデータを渡すための処理を追加する。
これまでと違い「class」にしなければいけないので注意。

from django_datatables_view.base_datatable_view import BaseDatatableView

class DataTableView(BaseDatatableView):
    # モデルの指定
    model = book_list
    # 表示するフィールドの指定
    columns = ['book_name', 'author', 'publisher', 'price']

    # 検索方法の指定:部分一致
    def get_filter_method(self):
        return super().FILTER_ICONTAINS

ソース全体はこちらを参照:
https://github.com/doramochi00/django/blob/2e1fb25380f567f96e764a91cdbec71b4282742c/test_app/views.py

urls.pyの編集

htmlファイルを読み込むための処理と先ほどのviews.pyに記載した処理を紐づけるための処理を記載する。

from django.urls import path
from django.views.generic import TemplateView
from .views import DataTableView #追加部分
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('book_list/', views.view_book_list, name='book_list'),
    path('book_list_datatables', TemplateView.as_view(template_name='test_app/book_list_datatables.html'), name='book_list_datatables'), #追加部分
    path('data/', DataTableView.as_view()), #追加部分
]
path('data/', DataTableView.as_view()),

の「'data/'」の部分はhtmlファイルに記載した「ajax」のurlの部分と一致させる必要があるので注意。

ajax: {
          "url": "/data/",
          "type": "GET",
        },

動作確認

python manage.py runserver 127.0.0.1:8000」でサーバを起動させ、
http://127.0.0.1:8000/book_list_datatables
にアクセスをし、下記のように表示されていればOK。
f:id:doramochi:20210530114822p:plain


環境一式はこちら:
https://github.com/doramochi00/django/tree/2e1fb25380f567f96e764a91cdbec71b4282742c

Djangoを使ってみる⑤(DBの中身を表示)

DBの中身を表示

目指す形

前回登録したデータをテーブル形式で表示するページを作成してみる。
「下記赤枠の部分をクリックすると、データ一覧表示」というのを目指してみる。
f:id:doramochi:20210523120021p:plain

view.pyの編集

test_appフォルダの中にある「view.py」に関数を追加する。

from django.http import HttpResponse
from django.shortcuts import render
from .models import book_list

def index(request):
    return render(request, 'test_app/index.html')

# 今回追加した関数
def view_book_list(request):
    data = book_list.objects.all() # book_listテーブルの全データ取得
    contents = {'data': data} # contentsという辞書型変数に取得したデータを格納
    return render(request, 'test_app/book_list.html', contents) # book_list.htmlを表示する処理。先ほどのcontentsという変数に格納されたデータを渡す

テンプレートの作成

「templates」フォルダに「book_list.html」を作成する。
f:id:doramochi:20210523122123p:plain
book_list.htmlには、
テーブルを1つ用意し、先ほどviews.pyで設定した「contents」という変数に格納されているデータを展開し、一覧を表示する
という処理を記載する。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>書籍一覧</title>
</head>

<body>
    <h1>書籍一覧</h1>
    <table border="1">
        <tr>
            <th>本の名前</th>
            <th>作者</th>
            <th>出版社</th>
            <th>価格</th>
        </tr>
        {% for book in data %} <!-- views.pyで格納したデータを展開。for文で1つずつ取り出す-->
        <tr>
            <td>{{ book.book_name }}</td>
            <td>{{ book.author }}</td>
            <td>{{ book.publisher }}</td>
            <td>{{ book.price }}</td>
        </tr>
        {% endfor %}
    </table>
</body>

</html>

urls.pyの編集

先ほど作成したテンプレートを表示できるようurls.pyに下記を追記する。

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('book_list/', views.view_book_list, name='book_list'), #追加部分
]

index.htmlの編集

index.htmlの101行目にリンクを記載する部分があるのでリンク先のURLを「book_list/」とする。
f:id:doramochi:20210523145735p:plain

動作確認

いつも通り
python manage.py runserver 127.0.0.1:8000
でサーバーを起動し、「http://127.0.0.1:8000/」へアクセスする。
赤枠部分をクリックしてページ遷移をするとDBに登録したデータの一覧が表示される。
f:id:doramochi:20210523150028p:plain
f:id:doramochi:20210523150135p:plain

Djangoを使ってみる④(Modelの作成~管理画面の使用)

自作ブログへ移行しました
hachidev.com

modelの作成

models.pyの編集

test_appフォルダの中にある「models.py」を編集しmodelを作成することでDBのテーブル設計が出来る。

例えば下記の様なデータ構造を作りたい場合
テーブル名:book_list

本の名前(book_name) 作者(author) 出版社(publisher) 価格(price)
入門 Python 3 Bill Lubanovic オライリージャパン 4070
独習Python 山田 祥寛 翔泳社 3300
・・・ ・・・ ・・・ ・・・

models.pyに下記のように追記する。

from django.db import models

# Create your models here.
class book_list(models.Model):
    book_name = models.CharField(max_length=200)
    author = models.CharField(max_length=200)
    publisher = models.CharField(max_length=200)
    price = models.CharField(max_length=200)

※field type一覧:
https://docs.djangoproject.com/en/3.2/ref/models/fields/#model-field-types
※field option一覧:
https://docs.djangoproject.com/en/3.2/ref/models/fields/#field-options

マイグレーションファイルの作成

settings.pyを開き、INSTALLED_APPSにアプリケーションを追加する。

makemigrationsコマンドを実行。
コマンド例:python manage.py makemigrations test_app

「book_list」というmodelが作成される。

modelをデータベースに反映

migrateコマンドを実行し、modelをデータベースに反映させる。
コマンド例:python manage.py migrate

これでデータベースにbook_listというテーブルを作成することが出来た。
modelを確認するため、管理画面を有効化してみる。

管理画面の使用

管理者ユーザーの作成

管理画面を使用するために管理者ユーザー (スーパーユーザー) を作成する。
コマンド:python manage.py createsuperuser
ユーザー名やメールアドレス、パスワードを入力する。
パスワードは8文字以上設定する必要があるので注意。

admin.pyの編集

管理画面でmodelを見るために、modelを登録する必要がある。
「test_app」フォルダに含まれているadmin.pyを開き、以下のように記述する。

from django.contrib import admin
from test_app.models import book_list #追加

# Register your models here.
admin.site.register(book_list) #追加


管理画面へログイン

これまで通り
python manage.py runserver 127.0.0.1:8000
でサーバを起動させる。
起動後、ブラウザから
http://127.0.0.1:8000/admin/
へアクセスする。

ログイン画面が出てくるので先ほど設定したユーザー名とパスワードを入力してログインする。

ログインに成功すると下記のような画面が表示される。
「Book_lists」が表示されていればmodelの登録は問題なくできている。

データ登録

先ほどの「Book_lists」クリックすると下記の様な画面が表示される。。

『「BOOK_LIST」を追加+』をクリックすると下記の様なデータ登録画面が表示される。

最初の例に挙げた2つのデータを登録してみる。

データが登録されると下記のように「book_list object」というのが2つ生成される。
各リンクをクリックすると、データの中身を編集することも可能。

Djangoを使ってみる③(テンプレートの適用)

自作ブログへ移行しました
hachidev.com

テンプレートの適用

使用するテンプレート

「Hello, world」と表示するだけでは味気ないので既存のテンプレートを使ってトップページを作ってみる。
今回は試しに「LESSER」というテンプレートを使ってみる。
その他、「bootstrap テンプレート」などで調べると無料、有料問わず色々出てくるので好きなテンプレートを選べばOK。
基本的にやることはどのテンプレートでも同じ。
freehtml5.co

テンプレートのダウンロードと配置

「Free Download」からテンプレートをダウンロード

manage.pyのある階層に「static」「templates」フォルダを作成する。

ダウンロードしたテンプレートを解凍し、「css」「fonts」「images」「js」「sass」を先ほど作成した「static」フォルダにコピーする。

「templates」フォルダに「test_app」というフォルダを作成し、そこにindex.htmlをコピーする。

settings.pyの編集

TEMPLATESの部分を以下のように修正する。

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',
            ],
        },
    },
]

また、下記を追加する。

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static")
]

※osがimportされていない場合は「import os」も追加する。

変更を加えた箇所

test_app/views.pyの編集

「Hello, world」と表示する処理を記載していた個所を、index.htmlを読み込む処理に変更する。
views.pyを以下のように編集する。

from django.http import HttpResponse
from django.shortcuts import render #新規追加

def index(request):
    return render(request, 'test_app/index.html') #index.htmlを読み込む処理に変更

index.htmlの修正

デフォルト状態ではstatic配下のファイルを読みに行くことが出来ないためパスを変更する必要がある。
まずファイルの先頭に下記の記述を追加する。

{% load static %}

次にcssファイル、jsファイル、jpgファイルを読み込ませるため、

<link rel="stylesheet" href="css/******.css">
<script src="js/******.js"></script>
<a href="#" class="featured-grid" style="background-image: url(images/******.jpg);">
<a href="#"><img class="img-responsive" src="images/******.jpg" alt="Blog"></a>

の様な個所のファイルパスの部分を「{% static 'ファイルパス' %}」で囲む。

<link rel="stylesheet" href="{% static 'css/******.css' %}">
<script src="{% static 'js/******.js' %}"></script>
<a href="#" class="featured-grid" style="background-image: url({% static 'images/******.jpg' %});">
<a href="#"><img class="img-responsive" src="{% static 'images/******.jpg' %}" alt="Blog"></a>

の様に修正する。
例えば下記の様な個所。

参考:変更後のファイル
github.com

動作確認

サーバを再起動し、http://127.0.0.1:8000へアクセス。
下記のようにテンプレートが読み込まれていればOK。