今回はDRF(django-rest-framework)を使用した、超入門レベルの簡単な商品検索システムの作成に挑戦しました。
完成例
- 商品のデータ(ID、商品名、商品タイプ、商品概要、値段、カスタマー評価、製造業者)が保存されたAPIサーバーと、そこへ簡単に検索できそれを表示できるWebサイトを作成しました。
- 検索結果一覧に商品検索の指定に合致するJsonのデータが加工されてテーブルで表示されます。
行いたいこと
上記の図の条件としてサーバーに保存されているデータは以下のようなデータであるとします。
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
└─...
- そして以下のファイルに変更を加えます。
from django.contrib import admin
from .models import Productslist
admin.site.register(Productslist)
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 = 製造業者
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"]
from django.urls import path
from .views import apiView
urlpatterns = [
path('api/' ,apiView.as_view()),
]
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 は、入力値が含まれているか
from django.contrib import admin
from django.urls import path ,include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('productlist.urls')),
]
INSTALLED_APPS = [
.....
'productlist.apps.ProductlistConfig',
'rest_framework',
]
- settings.py にそれぞれ追加してください。
2、検索およびJSONファイルを表示するサイトの作成
以下が作成したものです。
<!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の設定
- 2 で作成したものは、このままだと同一生成元ポリシーによってエラーになります。そのため以下の変更を加えます。
- 最初以下をインストールしてください。
$ pip install django-cors-headers
- インストール後、 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データです。
[
{
"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件に絞られています。
GitHub(KS_PF/Qiita_DRF_productlist_1217-) にJSONファイル、変更した部分などが置いてあります。
感想
超入門レベルではありますが、思ったよりも簡単に作れて驚きましした。ここからステップアップして、実用的なものが作れるように勉強を継続したいです。
今後とも勉強を続け、不適切である部分を発見したら適時更新します。
よろしくお願いします。
参考
udemy:APIを基礎からしっかりと学び、Django Rest Frameworkで天気情報を取得するアプリを作ろう!