====== 2023.12.14 React-adminとDjangoの連携 ======
===== DjangoでREST APIを作成 =====
[[06_virtualization:05_container:27_docker_django]] の方法で、インストールしている想定
プロジェクト名は、testproject で作成している形で説明
今回は、React-admin Django両方とも、[[06_virtualization:05_container:25_let_s_encrypt_proxy]] でSSL化している想定で説明します。
==== 1. settings.py 修正 ====
rest_frameworkを追加と
後のCORS (オリジン間リソース共有、 Cross-Origin Resource Sharing) の為、corsheadersを追加。
INSTALLED_APPS = [
...
...
...
'rest_framework',
'corsheaders',
'testproject',
]
==== 2. モデルの作成 ====
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.IntegerField()
def __str__(self):
return self.name
=== マイグレーションファイル作成 ===
$ docker-compose exec django python manage.py makemigrations testproject
このマイグレーションファイルが出来上がります。
# Generated by Django 5.0 on 2023-12-14 13:01
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('price', models.IntegerField()),
],
),
]
=== マイグレーション実行 ===
マイグレーション実行すると、テーブルが作成されます。
$ docker-compose exec django python manage.py migrate
=== テーブル確認 ===
docker-compose exec db mysql -u root -p db
mysql> show tables;
+----------------------------+
| Tables_in_db |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
| testproject_product |
+----------------------------+
11 rows in set (0.00 sec)
mysql> desc testproject_product;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(200) | NO | | NULL | |
| price | int | NO | | NULL | |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
==== 3. 管理画面での確認 ====
=== 管理ユーザを作成する ===
$ docker-compose exec django python manage.py createsuperuser
Username (leave blank to use 'root'): root
Email address: hogehoge@hogehoge.com
Password:
Password (again):
Superuser created successfully.
=== 管理画面にアクセス ===
先程作成した管理ユーザでアクセス。
https://django.fl8.jp/admin/
この画面になる場合
{{:50_dialy:2023:12:pasted:20231214-130608.png}}
CSRF_TRUSTED_ORIGINSにURLを追加してあげる
CSRF_TRUSTED_ORIGINS = ['https://django.fl8.jp']
今度は正しく表示される。
{{:50_dialy:2023:12:pasted:20231214-121722.png}}
=== 追加したテーブルを編集できるように変更 ===
from django.contrib import admin
# Register your models here.
from .models import Product
admin.site.register(Product)
編集すると、管理画面に追加したテーブルが表示されるので、テスト用にいくつかレコードを追加しておく。
{{:50_dialy:2023:12:pasted:20231214-131147.png}}
mysql> select * from newgen_product;
+----+--------------------+--------+
| id | name | price |
+----+--------------------+--------+
| 1 | みかん | 200 |
| 2 | すいか | 300 |
| 3 | おにぎり | 50 |
| 4 | さんま | 30 |
| 5 | ケーキ | 100 |
+----+--------------------+--------+
5 rows in set (0.01 sec)
==== 4. Serializersクラスを作成 ====
APIでデータのやり取りを行うためのSerializersクラスを作成
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'name', 'price']
==== 5. Viewの作成 ====
APIの処理を、ViewSetを使用して実装
一覧取得、登録、更新、削除などの処理が使えるようになります。
from rest_framework import viewsets
from .models import Product
from .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
==== 6. URL追加 ====
from django.contrib import admin
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet
router = DefaultRouter(trailing_slash=False)
router.register('products', ProductViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
]
==== 7.API確認 ====
https://django.fl8.jp/api/
{{:50_dialy:2023:12:pasted:20231214-131601.png}}
=== curlでも確認 ===
# curl -s https://django.fl8.jp/api/products| jq
[
{
"id": 1,
"name": "みかん",
"price": 200
},
{
"id": 2,
"name": "すいか",
"price": 300
},
{
"id": 3,
"name": "おにぎり",
"price": 50
},
{
"id": 4,
"name": "さんま",
"price": 30
},
{
"id": 5,
"name": "ケーキ",
"price": 100
}
]
# curl -s https://django.fl8.jp/api/products/3| jq
{
"id": 3,
"name": "おにぎり",
"price": 50
}
==== 8. Content-Rangeを追加する設定 ====
DjangoのREST APIをReact-adminのdataProviderとして利用する場合に、Content-Rangeを求められるので、Middlewareに追加しておく。
from .models import Product
class ContentRangeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if '/api/products' in request.path:
total_products = Product.objects.count()
response['Content-Range'] = f'items 0-{total_products}/{total_products}' # 実際の値を指定
return response
※MIDDLEWAREの一番下に、'testproject.middleware.ContentRangeMiddleware',を追加
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'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',
'testproject.middleware.ContentRangeMiddleware',
]
=== headerにContent-Rangeが追加された事確認 ===
# curl -is https://django.fl8.jp/api/products/3
HTTP/2 200
server: nginx/1.23.4
date: Thu, 14 Dec 2023 13:42:46 GMT
content-type: application/json
content-length: 41
vary: Accept, Cookie, origin
allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
content-range: items 0-9/20
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: same-origin
cross-origin-opener-policy: same-origin
strict-transport-security: max-age=31536000
{"id":3,"name":"おにぎり","price":50}
===== React-adminでREST APIを実行 =====
React-adminは、[[06_virtualization:05_container:30_react-admin]] で作成している想定です。
==== 1. post.tsx用意 ====
// in src/posts.tsx
import { SimpleForm, List, Datagrid, Create, Edit, TextInput, TextField, NumberField, ReferenceField } from "react-admin";
export const PostList = () => (
);
export const PostCreate = () => (
);
export const PostEdit = () => (
);
==== 2. dataProvider修正 ====
import fakeRestDataProvider from "ra-data-fakerest";
import simpleRestProvider from 'ra-data-simple-rest';
import data from "./data.json";
export const dataProviderfake = fakeRestDataProvider(data, true);
export const dataProvider = simpleRestProvider('https://django.fl8.jp/api');
==== 3. App.tsx修正 ====
import {
Admin,
Resource,
ListGuesser,
EditGuesser,
ShowGuesser,
} from "react-admin";
import { dataProvider } from "./dataProvider";
import { authProvider } from "./authProvider";
import {PostList,PostCreate,PostEdit} from './posts';
export const App = () => (
);
==== 4.ページ確認 ====
このエラーになる場合、CORSの設定ができてない。
{{:50_dialy:2023:12:pasted:20231214-150308.png}}
Access to fetch at 'https://django.fl8.jp/api/products?filter=%7B%7D&range=%5B0%2C9%5D&sort=%5B%22id%22%2C%22ASC%22%5D' from origin 'https://react-admin.fl8.jp' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
[[06_virtualization:05_container:25_let_s_encrypt_proxy]] にCORSの設定を入れてあげる。
# docker exec -it proxy bash
# vi /etc/nginx/vhost.d/django.fl8.jp
add_header 'Access-Control-Allow-Origin' 'https://react-admin.fl8.jp';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type,Range,Content-Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
# docker restart proxy
そうすると、今度はちゃんとデータ取得できるようになる。
{{:50_dialy:2023:12:pasted:20231214-151200.png}}
{{tag>日記 Django React}}