Warning: count(): Parameter must be an array or an object that implements Countable in /home/xs638785/agile-software.site/public_html/wp-content/plugins/rich-table-of-content/functions.php on line 490
API
https://www.agile-software.site/2021/04/29/api/トップページのURLを作成
views.pyのIndexViewに渡します。
from django.urls import path
from app import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
]
書籍検索フォームの作成
titleを入力できるフォームを作成します。
from django import forms
class SearchForm(forms.Form):
title = forms.CharField(label='タイトル', max_length=200, required=True)
viewの作成
フォームの内容を取得し、index.htmlテンプレートに渡すことになります。
https://www.agile-software.site/2021/04/24/django-render%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89/form = SearchForm(request.POST or None)
from django.views.generic import View
from django.shortcuts import render
from .forms import SearchForm
class IndexView(View):
def get(self, request, *args, **kwargs):
form = SearchForm(request.POST or None)
return render(request, 'app/index.html', {
'form': form
})
base.html
もしユーザーがログイン状態であったらプロフィールとログアウトを、未ログインであればサインアップボタンとログインを表示する
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'profile' %}">プロフィール</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'account_logout' %}">ログアウト</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'account_signup' %}">サインアップ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'account_login' %}">ログイン</a>
</li>
{% endif %}
javaScript
{% block extra_js %}
{% endblock %}
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<title>楽天ブックス書籍APIチュートリアル</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">楽天ブックス書籍API</a>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="/">ホーム</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'profile' %}">プロフィール</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'account_logout' %}">ログアウト</a>
</li>
{% else %}
{% endif %}
</ul>
</div>
</nav>
<main>
<div class="container">
{% block content %}
{% endblock %}
</div>
</main>
<footer class="py-2 bg-dark">
<p class="m-0 text-center text-white">Copyright © Django Startup 2020</p>
</footer>
{% block extra_js %}
{% endblock %}
</body>
</html>
index.html
widget_tweaks
を使用することで、入力欄にテキストを用意することができます。
{% extends "app/base.html" %}
{% load widget_tweaks %}
{% block content %}
<div class="text-center my-5">
<form method="post">
{% csrf_token %}
<div class="align-items-center">
{% render_field form.title class="form-control" placeholder="キーワードから探す" %}
<button class="btn btn-primary mt-2" type="submit">検索</button>
</div>
</form>
</div>
{% endblock %}
検索ボタンをクリックして書籍のリストを表示させる
jsonをインポートします。jsonについては下記記事をご覧ください。
https://www.agile-software.site/2021/03/31/%e3%83%a2%e3%82%b8%e3%83%a5%e3%83%bc%e3%83%ab/requests.get
を使用することで、APIから情報を取得することができます。
api = requests.get(SEARCH_URL, params=params).text
form.cleaned_data
でフォームに入力したデータを取得する
パラメータで設定したデータを表示します。keyword変数には入力した文字列が入っています。
hit数を28にしているので画面には28個の書籍が表示されます。
keyword = form.cleaned_data['title']
from django.views.generic import View
from django.shortcuts import render
import json
import requests
from .forms import SearchForm
SEARCH_URL = 'https://app.rakuten.co.jp/services/api/BooksBook/Search/20170404?format=json&applicationId=1111111' # ID変更
def get_api_data(params):
api = requests.get(SEARCH_URL, params=params).text
result = json.loads(api)
items = result['Items']
return items
class IndexView(View):
def get(self, request, *args, **kwargs):
form = SearchForm(request.POST or None)
return render(request, 'app/index.html', {
'form': form
})
def post(self, request, *args, **kwargs):
form = SearchForm(request.POST or None)
if form.is_valid():
keyword = form.cleaned_data['title']
params = {
'title': keyword,
'hits': 28,
}
items = get_api_data(params)
book_data = []
for i in items:
item = i['Item']
title = item['title']
image = item['largeImageUrl']
isbn = item['isbn']
query = {
'title': title,
'image': image,
'isbn': isbn
}
book_data.append(query)
return render(request, 'app/book.html', {
'book_data': book_data,
'keyword': keyword
})
return render(request, 'app/index.html', {
'form': 'form'
})
index.htmlでフォーム画面をIndexViewとbook.htmlで検索結果の画面を表示させています。
book.html
ビューから渡されたデータをループで回して表示させます。
{{ book.title }}本のタイトルを太く表示させています。
<a class=”stretched-link” href=”{% url ‘detail’ book.isbn %}”></a>書籍をクリックしたら、詳細画面に遷移するようにリンクを設定します。
{% extends "app/base.html" %}
{% block content %}
<div class="text-center my-5">
<div class="mb-5">
<h3>「{{ keyword }}」の検索結果</h3>
</div>
<div class="row">
{% for book in book_data %}
<div class="col-3 mb-3">
<div class="card img-thumbnail booklist h-100">
<img class="card-img-top card-thum" src="{{ book.image }}" alt="">
<div class="card-body text-center px-2 py-3">
<h5 class="font-weight-bold">{{ book.title }}</h5>
</div>
<a class="stretched-link" href=""></a>
</div>
</div>
{% empty %}
<p>該当するものはありません</p>
{% endfor %}
</div>
</div>
{% endblock %}
書籍の詳細を表示させます。
from django.urls import path
from app import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('detail/<str:isbn>', views.DetailView.as_view(), name='detail'), # 追加
]
パラメータを引数としてAPIを実行します。表示に必要なデータを取得して、各変数に格納していきます
class DetailView(View):
def get(self, request, *args, **kwargs):
isbn = self.kwargs['isbn']
params = {
'isbn': isbn
}
items = get_api_data(params)
items = items[0]
item = items['Item']
title = item['title']
image = item['largeImageUrl']
author = item['author']
itemPrice = item['itemPrice']
salesDate = item['salesDate']
publisherName = item['publisherName']
size = item['size']
isbn = item['isbn']
itemCaption = item['itemCaption']
itemUrl = item['itemUrl']
reviewAverage = item['reviewAverage']
reviewCount = item['reviewCount']
book_data = {
'title': title,
'image': image,
'author': author,
'itemPrice': itemPrice,
'salesDate': salesDate,
'publisherName': publisherName,
'size': size,
'isbn': isbn,
'itemCaption': itemCaption,
'itemUrl': itemUrl,
'reviewAverage': reviewAverage,
'reviewCount': reviewCount,
'average': float(reviewAverage) * 20,
}
return render(request, 'app/detail.html', {
'book_data': book_data
})
DetailViewとdetail.htmlで詳細画面を表示させています。
isbnとは書籍を世界共通で特定するための番号で、現在、ISBNは計13桁の番号で構成されています。
点数がパーセンテージでビューからもらえるので、width
をパーセンテージで指定すると、視覚的に分かります。
{% extends "app/base.html" %}
{% block content %}
<div class="my-5">
<div class="row">
<div class="col-md-6 text-center">
<img src="{{ book_data.image }}" class="img-fluid" alt="">
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body px-2 py-1">
<div class="p-4">
<h3>{{ book_data.title }}</h3>
<p class="mb-3">{{ book_data.author }}</p>
<hr>
{% if not book_data.reviewCount == 0 %}
<div class="d-flex flex-row">
<div class="star-rating">
<div class="star-rating-front text-warning" style="width: {{ book_data.average }}%">★★★★★</div>
<div class="star-rating-back">★★★★★</div>
</div>
<div class="average ml-2 text-danger">
{{ book_data.reviewAverage|floatformat:2 }}
</div>
<div class="d-flex align-items-center ml-2">
{{ book_data.reviewCount }}件
</div>
</div>
{% else %}
<p>まだレビューはありません。</p>
{% endif %}
<p class="mt-3">価格:<span class="text-danger font-weight-bold h3">{{ book_data.itemPrice }}円</span></p>
<a class="btn btn-primary" href="{{ book_data.itemUrl }}">買い物かごに入れる</a>
</div>
</div>
</div>
</div>
</div>
{% if book_data.itemCaption %}
<div class="mb-5">
<h5>商品説明</h5>
<hr>
<p>{{ book_data.itemCaption }}</p>
</div>
{% endif %}
<div class="mb-5">
<h5>商品情報</h5>
<hr>
<p>発売日:{{ book_data.salesDate }}</p>
<p>著者/編集:{{ book_data.author }}</p>
<p>出版社:{{ book_data.publisherName }}</p>
<p>発行形態:{{ book_data.size }}</p>
<p>ISBNコード:{{ book_data.isbn }}</p>
</div>
</div>
{% endblock %}
CSS
/* detail */
.star-rating {
position: relative;
font-size: 20px;
word-wrap: normal !important;
}
.star-rating-front {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
}
.star-rating-back {
color: #ccc;
}
.average {
font-size: 20px;
font-weight: bold;
}