初めに
SSG(Static Site Generation)を使ってページネーションを超簡単に実装しますっっ! 最低限の説明にするために、関係のない実装(CSS,データベース、等)はしていません。使う技術
- ・Django-rest-frameworkのPaginator
ページネーションのあるAPIを作ります。
- ・Next.jsの静的ジェネレーター(getStaticPathsとgetStaticProps)
Django側に対して、ページ番号を付けたURLへアクセスしてデータを取得します。
Django側のコード
私は、プロジェクト名prjの中にappとapiというアプリケーションを作成しました。 prj(プロジェクト名) app(アプリケーション名) api(アプリケーション名)プロジェクトの設定
INSTALLED_APPASにrest_framework,app,apiを追加するだけでOK
データベースも標準のSQLiteを使っちゃってますが、PostgreSQLとかでも同じことです。
各々の設定はお任せで。
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),# ←追加
]
appアプリケーション
from django.db import models
class Datas(models.Model):
title = models.CharField(max_length=100)
content = models.CharField(max_length=1000)
def __str__(self):
return self.title
from django.contrib import admin
from app.models import Datas
admin.site.register(Datas)
apiアプリケーション
from django.urls import path
from . import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'data', views.DataViewSet, basename='data')
urlpatterns = router.urls
urlpatterns = [
path('data/', views.DataViewSet.as_view({'get': 'list'})),
path('data/pagination/', views.DataPaginationView.as_view({'get': 'list'}))
]
from rest_framework import viewsets
from app.models import Datas
from .serializers import DataSerializer
from .pagination import CustomPagiNumberPagination
class DataViewSet(viewsets.ModelViewSet):
model = Datas
serializer_class = DataSerializer
queryset = Datas.objects.all().order_by('id')
class DataPaginationView(viewsets.ModelViewSet):
model = Datas
pagination_class = CustomPagiNumberPagination
serializer_class = DataSerializer
queryset = Datas.objects.all().order_by('id')
from app.models import Datas
from rest_framework import serializers
class DataSerializer(serializers.ModelSerializer):
class Meta:
model = Datas
fields = '__all__'
シリアライザーはこれでOK。
from rest_framework import pagination
class CustomPagiNumberPagination(pagination.PageNumberPagination):
page_size = 5# 1ページのデータ数を指定します。
page_size_query_param = 'count'
max_page_size = 5
page_query_param = 'p'# api/data/pagination/?p=2 などとできます。
Django動作確認
http://127.0.0.1:8000/admin/
にアクセスし、Datasのデータを10個くらい追加しましょう。
今は、pagination.pyでデータ数を5に設定しています。
http://127.0.0.1:8000/api/data/pagination/?p=1
にアクセス↓
こんな感じで表示されれば成功です。
Next.js側のコード
Create Next App
任意の名前でアプリケーションを作成します。npx create-next-app@latest
元からあるpagesフォルダ内にdatas/page/[id].jsを作りました。
また、pagesと同じ階層に、lib/datas/datas.jsを作りました。
npm(もしくはyarn)でインストールするもの
以下の、dependencies内のモジュールを集めてきてください!
バージョンも合わせるとエラーを回避できます。
ちなみに、Paginationのスタイルと機能は、Material-uiにお任せしています。
{
"name": "next-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.8.6",
"next": "12.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"eslint": "8.18.0",
"eslint-config-next": "12.2.0"
}
}
データ取得をする2つの関数を定義します
export async function getAllDatasId() {
const res = await fetch(new URL(`http://127.0.0.1:8000/api/data/pagination/`));
const data = await res.json();
const totalPages = Math.ceil(Number(data.count)/Number(10))//6
let paramId = []
for (let i = 0; i < totalPages; i++) {
const parameter = {
params: {
id:String(i+1)
},
};
paramId.push(parameter);
}
return paramId
}
export async function getData(page) {
const res = await fetch(new URL(`http://127.0.0.1:8000/api/data/pagination/?p=${page}`));
const datas = await res.json();
return datas;
}
getAllDatasId()はgetStaticPaths(ページネーションの動的なページ番号を取得します)のための関数です。django-rest-frameworkで用意したAPIのcountから求めています。
getData()はgetStaticPropsで使います。getStaticPropsから求めたページ番号を基に、データを取得します。
ぜひ、コンソールに出したりして確かめてみて下さい!
import { useEffect, useState } from 'react';
import { Pagination } from '@mui/material';
import { useRouter } from 'next/router';
import { getAllDatasId, getData } from '../../../lib/datas/datas';
export default function All(props) {
const { datas } = props;
const [page, setPage] = useState(1),//ページ番号
[count, setCount] = useState();//総ページ数
const router = useRouter();
const clickPage = (e, page) => {
setPage(page);
router.push(`${page}`)
}
useEffect(() => {
setCount(Math.ceil(Number(datas && datas.count && datas.count)/Number(10)))
},[count,datas])
return (
<div>
<div style={{marginTop: "50px", textAlign: "center"}}>
<Pagination
count={count}//総ページ数
color="primary"
variant="outlined"
onChange={clickPage}//第2引数にページ番号が入る
page={page}
/>
{
datas.results.map(data => {
return (
<div>
<hr/>
<p>{data.title}</p>
<p>{data.content}</p>
<hr/>
</div>
)
})
}
</div>
</div>
)
}
export async function getStaticPaths() {
const paths = await getAllDatasId();
return {
paths,
fallback:true// ないページ番号にアクセスがあった際、falseの場合は404を返します。
// trueなら、同様に静的なページが表示されますが、propsが空になります。
};
}
export async function getStaticProps({ params }) {
const datas = await getData(params.id)
return {
props: {
datas,
},
revalidate:100// 本番環境の時に100秒毎にデータを読み込みます。
}
}
getStaticPathsでページ番号を取得し、getStaticPropsでページ毎のデータを取得できました。
Next.jsでは、SPAとしてURLを変更する際、useRouter()オブジェクトのrouter.push(相対パス)を使います。
clickPageという自作の関数は、Paginationのボタンがクリックされた時に、pageのステートを更新し、router.push()でURLに反映させるという処理をします。
こんな感じでuseStateで管理しているpageを[id].jsのidの部分と連動させています。
useEffect内では、Paginationに使う、ページ番号の総数を求めています。
3ページあるなら3が入る事で、Paginationが1,2,3と表示されます。
簡単な説明↓
・getStaticPathsで得られるもの
データの総数÷1ページのデータ数でページ数を求めて下のgetStaticPropsのためのページ番号をすべて準備しています。
[
{
params: {
id: '1'
}
},
{
params: {
id: '2'
}
}
]
・getStaticPropsでえられるもの
上のgetStaticPathsから引数の{params}でページ番号を取得して、ページ毎にデータを取得しています。
{
count: 11,
next: 'http://127.0.0.1:8000/api/data/pagination/?p=2',
previous: null,
results: [
{
id: 1,
title: 'タイトルです',
content: '内容です。',
savedat: '2022-06-30T18:25:53.505316+09:00'
},
{
id: 2,
title: 'タイトル2です',
content: '内容2です。',
savedat: '2022-06-30T18:26:04.498775+09:00'
},
{
id: 3,
title: 'タイトル3です',
content: '内容3です。',
savedat: '2022-06-30T18:26:15.109920+09:00'
},
{
id: 4,
title: 'タイトル4です',
content: '内容4です。',
savedat: '2022-06-30T18:26:28.785373+09:00'
},
{
id: 5,
title: 'タイトル5です',
content: '内容5です。',
savedat: '2022-06-30T18:26:43.141520+09:00'
}
]
}
動作確認
おわり
DjangoバックエンドのNext.jsの静的サイトジェネレーションを使った時のページネーションの作り方をまとめました。 DjangoでページネーションのAPIを作ってNext.jsで受け取ればいいという事でした。Django側での実装↓
- django-rest-frameworkのpaginatorを使ってpaginationのあるAPIを作る
- urls.pyで外部からアクセスできるようにする
Next.js側での実装↓
- getStaticPaths
- getStaticPropsでデータを取得
djangoのAPI上のcount(総データ数)からpage数を計算する。
count/1pageのデータ数=page数
getStaticPathsからpage数を受け取ってDjangoのAPIにアクセスしてデータを取得できます。
以上です。
見て頂きありがとうございました!
質問や指摘等あれば気軽によろしくお願いします!