LoginSignup
1
3

Django rest frameworkを使用した簡単な商品検索システムを制作してみた

Last updated at Posted at 2023-12-17

今回はDRF(django-rest-framework)を使用した、超入門レベルの簡単な商品検索システムの作成に挑戦しました。

完成例

  • 商品のデータ(ID、商品名、商品タイプ、商品概要、値段、カスタマー評価、製造業者)が保存されたAPIサーバーと、そこへ簡単に検索できそれを表示できるWebサイトを作成しました。
  • 検索結果一覧に商品検索の指定に合致するJsonのデータが加工されてテーブルで表示されます。

qk1.png

行いたいこと

上記の図の条件としてサーバーに保存されているデータは以下のようなデータであるとします。

id=1,name="あ",type1="a",type2="a"
id=2,name="い",type1="a",type2="b"
id=3,name="う",type1="b",type2="a"

例えば、クライアント側からタイプ1が”a”かつタイプ2が”b”のものという指定をしたときに以下のようなJsonデータを返すAPIサーバーを作成したいと考えました。

{
 id=2,
 name="い",
 type1="a",
 type2="b",
}

使用するもの

  • Django 及び Django Rest Framework
  • HTML&Javascript 及び BootStrap

作成

1、プロジェクトの作成

  • 仮想環境にDjangoとDjango-rest-frameworkをインストールします。
  • 次にプロジェクトとアプリケーションを作成します。以下の図が主に変更が必要なファイルの構成になります。
  • 今回はプロジェクト名が productlistproject 、アプリケーション名が、 productlist となっています。
productlistproject
   ├─ manage.py
   ├─ /productlist
   │       ├─ /migrations
   │       ├─ admin.py
   │       ├─ models.py
   │       ├─ serializers.py
   │       ├─ urls.py
   │       ├─ views.py
   │       └─...
   └─ /productlistproject
           ├─ settings.py
           ├─ urls.py
           └─...
           
  • そして以下のファイルに変更を加えます。
productlist/admin.py
from django.contrib import admin
from .models import Productslist

admin.site.register(Productslist)
productlist/models.py
from django.db import models

class Productslist(models.Model):
    name = models.CharField(max_length=32)
    producttype = models.CharField(max_length=32)
    description = models.CharField(max_length=64)
    price = models.IntegerField()
    review = models.IntegerField()
    manufacturer = models.CharField(max_length=32)

    def __str__(self):
        return self.name
    
    class Meta:
        verbose_name=verbose_name_plural="商品一覧"

このmodels.pyで、 完成例 に基づいてデータの定義を行なっています。
それぞれ以下の通りに定義しています。

  • name=商品名
  • producttype = 商品タイプ
  • description = 商品概要
  • price = 価格
  • review = レビュー
  • manufacturer = 製造業者
productlist/serializers.py
from rest_framework import serializers
from .models import Productslist

class ProductslistSerializers(serializers.ModelSerializer):
    class Meta:
        model= Productslist
        fields=["id","name","producttype","description","price","review","manufacturer"]

productlist/urls.py
from django.urls import path
from .views import apiView

urlpatterns = [
    path('api/' ,apiView.as_view()),
]
productlist/views.py
from django.shortcuts import render
from .serializers import ProductslistSerializers
from rest_framework.generics import ListCreateAPIView
from .models import Productslist

class apiView(ListCreateAPIView):
    queryset=Productslist.objects.all().order_by('-review')
    serializer_class = ProductslistSerializers

    def get_queryset(self):
        name = self.request.query_params.get('name', None)
        producttype = self.request.query_params.get('producttype', None)
        price = self.request.query_params.get('price', None)
        review = self.request.query_params.get('review', None)
        manufacturer = self.request.query_params.get('manufacturer', None)

        queryset = super().get_queryset()

        if name:
            queryset = queryset.filter(name__contains=name)
        if producttype:
            queryset = queryset.filter(producttype__iexact=producttype)
        if manufacturer:
            queryset = queryset.filter(manufacturer__contains=manufacturer)
        if price:
            queryset = queryset.filter(price__lte=price)
        if review:
            queryset = queryset.filter(review__gte=review)
        return queryset

それぞれ以下のようなフィルターをしています。

  • name は、入力値が含まれているか
  • producttype は、入力値と同じか
  • price は、入力値以下か
  • review は、入力値以上か
  • manufacturer は、入力値が含まれているか
productlistproject/urls.py
from django.contrib import admin
from django.urls import path ,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('productlist.urls')),
]
productlistproject/settings.py
INSTALLED_APPS = [  
    .....
    'productlist.apps.ProductlistConfig',
    'rest_framework',
]

  • settings.py にそれぞれ追加してください。

2、検索およびJSONファイルを表示するサイトの作成

以下が作成したものです。

API.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
    <title>PRODUCT LIST API</title>
</head>
<body>
    <div class="container">
        <h1 class="display-3">PRODUCT LIST API</h1>
        <div style="height: 16px;"></div>
        <h2>商品検索</h2>
        <div class="container">
            <label for="name">商品名を入力してください:</label>
            <input class="form-control" type="text" id="name" placeholder="例:プレミアムワイヤレスヘッドフォン"><br>
            <label for="producttype">商品のタイプを入力してください:</label>
            <input class="form-control" type="text" id="producttype" placeholder="例:カメラ、テレビ"><br>
            <label for="manufacturer">製造業者名を入力してください:</label>
            <input class="form-control" type="text" id="manufacturer" placeholder="例:〇〇工業株式会社"><br>
            <label for="review">評価(下限)を入力してください:</label>
            <input class="form-control" type="number" id="review" min="0" max="5" placeholder="1~5の数字を入力してください"><br>
            <label for="price">値段(上限)を入力してください:</label>
            <input class="form-control" type="number" id="price" min="0" max="10000" placeholder="例:1000000円"><br>
            <div class="d-grid gap-2 col-6 mx-auto">
                <button type="button" class="btn btn-outline-primary" onclick="displayText()">検索する</button>
            </div>
        </div>
        <hr>
        <h3>検索結果一覧 (検索数:<span id="num"></span></h3>
        <div style="height: 16px;"></div>
        <div id="productslistContainer"></div>
    </div>
    <script>
        apifetch("あなたのURLを入れてください/?format=json")
        function apifetch(ogapiurl) {
            clearproductslistContainer()
            apiurl = ogapiurl;
            fetch(apiurl)
                .then(response => response.json())
                .then(data => {
                    displayProductslistData(data);
                })
                .catch(error => {
                    console.error('Error fetching Productslist data:', error);
                });
        }
        function displayProductslistData(productslistData) {
            var num=0;
            productslistData.forEach(productslist => {
                num+=1;
                var productslistElement = null
                productslistElement = document.createElement('div');
                productslistElement.innerHTML = `
                <div class="container">
                <table class="table table-borderless">
                    <tr>
                        <td colspan="2"><p class="fw-light">商品名</p></td>
                        <td colspan="2"><p class="fs-3">${productslist.name}</p></td>
                    </tr>
                    <tr>
                        <td colspan="2"><p class="fw-light">レビュー</p></td>
                        <td colspan="2"><p class="fs-5">${productslist.review}/5</p></td>
                    </tr>
                    <tr>
                        <td colspan="2"><p class="fw-light">価格</p></td>
                        <td colspan="2"><p class="fs-5">${productslist.price}円</p></td>
                    </tr>
                    <tr>
                        <td colspan="2"><p class="fw-light">商品概要</p></td>
                        <td colspan="2"><p class="fs-5">${productslist.description}</p></td>
                    </tr>
                    <tr>
                        <td colspan="2"><p class="fw-light">商品タイプ</p></td>
                        <td colspan="2"><p class="fs-5">${productslist.producttype}</p></td>
                    </tr>
                    <tr>
                        <td colspan="2"><p class="fw-light">製造業者</p></td>
                        <td colspan="2"><p class="fs-5">${productslist.manufacturer}</p></td>
                    </tr>
                </table>
                <hr>
                </div>
                `;
                document.getElementById('productslistContainer').appendChild(productslistElement);
            });
            document.getElementById("num").innerHTML = String(num);
        }
        function addQueryParam(parameter, apiUrl) {
            var inputValue = document.getElementById(parameter).value;
            if (inputValue !== "") {
                apiUrl += "&" + parameter + "=" + inputValue;
            }
            return apiUrl;
        }
        function addIntQueryParam(parameter, apiUrl) {
            var inputValue = document.getElementById(parameter).value;
            if (inputValue != "") {
                inputValue = Math.round(Number(inputValue));
                if (!isNaN(inputValue)) {
                    inputValue = String(inputValue)
                    apiUrl += "&" + parameter + "=" + inputValue
                }
            }
            return apiUrl;
        }
        function displayText() {
            let ogapiurl = "あなたのURLを入れてください/?format=json";
            ogapiurl = addQueryParam("name", ogapiurl);
            ogapiurl = addQueryParam("producttype", ogapiurl);
            ogapiurl = addQueryParam("manufacturer", ogapiurl);
            ogapiurl = addIntQueryParam("review", ogapiurl);
            ogapiurl = addIntQueryParam("price", ogapiurl);
            apifetch(ogapiurl)
        }
        function clearproductslistContainer() {
            var productslistContainer = document.getElementById('productslistContainer');
            while (productslistContainer.firstChild) {
                productslistContainer.removeChild(productslistContainer.firstChild);
            }
        }
    </script>
</body>
</html>

3、CORSの設定

  • で作成したものは、このままだと同一生成元ポリシーによってエラーになります。そのため以下の変更を加えます。
  • 最初以下をインストールしてください。
$ pip install django-cors-headers
  • インストール後、 settings.py に以下の変更を加えます。
productlistproject/settings.py
INSTALLED_APPS = [
    ...
    'corsheaders',
]
...
MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    ....
]

CORS_ALLOW_ALL_ORIGINS = True

  • CORS_ALLOW_ALL_ORIGINS = True で全てのオリジンからのアクセスを許可します。そのため、ファイルからのアクセス以外の場合は CORS_ORIGIN_WHITELIST に記載する方が良いと思います。

4、テストデータの入力

  • 次に以下の実行し、最後にスーパーユーザーを登録します。
$ python3 manage.py makemigrations
$ python3 manage.py migrate
$ python3 manage.py createsuperuser
$ python3 manage.py runserver
  • 以下のデータを”ローカルホスト/admin”にログインして追加します。
  • 入力するデータはなんでも良いと思います。以下が例として作成したJSONデータです。
rei.json
[
    {
        "id": 4,
        "name": "デジタルビジョンプロM49",
        "producttype": "カメラ",
        "description": "高解像度で手軽に美しい動画を撮影できるビデオカメラ",
        "price": 80000,
        "review": 5,
        "manufacturer": "産業株式会社"
    },
    {
        "id": 1,
        "name": "プロビューカメラX1",
        "producttype": "カメラ",
        "description": "商品概要説明: 高解像度でクリアな映像を提供するプロ仕様のビデオカメラ。",
        "price": 50000,
        "review": 4,
        "manufacturer": "睦電機株式会社"
    },
    {
        "id": 2,
        "name": "プロビューカメラX2",
        "producttype": "カメラ",
        "description": "高解像度で手軽に美しい動画を撮影できるビデオカメラ",
        "price": 30000,
        "review": 4,
        "manufacturer": "睦電機株式会社"
    },
    {
        "id": 6,
        "name": "ウルトラビジョンマスターZ900",
        "producttype": "テレビ",
        "description": "フレームレスデザインとHDR対応の大画面液晶テレビ。",
        "price": 95000,
        "review": 4,
        "manufacturer": "睦電機株式会社"
    },
    {
        "id": 3,
        "name": "デジタルビジョンプロM4",
        "producttype": "カメラ",
        "description": "専門家向けに設計され、多機能で使いやすいデジタルビデオカメラ。",
        "price": 75000,
        "review": 3,
        "manufacturer": "産業株式会社"
    },
    {
        "id": 5,
        "name": "ビジョンプロエリートX5000",
        "producttype": "テレビ",
        "description": "高解像度4K画面と先進の音声認識技術を搭載した最先端のテレビ",
        "price": 120000,
        "review": 3,
        "manufacturer": "蓮工房有限会社"
    },
    {
        "id": 7,
        "name": "プレミアムワイヤレスヘッドフォン",
        "producttype": "ヘッドフォン",
        "description": "商品概要: 高音質でノイズキャンセリング対応のワイヤレスヘッドフォン",
        "price": 4500,
        "review": 2,
        "manufacturer": "睦電機株式会社"
    }
]

上のデータは生成系AIである ChatGTP で生成した架空のデータです。

動作確認

以下はすべのデータの中から、 タイプが”カメラ” かつ、 評価が4以上 かつ、 製造業者が睦電機株式会社 であるデータを取得し、テーブルとして表示しています。

全部で7件だったものが2件に絞られています。

qk2.png

GitHub(KS_PF/Qiita_DRF_productlist_1217-) にJSONファイル、変更した部分などが置いてあります。

感想

超入門レベルではありますが、思ったよりも簡単に作れて驚きましした。ここからステップアップして、実用的なものが作れるように勉強を継続したいです。

今後とも勉強を続け、不適切である部分を発見したら適時更新します。
よろしくお願いします。

参考

udemy:APIを基礎からしっかりと学び、Django Rest Frameworkで天気情報を取得するアプリを作ろう!

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3