Админка на react-admin.

Будем использовать готовую наработку одно автора.

git clone git@github.com:bmihelac/ra-data-django-rest-framework.git provider

Вначале соберем провайдера данных.

cd provider
npm install
npm run build

Вынесем папку client приложения в папке examples и из нее создадим папку приложения admin в которую перенесем папку provider

Поменяем ссылку на локальную зависимость ra-data-django-rest-framework в package.json приложения.

"ra-data-django-rest-framework": "file:./provider",

Также изменим адрес проксирования.

"proxy": "http://localhost",

Собираем приложение.

cd client
npm install
npm run build

Запуск приложения.

npm run start

Настраиваем авторизацию на бекенде.

Настройки DRF

REST_FRAMEWORK = {

    'DEFAULT_AUTHENTICATION_CLASSES': [
        # SessionAuthentication is intentionally removed, see:
        # https://github.com/encode/django-rest-framework/issues/6104'
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

Роутинг.

from rest_framework.authtoken.views import ObtainAuthToken

urlpatterns = [
    ...
    path("api-token-auth/", ObtainAuthToken.as_view()),
]

Рабочий запрос.

start page

Копируем постраничку из example/backend/exampleapp/pagination.py нам в проект и подключаем в настройках.

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'exampleapp.pagination.PageNumberWithPageSizePagination',
    'PAGE_SIZE': 10,
...

Установим библиотеку django_filters

pip install django-filter==2.1.0

Добавляем в настройки

INSTALLED_APPS = [
    ...
    "django_filters",

а так же в настройки DRF

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'exampleapp.pagination.PageNumberWithPageSizePagination',
    'PAGE_SIZE': 10,
    'DEFAULT_FILTER_BACKENDS': [
        'rest_framework.filters.OrderingFilter',
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
    ],

...

Результат.

start page

start page

Авторизация работает.

Теперь необходимо удалить приложения из оригинального примера и на основе их построить свои собственные.

Докеризируем приложение с админкой.

Создаем Dockerfile

    FROM node:14-buster
    RUN mkdir /provider
    RUN mkdir /app
    COPY ./provider /app/provider
    WORKDIR /app
    COPY package.json /app/package.json
    RUN npm install --no-package-lock
    RUN npm cache clean --force

Тут мы копируем папку provider внутрь образа так как команда npm install требует присутствия этой зависимости как локальной в файле package.json.

"ra-data-django-rest-framework": "file:./provider",

Создаем контейнер в docker-compose.yaml.

    pressa-admin:
        build: 
            context: ../admin
            dockerfile: Dockerfile
        container_name: pressa-admin
        volumes:
            - ../admin:/app
            - ../admin/provider:/provider
            - /app/node_modules
        command: npm run start
        ports:
            - 3000:3000
        networks: 
            - pressa_network

Проксируем nginx c 3000 порта на адрес /admin в файле conf/nginx.default.conf

    location /radmin {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
        proxy_pass http://pressa-admin:3000;
    }


    location /radmin {

        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://pressa-admin:3000; 
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

    }

К сожалению не получилось поднять через nginx.

Поэтому можно поменять прокcирование в package.json

    "proxy": "http://pressa-django:8000",

И работать через открытый контейнером локальный порт localhost:3000.

Админка. Раздел изданий.

Создадим серилизатор по пути journal/serilizers/journal.py.

from rest_framework import serializers
from journal.models import Journal


class JournalSerializer(serializers.ModelSerializer):

    class Meta:
        model = Journal
        fields = [
            'id',
            'name'
        ]

Тперь представление по пути journal/viewsets/journal.

from rest_framework import viewsets
from journal.models import Journal
from journal.serializers.journal import JournalSerializer


class JournalViewSet(viewsets.ModelViewSet):
    queryset = Journal.objects.all()
    serializer_class = JournalSerializer
    search_fields = ['id', 'name']

Дочерний роутинг journal/urls.py

from .viewsets.journal import JournalViewSet
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'journal', JournalViewSet)

urlpatterns = router.urls

Включим в главный роутинг.

path('v1/', include([
    ...
    path('journal/', include('journal.urls'))
])),

Результат.

start page

Создадим класс пагинации.

from rest_framework.pagination import PageNumberPagination


class PageNumberWithPageSizePagination(PageNumberPagination):
    page_size_query_param = 'page_size'

Добавим его в настройки.

REST_FRAMEWORK = {
    ...
    'DEFAULT_PAGINATION_CLASS': 'app.pagination.PageNumberWithPageSizePagination',
    'PAGE_SIZE': 10
}

Поставим две библиотеки для фильтрации.

django-extensions
django-filter==2.4.0

Класс фильтра.

import django_filters
from journal.models import Journal


class JournalFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(field_name="name", lookup_expr="contains")


    class Meta:
        model = Journal
        fields = ["name"]

Подключение в представлении.

from rest_framework import viewsets
from journal.models import Journal
from journal.serializers.journal import JournalSerializer
from journal.filters.journal import JournalFilter

class JournalViewSet(viewsets.ModelViewSet):
    queryset = Journal.objects.all()
    serializer_class = JournalSerializer
    filterset_class = JournalFilter
    search_fields = ['name']
Задать вопрос, прокомментировать.