Как создать современное веб-приложение для управления информацией о клиентах с помощью Django и React в Ubuntu 18.04

0 Акции
0
0
0
0

Введение

Люди используют различные устройства для подключения к Интернету и просмотра веб-страниц. Поэтому приложения должны быть доступны из разных мест. Для традиционных веб-сайтов обычно достаточно адаптивного пользовательского интерфейса, но более сложные приложения часто требуют использования других технологий и архитектур. К ним относятся раздельные бэкенд и фронтенд REST-приложения, которые могут запускаться как клиентские веб-приложения, прогрессивные веб-приложения (PWA) или нативные мобильные приложения.

Вот некоторые инструменты, которые можно использовать при создании более сложных приложений:

  • React — это фреймворк JavaScript, позволяющий разработчикам создавать веб-страницы и собственные страницы для своих бэкэндов REST API.
  • Django — это бесплатный веб-фреймворк Python с открытым исходным кодом, который следует шаблону архитектуры программного обеспечения Model-View-Controller (MVC).
  • Django REST Framework — мощный и гибкий инструментарий для создания REST API в Django.

В этом руководстве вы создадите современное веб-приложение с раздельными бэкендом и фронтендом REST API, используя React, Django и Django REST Framework. Используя React с Django, вы сможете воспользоваться новейшими достижениями в области JavaScript и фронтенд-разработки. Вместо создания приложения на Django, использующего встроенный шаблонизатор, вы будете использовать React как библиотеку пользовательского интерфейса, использующую виртуальную объектную модель документа (DOM), декларативный подход и компоненты, быстро отображающие изменения данных.

Веб-приложение, которое вы создадите, будет хранить данные о клиентах в базе данных, и вы сможете использовать его в качестве отправной точки для CRM-приложения. После этого вы сможете читать, обновлять и удалять данные, используя интерфейс React, стилизованный под Bootstrap 4.

Предпосылки

Для выполнения этого урока вам понадобится:

  • Машина для разработки с Ubuntu 18.04.
  • Python 3, pip и venv устанавливаются на ваш компьютер, следуя шагам 1 и 2 из раздела Установка Python 3 и настройка локальной среды разработки в Ubuntu 18.04.
  • На вашем компьютере установлены Node.js 6+ и npm 5.2 или выше. Вы можете установить оба, следуя инструкциям в статье «Установка Node.js в Ubuntu 18.04» в разделе «Установка из PPA».

Шаг 1 — Создайте виртуальную среду Python и установите зависимости

На этом этапе мы создаем виртуальную среду и устанавливаем необходимые зависимости для нашего приложения, включая Django, фреймворк Django REST и django-cors-headers.

Наше приложение будет использовать два разных сервера разработки для Django и React. Они будут работать на разных портах и действовать как два отдельных домена. Поэтому нам необходимо включить Cross-Resource Sharing (CORS) для отправки HTTP-запросов из React в Django без блокировки браузером.

Перейдите в домашний каталог и создайте виртуальную среду, используя модуль Python 3 venv:

cd ~
python3 -m venv ./env

Активируйте созданную виртуальную среду, используя источник:

source env/bin/activate

Затем установите зависимости проекта с помощью pip. Они включают в себя следующее:

  • Django: веб-фреймворк для проекта.
  • Django REST Framework: стороннее приложение, которое создает REST API с помощью Django.
  • django-cors-headers: пакет, включающий CORS.

Установите фреймворк Django:

pip install django djangorestframework django-cors-headers

Установив зависимости проекта, вы можете создать проект Django и фронтенд React.

Шаг 2 — Создание проекта Django

На этом этапе мы создаем проект Django, используя следующие команды и инструменты:

django-admin startproject-name: django-admin — это инструмент командной строки, используемый для выполнения задач в Django. Команда startproject создаёт новый проект Django.

python manager.py startapp myapp: manager.py — это служебный скрипт, который автоматически добавляется в каждый проект Django и выполняет ряд административных задач: создание новых приложений, миграцию баз данных и локальное обслуживание проекта Django. Команда startapp создаёт приложение Django в проекте Django. В Django термин «приложение» описывает пакет Python, предоставляющий проекту набор функций.

Для начала создайте проект Django с помощью django-admin startproject . Назовём наш проект djangoreactproject :

django-admin startproject djangoreactproject

Прежде чем двигаться дальше, давайте рассмотрим структуру каталогов проекта Django с помощью команды tree.

Перейдите в папку djangoreactproject в корне вашего проекта и выполните команду tree:

cd ~/djangoreactproject
tree

Вы увидите следующий вывод:

Output
├── djangoreactproject
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py

Папка ~/djangoreactproject является корневой папкой проекта. В ней находится несколько файлов, важных для вашей работы:

  • manager.py: служебный скрипт, выполняющий ряд административных задач.
  • settings.py: основной файл конфигурации проекта Django, в котором можно изменять его параметры. Эти параметры включают в себя переменные, такие как INSTALLED_APPS — список строк, определяющих активные приложения для вашего проекта. Подробнее о доступных параметрах см. в документации Django.
  • urls.py: Этот файл содержит список шаблонов URL и связанных с ними представлений. Каждый шаблон устанавливает связь между URL и функцией, которая должна быть вызвана для этого URL. Подробнее об URL и представлениях см. в нашем руководстве по созданию представлений Django.

Первым шагом в работе с проектом станет настройка пакетов, установленных на предыдущем этапе, включая фреймворк Django REST и пакет Django CORS, путём добавления их в settings.py. Откройте файл в nano или другом удобном вам редакторе:

nano ~/djangoreactproject/djangoreactproject/settings.py

Перейдите в настройки INSTALLED_APPS и добавьте приложения rest_framework и corsheaders в конец списка:

...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders'
]

Затем добавьте промежуточное ПО corsheaders.middleware.CorsMiddleware из ранее установленного пакета CORS в конфигурацию MIDDLEWARE. Эта конфигурация представляет собой список промежуточного ПО — классов Python, содержащих код, который обрабатывается каждый раз, когда ваше веб-приложение отправляет запрос или ответ:

...
MIDDLEWARE = [
...
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware'
]

Далее можно включить CORS. Параметр CORS_ORIGIN_ALLOW_ALL определяет, хотите ли вы разрешить CORS для всех доменов, а CORS_ORIGIN_WHITELIST — это кортеж Python, содержащий разрешённые URL-адреса. В нашем случае, поскольку сервер разработки React будет работать по адресу http://localhost:3000, мы добавим новые настройки CORS_ORIGIN_ALLOW_ALL = False и CORS_ORIGIN_WHITELIST('localhost:3000',) в файл settings.py. Добавьте эти настройки в любое место файла:

...
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'localhost:3000',
)
...

Дополнительные параметры конфигурации можно найти в документации django-cors-headers.

Сохраните файл и выйдите из редактора по завершении.

Находясь в каталоге ~/djangoreactproject, создайте новое приложение Django с именем customers:

python manage.py startapp customers

Сюда входят модели управления клиентами и представления. Модели определяют поля и поведение данных нашего приложения, а представления позволяют приложению корректно обрабатывать веб-запросы и возвращать необходимые ответы.

Затем добавьте это приложение в список установленных приложений в файле settings.py вашего проекта, чтобы Django распознал его как часть проекта. Откройте settings.py ещё раз:

nano ~/djangoreactproject/djangoreactproject/settings.py

Добавьте приложение клиента:

...
INSTALLED_APPS = [
...
'rest_framework',
'corsheaders',
'customers'
]
...

Затем выполните миграцию базы данных и запустите локальный сервер разработки. Миграции — это способ Django переносить изменения, вносимые в модели, в схему базы данных. Эти изменения могут включать, например, добавление поля или удаление модели. Подробнее о моделях и миграциях см. в статье «Как создавать модели Django».

Передача базы данных:

python manage.py migrate

Запустите локальный сервер разработки:

python manage.py runserver

Вы увидите вывод, подобный следующему:

Output
Performing system checks...
System check identified no issues (0 silenced).
October 22, 2018 - 15:14:50
Django version 2.1.2, using settings 'djangoreactproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Ваше веб-приложение будет работать по адресу http://127.0.0.1:8000. Если вы перейдёте по этому адресу в браузере, вы увидите следующую страницу:

На этом этапе оставьте программу запущенной и откройте новый терминал, чтобы продолжить разработку проекта.

Шаг 3 — Создание React Frontend

В этом разделе мы собираемся создать front-end приложение нашего проекта с использованием React.

React предлагает официальный инструмент, позволяющий быстро генерировать проекты React без необходимости прямой настройки Webpack. Webpack — это сборщик модулей, используемый для сборки веб-ресурсов, таких как код JavaScript, CSS и изображения. Обычно перед использованием Webpack требуется настроить различные параметры конфигурации, но благодаря инструменту Create-react-app вам не придётся работать с Webpack напрямую, если только вам не потребуется больше контроля. Для запуска Create-react-app можно использовать npx — инструмент, запускающий упакованные в npm двоичные файлы.

Во втором терминале убедитесь, что вы находитесь в каталоге своего проекта:

cd ~/djangoreactproject

Создайте проект React под названием frontend, используя create-react-app и npx:

npx create-react-app frontend

Затем перейдите в приложение React и запустите сервер разработки:

cd ~/djangoreactproject/frontend
npm start

Ваше приложение будет работать по адресу http://localhost:3000/:

Оставьте сервер разработки React работающим и откройте другое окно терминала, чтобы продолжить.

Чтобы увидеть структуру каталогов всего проекта на этом этапе, перейдите в корневую папку и снова запустите дерево:

cd ~/djangoreactproject
tree

Вы увидите такую структуру:

Output
├── customers
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── djangoreactproject
│ ├── __init__.py
│ ├── __pycache__
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── frontend
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── README.md
│ ├── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ └── registerServiceWorker.js
│ └── yarn.lock
└── manage.py

Наше приложение использует Bootstrap 4 для стилизации интерфейса React, поэтому мы добавим его в файл frontend/src/App.css, который будет управлять нашими настройками CSS. Откройте файл:

nano ~/djangoreactproject/frontend/src/App.css

Добавьте следующую запись в начало файла. Вы можете удалить существующее содержимое файла, хотя это не обязательно:

@import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';

Здесь @import — это директива CSS, используемая для импорта правил стиля из других таблиц стилей.

Теперь, когда мы создали как Back-end, так и Front-end приложения, давайте создадим модель Customer и некоторые демонстрационные данные.

Шаг 4 — Создание модели клиента и исходных данных

После создания приложения Django и экземпляра React следующим шагом станет создание модели клиента, представляющей таблицу базы данных, содержащую информацию о клиенте. SQL-код не требуется, поскольку объектно-реляционный преобразователь (ORM) Django выполняет операции с базой данных, сопоставляя классы и переменные Python с таблицами и столбцами SQL. Таким образом, ORM Django абстрагирует взаимодействие SQL с базой данных через интерфейс Python.

Повторно включите вашу виртуальную среду:

cd ~
source env/bin/activate

Перейдите в каталог клиентов и откройте models.py — файл Python, содержащий модели вашего приложения:

cd ~/djangoreactproject/customers/
nano models.py

Файл будет содержать следующее содержимое:

from django.db import models
# Create your models here.

API модели Customer уже включено в файл благодаря оператору import import models from django.db. Теперь вам нужно добавить класс Customer, расширяющий models.Model. Каждая модель в Django — это класс Python, расширяющий django.db.models.Model.

Модель Customer будет иметь следующие поля базы данных:

  • имя – Имя клиента.
  • фамилия – Фамилия клиента.
  • ЭЛЕКТРОННАЯ ПОЧТА – Адрес электронной почты клиента.
  • телефон – Номер телефона клиента.
  • АДРЕС – Адрес клиента.
  • ОПИСАНИЕ – Описание клиента.
  • createAt – Дата добавления клиента.

Мы также добавляем функцию __str__(), которая определяет, как будет отображаться модель. В нашем случае это будет имя клиента. Подробнее о создании классов и определении объектов см. в статье «Как создавать классы и определять объекты в Python 3».

Добавьте в файл следующий код:

from django.db import models
class Customer(models.Model):
first_name = models.CharField("First name", max_length=255)
last_name = models.CharField("Last name", max_length=255)
email = models.EmailField()
phone = models.CharField(max_length=20)
address = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
createdAt = models.DateTimeField("Created At", auto_now_add=True)

def __str__(self):
return self.first_name

Затем выполните миграцию базы данных для создания таблиц. Команда makemigrations создаёт файлы миграции, в которые добавляются изменения модели, а команда migrate применяет эти изменения к базе данных.

Вернитесь в корневую папку проекта:

cd ~/djangoreactproject

Чтобы создать файлы миграции, выполните следующее:

python manage.py makemigrations

Вы получите такой вывод:

Output
customers/migrations/0001_initial.py
- Create model Customer

Примените эти изменения к базе данных:

python manage.py migrate

Вы увидите вывод, указывающий на успешную передачу:

Output
Operations to perform:
Apply all migrations: admin, auth, contenttypes, customers, sessions
Running migrations:
Applying customers.0001_initial... OK

Далее вам нужно будет использовать файл переноса данных для создания исходных данных о клиентах. Файл переноса данных — это миграция, которая добавляет или изменяет данные в базу данных. Создайте пустой файл переноса данных для приложения «Клиенты»:

python manage.py makemigrations --empty --name customers customers

Вы увидите следующее подтверждение с именем вашего файла миграции:

Output
Migrations for 'customers':
customers/migrations/0002_customers.py

Обратите внимание, что имя вашего файла миграции — 0002_customers.py.

Далее перейдите в папку миграций клиентского приложения:

cd ~/djangoreactproject/customers/migrations

Откройте созданный файл миграции:

nano 0002_customers.py

Это первоначальное содержимое файла:

from django.db import migrations

class Migration(migrations.Migration):
dependencies = [
('customers', '0001_initial'),
]
operations = [
]

Оператор импорта импортирует API миграции, API Django для создания миграций, из django.db, встроенного пакета, содержащего классы для работы с базами данных.

Класс Migration — это класс Python, описывающий операции, выполняемые при миграции базы данных. Он расширяет класс migrations.Migration и содержит два списка:

  • зависимости: Включает зависимые миграции.
  • операции: Содержит операции, выполняемые путем применения миграций.

Затем добавьте метод для создания тестовых данных о клиентах. Перед определением класса Migration добавьте следующий метод:

...
def create_data(apps, schema_editor):
Customer = apps.get_model('customers', 'Customer')
Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
...

В этом методе мы берем класс Customer нашего клиентского приложения и создаем тестового клиента для вставки в базу данных.

Для получения класса Customer, позволяющего создавать новых клиентов, мы используем метод get_model() объекта apps. Объект apps представляет собой реестр установленных приложений и их моделей баз данных.

При использовании метода RunPython() для выполнения create_data() передаётся объект apps. Добавьте метод migrations.RunPython() в пустой список операций:

...
operations = [
migrations.RunPython(create_data),
]

RunPython() — это часть API миграций, которая позволяет запускать собственный код Python в миграции. В нашем списке операций указано, что этот метод будет выполнен при применении миграции.

Вот полный файл:

from django.db import migrations

def create_data(apps, schema_editor):
Customer = apps.get_model('customers', 'Customer')
Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()

class Migration(migrations.Migration):
dependencies = [
('customers', '0001_initial'),
]
operations = [
migrations.RunPython(create_data),
]

Дополнительную информацию о миграции данных см. в документации по миграции данных Django.

Чтобы переместить базу данных, сначала вернитесь в основную папку вашего проекта:

cd ~/djangoreactproject

Перенесите базу данных для создания тестовых данных:

python manage.py migrate

Вы увидите вывод, подтверждающий миграцию:

Output
Operations to perform:
Apply all migrations: admin, auth, contenttypes, customers, sessions
Running migrations:
Applying customers.0002_customers... OK

Создав модель клиента и репрезентативные данные, мы можем перейти к построению REST API.

Шаг 5 — Создание REST API

На этом этапе мы создадим REST API с использованием фреймворка Django REST. Мы создадим несколько различных представлений API. Представление API — это функция, обрабатывающая запрос или вызов API, а конечная точка API — это уникальный URL-адрес, представляющий точку контакта с системой REST. Например, когда пользователь отправляет GET-запрос к конечной точке API, Django вызовет соответствующую функцию или представление API для обработки запроса и возврата возможных результатов.

Мы также будем использовать сериализаторы. Сериализатор в REST-фреймворке Django позволяет преобразовывать сложные экземпляры моделей и наборы запросов (QuerySets) в формат JSON для использования через API. Класс сериализатора может работать и в обратном направлении, предоставляя механизмы для разбора и разделения данных в моделях и наборах запросов Django.

Наши конечные точки API будут включать:

  • api/customers: эта конечная точка используется для создания клиентов и возврата постраничных коллекций клиентов.
  • API/клиенты/ : Эта конечная точка используется для получения, обновления и удаления отдельных клиентов по первичному ключу или идентификатору.

Мы также создаем URL-адреса в файле urls.py проекта для соответствующих конечных точек (например, api/customers и api/customers/ ).

Начнем с создания класса сериализатора для нашей клиентской модели.

Добавить класс сериализатора

Создание класса сериализатора для нашей модели Customer необходимо для преобразования экземпляров Customer и QuerySet в JSON и обратно. Чтобы создать класс сериализатора, сначала создайте файл serializers.py в приложении Customers:

cd ~/djangoreactproject/customers/
nano serializers.py

Добавьте следующий код для импорта серийного номера API и модели клиента:

from rest_framework import serializers
from .models import Customer

Затем создайте класс сериализатора, который расширяет Serializers.ModelSerializer и указывает поля, которые будут сериализованы:

...
class CustomerSerializer(serializers.ModelSerializer):

class Meta:
model = Customer
fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')

Класс Meta определяет модель и поля для сериализации: pk, first_name, last_name, email, phone, address, description.

Вот полное содержимое файла:

from rest_framework import serializers
from .models import Customer

class CustomerSerializer(serializers.ModelSerializer):

class Meta:
model = Customer
fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')

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

Добавление представлений API

В этом разделе мы создадим представления API для нашего приложения, которые будут вызываться Django, когда пользователь посещает конечную точку, соответствующую функции представления.

~/djangoreactproject/customers/views.py Открыть:

nano ~/djangoreactproject/customers/views.py

Удалите существующие и добавьте следующие импорты:

from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Customer 
from .serializers import *

Мы импортируем созданную нами сериализацию вместе с клиентской моделью и API-интерфейсами фреймворка Django REST.

Далее добавьте представление для обработки HTTP-запросов POST и GET:

...
@api_view(['GET', 'POST'])
def customers_list(request):
"""
List customers, or create a new customer.
"""
if request.method == 'GET':
data = []
nextPage = 1
previousPage = 1
customers = Customer.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(customers, 10)
try:
data = paginator.page(page)
except PageNotAnInteger:
data = paginator.page(1)
except EmptyPage:
data = paginator.page(paginator.num_pages)
serializer = CustomerSerializer(data,context={'request': request} ,many=True)
if data.has_next():
nextPage = data.next_page_number()
if data.has_previous():
previousPage = data.previous_page_number()
return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
elif request.method == 'POST':
serializer = CustomerSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Сначала мы используем декоратор @api_view(['GET', 'POST']) для создания API-представления, принимающего запросы GET и POST. Декоратор — это функция, которая принимает другую функцию и динамически расширяет её.

В теле метода мы используем переменную request.method для проверки текущего HTTP-метода и выполнения соответствующей логики в зависимости от типа запроса:

  • Если запрос GET, этот метод разбивает данные на страницы с помощью Django Paginator и возвращает первую страницу данных после сериализации, количество доступных клиентов, количество доступных страниц и ссылки на предыдущую и следующую страницы. Paginator — это встроенный класс Django, который разбивает список данных на страницы и предоставляет методы для доступа к элементам на каждой странице.
  • Если запрос — POST, метод сериализует полученные клиентские данные, а затем вызывает метод save() объекта сериализатора. После этого он возвращает объект Response, экземпляр HttpResponse, с кодом состояния 201. Каждый созданный вами профиль отвечает за возврат объекта HttpResponse. Метод save() сохраняет сериализованные данные в базе данных.

Дополнительную информацию о HttpResponse и представлениях см. в этом обсуждении по созданию функций представления.

Теперь добавьте представление API, которое отвечает за обработку запросов GET, PUT и DELETE для получения, обновления и удаления клиентов по pk (первичному ключу):

...
@api_view(['GET', 'PUT', 'DELETE'])
def customers_detail(request, pk):
"""
Retrieve, update or delete a customer by id/pk.
"""
try:
customer = Customer.objects.get(pk=pk)
except Customer.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

if request.method == 'GET':
serializer = CustomerSerializer(customer,context={'request': request})
return Response(serializer.data)

elif request.method == 'PUT':
serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

elif request.method == 'DELETE':
customer.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

Этот метод дополнен @api_view(['GET', 'PUT', 'DELETE']), чтобы указать, что это представление API, которое может принимать запросы GET, PUT и DELETE.

Проверка поля request.method проверяет метод запроса и вызывает правильную логику в зависимости от его значения:

  • Если запрос GET, данные клиента сериализуются и отправляются с использованием объекта Response.
  • Если это запрос PUT, этот метод создаёт сериализатор для новых данных клиента. Затем метод save() вызывает созданный объект сериализатора. Наконец, он отправляет объект Response с обновлёнными данными клиента.
  • Если это запрос DELETE, метод delete() вызывает метод delete() клиентского объекта, чтобы удалить его, а затем возвращает объект Response без данных.

Готовый файл выглядит так:

from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Customer
from .serializers import *


@api_view(['GET', 'POST'])
def customers_list(request):
"""
List customers, or create a new customer.
"""
if request.method == 'GET':
data = []
nextPage = 1
previousPage = 1
customers = Customer.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(customers, 5)
try:
data = paginator.page(page)
except PageNotAnInteger:
data = paginator.page(1)
except EmptyPage:
data = paginator.page(paginator.num_pages)

serializer = CustomerSerializer(data,context={'request': request} ,many=True)
if data.has_next():
nextPage = data.next_page_number()
if data.has_previous():
previousPage = data.previous_page_number()

return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})

elif request.method == 'POST':
serializer = CustomerSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET', 'PUT', 'DELETE'])
def customers_detail(request, pk):
"""
Retrieve, update or delete a customer by id/pk.
"""
try:
customer = Customer.objects.get(pk=pk)
except Customer.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

if request.method == 'GET':
serializer = CustomerSerializer(customer,context={'request': request})
return Response(serializer.data)

elif request.method == 'PUT':
serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

elif request.method == 'DELETE':
customer.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

Теперь мы можем продолжить создание наших конечных точек.

Добавление конечных точек API

Теперь мы создадим конечные точки API: api/customers/ для запроса и создания клиентов и api/customers/ , для получения, обновления или удаления отдельных клиентов по их pk.

Откройте ~/djangoreactproject/djangoreactproject/urls.py:

nano ~/djangoreactproject/djangoreactproject/urls.py

Оставьте все как есть, но добавьте импорт в представление «Клиенты» в верхней части файла:

from django.contrib import admin
from django.urls import path
from customers import views
from django.conf.urls import url

Затем URL-адреса api/customers/ и api/customers/ Добавить в список шаблонов URL, содержащих URL-адреса приложений:

...

urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/customers/$', views.customers_list),
url(r'^api/customers/(?P<pk>[0-9]+)$', views.customers_detail),
]

Создав конечные точки REST, давайте посмотрим, как их можно использовать.

Шаг 6 — Использование REST API с помощью Axios

На этом этапе мы установим Axios — HTTP-клиент, который будем использовать для выполнения вызовов API. Мы также создадим класс для использования созданных нами конечных точек API.

Сначала деактивируйте вашу виртуальную среду:

deactivate

Затем перейдите в папку вашего frontend:

cd ~/djangoreactproject/frontend

Установите axios из npm с помощью:

npm install axios --save

Параметр –save добавляет зависимость axios в файл package.json вашего приложения.

Затем создайте JavaScript-файл CustomersService.js, содержащий код для вызова REST API. Мы разместим его в папке src, где находится код приложения нашего проекта:

cd src
nano CustomersService.js

Добавьте следующий код, содержащий методы для подключения к Django REST API:

import axios from 'axios';
const API_URL = 'http://localhost:8000';

export default class CustomersService{

constructor(){}


getCustomers() {
const url = `${API_URL}/api/customers/`;
return axios.get(url).then(response => response.data);
}
getCustomersByURL(link){
const url = `${API_URL}${link}`;
return axios.get(url).then(response => response.data);
}
getCustomer(pk) {
const url = `${API_URL}/api/customers/${pk}`;
return axios.get(url).then(response => response.data);
}
deleteCustomer(customer){
const url = `${API_URL}/api/customers/${customer.pk}`;
return axios.delete(url);
}
createCustomer(customer){
const url = `${API_URL}/api/customers/`;
return axios.post(url,customer);
}
updateCustomer(customer){
const url = `${API_URL}/api/customers/${customer.pk}`;
return axios.put(url,customer);
}
}

Класс CustomersService вызывает следующие методы Axios:

  • getCustomers(): Получает первую страницу клиентов.
  • getCustomersByURL(): Получает клиентов по URL. Это позволяет получить доступ к последующим страницам клиентов, передавая ссылки типа /api/customers/?page=2.
  • getCustomer(): получает клиента по первичному ключу.
  • createCustomer(): создает клиента.
  • updateCustomer(): обновляет клиента.
  • deleteCustomer(): удаляет клиента.

Теперь мы можем отобразить данные нашего API в пользовательском интерфейсе React, создав компонент CustomersList.

Шаг 7 — Отображение данных из API в приложении React

На этом этапе мы создадим React-компонент CustomersList. React-компонент представляет собой часть пользовательского интерфейса. Он также позволяет разделить пользовательский интерфейс на независимые, повторно используемые части.

Начните с создания CustomersList.js в frontend/src:

nano ~/djangoreactproject/frontend/src/CustomersList.js

Начните с импорта React и Component, чтобы создать компонент React:

import React, { Component } from 'react';

Затем импортируйте и создайте экземпляр модуля CustomersService, созданного на предыдущем шаге, который предоставляет методы, взаимодействующие с внутренним REST API:

...
import CustomersService from './CustomersService';
const customersService = new CustomersService();

Затем создайте компонент CustomersList, расширяющий класс Component для вызова REST API. Компонент React должен расширять класс Component или создавать от него подклассы. Подробнее о классах E6 и наследовании см. в нашем руководстве по классам в JavaScript.

Добавьте следующий код для создания компонента React, расширяющего react.Component:

...
class CustomersList extends Component {

constructor(props) {
super(props);
this.state = {
customers: [],
nextPageURL: ''
};
this.nextPage = this.nextPage.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
}
export default CustomersList;

Внутри конструктора мы инициализируем объект состояния. Он хранит переменные состояния нашего компонента, используя пустой массив клиентов. Этот массив содержит клиентов и nextPageURL, содержащий URL следующей страницы, которую можно получить из вспомогательного API. Мы также присоединяем к нему методы nextPage() и handleDelete(), чтобы они были доступны из HTML-кода.

Затем добавьте метод componentDidMount() и вызов getCustomers() в классе CustomersList перед закрывающей фигурной скобкой.

Метод componentDidMount() — это метод жизненного цикла компонента, который вызывается при создании компонента и вставке его в DOM. getCustomers() вызывает объект Customer Service для получения первой страницы данных и ссылки на следующую страницу из бэкэнда Django:

...
componentDidMount() {
var self = this;
customersService.getCustomers().then(function (result) {
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}

Теперь добавьте метод handleDelete(), который обрабатывает удаление клиента, под componentDidMount():

...
handleDelete(e,pk){
var self = this;
customersService.deleteCustomer({pk : pk}).then(()=>{
var newArr = self.state.customers.filter(function(obj) {
return obj.pk !== pk;
});
self.setState({customers: newArr})
});
}

Метод handleDelete вызывает метод deleteCustomer() для удаления клиента по его первичному ключу (pk). Если операция выполнена успешно, массив customer фильтруется по удалённому клиенту.

Затем добавьте метод nextPage() для получения данных для следующей страницы и обновите ссылку на следующую страницу:

...
nextPage(){
var self = this;
customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}

Метод nextPage() вызывает метод getCustomersByURL(), который получает URL-адрес следующей страницы из объекта состояния this.state.nextPageURL и обновляет массив клиентов возвращенными данными.

Наконец, добавьте метод render() компонента, который отображает таблицу клиентов из состояния компонента:

...
render() {

return (
<div className="customers--list">
<table className="table">
<thead key="thead">
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Phone</th>
<th>Email</th>
<th>Address</th>
<th>Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{this.state.customers.map( c =>
<tr key={c.pk}>
<td>{c.pk} </td>
<td>{c.first_name}</td>
<td>{c.last_name}</td>
<td>{c.phone}</td>
<td>{c.email}</td>
<td>{c.address}</td>
<td>{c.description}</td>
<td>
<button onClick={(e)=> this.handleDelete(e,c.pk) }> Delete</button>
<a href={"/customer/" + c.pk}> Update</a>
</td>
</tr>)}
</tbody>
</table>
<button className="btn btn-primary" onClick= { this.nextPage }>Next</button>
</div>
);
}

Вот полное содержимое файла:

import React, { Component } from 'react';
import CustomersService from './CustomersService';

const customersService = new CustomersService();

class CustomersList extends Component {

constructor(props) {
super(props);
this.state = {
customers: [],
nextPageURL: ''
};
this.nextPage = this.nextPage.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}

componentDidMount() {
var self = this;
customersService.getCustomers().then(function (result) {
console.log(result);
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}
handleDelete(e,pk){
var self = this;
customersService.deleteCustomer({pk : pk}).then(()=>{
var newArr = self.state.customers.filter(function(obj) {
return obj.pk !== pk;
});

self.setState({customers: newArr})
});
}

nextPage(){
var self = this;
console.log(this.state.nextPageURL);
customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}
render() {

return (
<div className="customers--list">
<table className="table">
<thead key="thead">
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Phone</th>
<th>Email</th>
<th>Address</th>
<th>Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{this.state.customers.map( c =>
<tr key={c.pk}>
<td>{c.pk} </td>
<td>{c.first_name}</td>
<td>{c.last_name}</td>
<td>{c.phone}</td>
<td>{c.email}</td>
<td>{c.address}</td>
<td>{c.description}</td>
<td>
<button onClick={(e)=> this.handleDelete(e,c.pk) }> Delete</button>
<a href={"/customer/" + c.pk}> Update</a>
</td>
</tr>)}
</tbody>
</table>
<button className="btn btn-primary" onClick= { this.nextPage }>Next</button>
</div>
);
}
}
export default CustomersList;

Теперь, когда мы создали компонент CustomersList для отображения списка клиентов, мы можем добавить компонент, который обрабатывает создание и обновление клиентов.

Шаг 8 — Добавьте компонент React Create и обновите клиент

На этом этапе мы создаём компонент CustomerCreateUpdate, который отвечает за создание и обновление данных клиентов. Он предоставляет форму, которую пользователи могут использовать для ввода данных новых клиентов или обновления существующих записей.

В frontend/src создайте файл CustomerCreateUpdate.js:

nano ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

Добавьте следующий код для создания компонента React путем импорта React и Component:

import React, { Component } from 'react';

Мы также можем импортировать и создать экземпляр класса CustomersService, созданного на предыдущем шаге, который предоставляет методы, взаимодействующие с внутренним REST API:

...
import CustomersService from './CustomersService';

const customersService = new CustomersService();

Затем создайте компонент CustomerCreateUpdate, который расширяет Component для создания и обновления клиентов:

...
class CustomerCreateUpdate extends Component {

constructor(props) {
super(props);
}
}
export default CustomerCreateUpdate;

В определении класса добавьте метод render() компонента, который отображает HTML-форму, собирающую информацию о клиенте:

...
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>
First Name:</label>
<input className="form-control" type="text" ref='firstName' />

<label>
Last Name:</label>
<input className="form-control" type="text" ref='lastName'/>

<label>
Phone:</label>
<input className="form-control" type="text" ref='phone' />

<label>
Email:</label>
<input className="form-control" type="text" ref='email' />

<label>
Address:</label>
<input className="form-control" type="text" ref='address' />

<label>
Description:</label>
<textarea className="form-control" ref='description' ></textarea>

<input className="btn btn-primary" type="submit" value="Submit" />
</div>
</form>
);
}

Для каждого элемента ввода формы метод добавляет атрибут ref для доступа и установки значения элемента формы.

Затем определите метод handleSubmit(event) над методом render(), чтобы обеспечить соответствующее поведение, когда пользователь нажимает кнопку «Отправить»:

...
handleSubmit(event) {
const { match: { params } } = this.props;
if(params && params.pk){
this.handleUpdate(params.pk);
}
else
{
this.handleCreate();
}
event.preventDefault();
}
...

Метод handleSubmit(event) обрабатывает отправку формы и, в зависимости от маршрута, вызывает либо метод handleUpdate(pk) для обновления клиента с переданным pk, либо метод handleCreate() для создания нового клиента. Мы определим эти методы ниже.

Вернитесь в конструктор компонента и свяжите с ним недавно добавленный метод handleSubmit(), чтобы иметь к нему доступ в форме:

...
class CustomerCreateUpdate extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
...

Затем определите метод handleCreate() для создания клиента на основе данных формы. Добавьте следующий код над методом handleSubmit(event):

...
handleCreate(){
customersService.createCustomer(
{
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
}).then((result)=>{
alert("Customer created!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}
...

Метод handleCreate() используется для создания клиента на основе входных данных. Он вызывает соответствующий метод CustomersService.createCustomer(), который, в свою очередь, обращается к API бэкенда для создания клиента.

Затем под методом handleCreate определите метод handleUpdate(pk) для реализации обновлений:

...
handleUpdate(pk){
customersService.updateCustomer(
{
"pk": pk,
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
}
).then((result)=>{

alert("Customer updated!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}

Метод updateCustomer() обновляет данные о клиенте, используя новую информацию из формы. Он вызывает метод customersService.updateCustomer().

Затем добавьте метод componentDidMount(). Если пользователь посещает маршрут customer/:pk, мы хотим заполнить форму информацией о клиенте, используя первичный ключ из URL-адреса. Для этого можно добавить метод getCustomer(pk) после монтирования компонента в событии жизненного цикла componentDidMount(). Чтобы добавить этот метод, добавьте следующий код под конструктором компонента:

...
componentDidMount(){
const { match: { params } } = this.props;
if(params && params.pk)
{
customersService.getCustomer(params.pk).then((c)=>{
this.refs.firstName.value = c.first_name;
this.refs.lastName.value = c.last_name;
this.refs.email.value = c.email;
this.refs.phone.value = c.phone;
this.refs.address.value = c.address;
this.refs.description.value = c.description;
})
}
}

Вот полное содержимое файла:

import React, { Component } from 'react';
import CustomersService from './CustomersService';
const customersService = new CustomersService();
class CustomerCreateUpdate extends Component {
constructor(props) {
super(props);

this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount(){
const { match: { params } } = this.props;
if(params && params.pk)
{
customersService.getCustomer(params.pk).then((c)=>{
this.refs.firstName.value = c.first_name;
this.refs.lastName.value = c.last_name;
this.refs.email.value = c.email;
this.refs.phone.value = c.phone;
this.refs.address.value = c.address;
this.refs.description.value = c.description;
})
}
}
handleCreate(){
customersService.createCustomer(
{
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
} 
).then((result)=>{
alert("Customer created!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}
handleUpdate(pk){
customersService.updateCustomer(
{
"pk": pk,
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
} 
).then((result)=>{
console.log(result);
alert("Customer updated!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}
handleSubmit(event) {
const { match: { params } } = this.props;
if(params && params.pk){
this.handleUpdate(params.pk);
}
else
{
this.handleCreate();
}
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>
First Name:</label>
<input className="form-control" type="text" ref='firstName' />

<label>
Last Name:</label>
<input className="form-control" type="text" ref='lastName'/>

<label>
Phone:</label>
<input className="form-control" type="text" ref='phone' />

<label>
Email:</label>
<input className="form-control" type="text" ref='email' />

<label>
Address:</label>
<input className="form-control" type="text" ref='address' />

<label>
Description:</label>
<textarea className="form-control" ref='description' ></textarea>

<input className="btn btn-primary" type="submit" value="Submit" />
</div>
</form>
);
} 
}
export default CustomerCreateUpdate;

Создав компонент CustomerCreateUpdate, мы можем обновить основной компонент приложения, добавив ссылки на различные созданные нами компоненты.

Шаг 9 — Обновление основного компонента приложения

В этом разделе мы обновим наш компонент приложения, чтобы связать его с компонентами, созданными на предыдущих этапах.

Из папки frontend выполните следующую команду, чтобы установить маршрутизатор React, который позволит добавить маршрутизацию и навигацию между различными компонентами React:

cd ~/djangoreactproject/frontend
npm install --save react-router-dom

Далее откройте ~/djangoreactproject/frontend/src/App.js:

nano ~/djangoreactproject/frontend/src/App.js

Удалите все существующие элементы и добавьте следующий код для импорта необходимых классов для добавления маршрутизации. Это BrowserRouter, создающий компонент Router, и Route, создающий компонент Route:

import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom'
import { Route, Link } from 'react-router-dom'
import CustomersList from './CustomersList'
import CustomerCreateUpdate from './CustomerCreateUpdate'
import './App.css';

BrowserRouter синхронизирует пользовательский интерфейс с URL-адресом, используя API истории HTML5.

Затем создайте базовый макет, который предоставит базовый компонент, который будет обернут компонентом BrowserRouter:

...
const BaseLayout = () => (
<div className="container-fluid">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand" href="#">Django React Demo</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<a className="nav-item nav-link" href="/">CUSTOMERS</a>
<a className="nav-item nav-link" href="/customer">CREATE CUSTOMER</a>
</div>
</div>
</nav>
<div className="content">
<Route path="/" exact component={CustomersList} />
<Route path="/customer/:pk" component={CustomerCreateUpdate} />
<Route path="/customer/" exact component={CustomerCreateUpdate} />
</div>
</div>
)

Мы используем компонент Route для определения маршрутов нашего приложения. Этот компонент маршрутизатор должен загрузить сразу после обнаружения совпадения. Для каждого маршрута требуется маршрут, указывающий маршрут для сопоставления, и компонент, указывающий компонент для сопоставления. Атрибут exact указывает маршрутизатору точное сопоставление маршрута.

Наконец, создайте компонент App, основной или компонент верхнего уровня нашего приложения React:

...
class App extends Component {
render() {
return (
<BrowserRouter>
<BaseLayout/>
</BrowserRouter>
);
}
}
export default App;

Мы заключили компонент BaseLayout в компонент BrowserRouter, поскольку наше приложение будет работать в браузере.

Готовый файл выглядит так:

import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom'
import { Route, Link } from 'react-router-dom'
import CustomersList from './CustomersList'
import CustomerCreateUpdate from './CustomerCreateUpdate'
import './App.css';
const BaseLayout = () => (
<div className="container-fluid">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand" href="#">Django React Demo</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<a className="nav-item nav-link" href="/">CUSTOMERS</a>
<a className="nav-item nav-link" href="/customer">CREATE CUSTOMER</a>

</div>
</div>
</nav>
<div className="content">
<Route path="/" exact component={CustomersList} />
<Route path="/customer/:pk" component={CustomerCreateUpdate} />
<Route path="/customer/" exact component={CustomerCreateUpdate} />
</div>
</div>
)
class App extends Component {
render() {
return (
<BrowserRouter>
<BaseLayout/>
</BrowserRouter>
);
}
}
export default App;

После добавления маршрутизации в наше приложение мы готовы к его тестированию. Перейдите по адресу http://localhost:3000. Вы увидите домашнюю страницу приложения:

Благодаря этой программе у вас теперь есть основа для CRM-программы.

Результат

В этом руководстве вы создали демонстрационное приложение с использованием Django и React. Для создания REST API вы использовали фреймворк Django REST, для использования API — Axios, а для CSS-стилей — Bootstrap 4. Исходный код этого проекта можно найти в этом репозитории GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может понравиться

Программное обеспечение для автоматического отключения VPN

Что такое программное обеспечение для автоматического отключения VPN? Программное обеспечение для автоматического отключения VPN — это передовая мера безопасности, которая…