В этом уроке мы собираемся создать приложение CRUD для хранения книг с помощью React и Django . CRUD расшифровывается как Create, Read, Update и Delete, поэтому в основном это приложение будет работать как библиотечная полка, где мы можем взглянуть на данные каждой книги, которые можно изменять по своему усмотрению или необходимости.
Используемые технологии
Для этого приложения мы будем использовать два очень популярных решения для создания пользовательских интерфейсов и API. Пользовательский интерфейс приложения будет построен с использованием React — для этого урока не требуется предварительное знание React.
Серверная часть для этого приложения будет построена с использованием Django, который представляет собой веб-фреймворк Python высокого уровня, который способствует быстрой разработке и чистому прагматичному дизайну. Создавать API в Django легко с помощью Django Rest Framework.
Мы будем строить приложение в три этапа:
1. Создание внешнего интерфейса в React
2. Создание API в Django
3. Соединение внешнего интерфейса с API
Начало работы: настройка среды разработки
Для этого урока мы будем использовать Eclipse IDE с установленным плагином CodeMix . Тем не менее, не стесняйтесь следовать в любой IDE, которая поддерживает разработку как React, так и Python.
- Загрузите Eclipse IDE здесь .
- CodeMix можно установить с торговой площадки Eclipse или через genuitec.com .
- Если пакет React еще не установлен, установите его с помощью браузера расширений CodeMix (Справка> Расширения CodeMix). Его также можно установить прямо с торговой площадки Eclipse.
Если вы ищете IDE для разработки с такими фреймворками, как Angular, React и Vue.js, или с такими языками, как Python и Rust, CodeMix поможет вам. Codemix совместим со всеми IDE и инструментами на основе Eclipse, такими как MyEclipse , Spring Tools Suite и JBoss Tools.
Кроме того, нам нужно установить Python в нашей системе, поэтому давайте удостоверимся, что он установлен правильно. Его можно скачать здесь .
Создание внешнего интерфейса в React
Мы создадим наше приложение React с помощью мастера CodeMix.
Чтобы создать приложение, выберите « Файл»> «Создать»> «Проект»> «CodeMix»> «Реактировать проект».
В появившемся диалоговом окне введите response-book-app в качестве имени приложения, затем нажмите «Готово».
Структура папок для вашего нового приложения реакции, созданного CodeMix, будет выглядеть следующим образом:
Дополнительные пакеты
Как видно из структуры папок, папка node_modules отсутствует. Следовательно, нам нужно установить пакеты узлов. Используя Terminal + , запустите:
npm install
Мы будем использовать bootstrap и fontawesome для некоторых дополнительных стилей и для некоторых иконок.
Добавьте следующее в раздел <head> в src / index.js :
HTML
1
<link rel="stylesheet" href="https://bootswatch.com/4/lux/bootstrap.min.css">
2
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
Построение интерфейса
React — это библиотека, управляемая компонентами. Поэтому нам нужно разбить наше приложение на разные компоненты. Вот интерфейс, который мы пытаемся построить:
Интерфейс можно разбить на разные компоненты:
Компонент Book Dashboard : это родительский компонент в приложении. Он содержит компонент списка книг и компонент формы переключаемой книги.
Компонент списка книг : на этот компонент возложена ответственность перечислять все книги, созданные в приложении. Он содержит список редактируемых компонентов книги.
- Компонент «Редактируемая книга» : визуализирует компонент «Книга» и компонент «Форма книги». Если щелкнуть значок редактирования, компонент «Редактируемая книга» отобразит компонент «Форма книги», который позволяет редактировать созданные книги.
- Компонент книги : этот компонент показывает детали книги.
- Компонент «Форма книги» . Этот компонент показывает форму, позволяющую создавать или редактировать книгу.
Компонент формы переключаемой книги. Этот компонент отвечает за создание новых книг. При нажатии кнопки «+» будет отображен компонент «Форма книги», который позволит нам создать новую книгу.
Приложение сможет обрабатывать все 4 операции CRUD (Create Read Update Delete).
Создание компонентов
Мы создадим компоненты иерархически — из родительского компонента. Первым компонентом, который будет создан, является компонент Book Dashboard.
Измените файл src / index.js так, чтобы он выглядел следующим образом:
HTML
xxxxxxxxxx
1
class BookDashboard extends React.Component {
2
state = {
3
books: [
4
{
5
id: 1,
6
title: 'A simple book',
7
author: 'Jude Ben',
8
description: `Lorem ipsum dolor sit amet, consectetur
9
adipiscing elit, sed do eiusmod tempor incididunt
10
ut labore et dolore magna aliqua. Ut enim ad minim
11
veniam, quis nostrud`
12
},
13
{
14
id: 2,
15
title: 'A book of secrets',
16
author: 'James John',
17
description: `Sed ut perspiciatis unde omnis iste natus
18
error sit voluptatem accusantium doloremque laudantium,
19
totam rem aperiam, eaque ipsa quae ab illo inventore
20
veritatis et quasi architecto beatae vitae dicta sunt
21
explicabo.`
22
}
23
]
24
}
25
createNewBook = (book) => {
27
book.id = Math.floor(Math.random() * 1000);
28
this.setState({books: this.state.books.concat([book])});
29
}
30
updateBook = (newBook) => {
32
const newBooks = this.state.books.map(book => {
33
if(book.id === newBook.id) {
34
return Object.assign({}, newBook)
35
} else {
36
return book;
37
}
38
});
39
this.setState({books: newBooks});
41
}
42
deleteBook = (bookId) => {
44
this.setState({books: this.state.books.filter(book => book.id !== bookId)})
45
}
46
render() {
47
return (
48
<main className="d-flex justify-content-center my-4">
49
<div className="col-5">
50
<BookList
51
books={this.state.books}
52
onDeleteClick={this.deleteBook}
53
onUpdateClick={this.updateBook}
54
></BookList>
55
<ToggleableBookForm
56
onBookCreate={this.createNewBook}
57
></ToggleableBookForm>
58
</div>
59
</main>
60
)
61
}
62
}
Как видно выше, компонент BookDashboard хранит книги в состоянии. Затем книги передаются в качестве реквизита компоненту BookList вместе с некоторыми функциями для удаления и обновления книг.
BookDashboard также визуализирует ToggleableBookForm и передает функцию в качестве опоры, которая будет обрабатывать создание книг. Реагировать должны быть неизменны, поэтому мы идем в наши функции обработчика: , , которые обновляют состояние без мутирует начальное содержимое книги.createNewBook
updateBook
deleteBook
Далее нам нужно создать компонент BookList. Для этого добавьте следующий код в index.js .
HTML
xxxxxxxxxx
1
class BookList extends React.Component {
2
render() {
3
const books = this.props.books.map(book => (
4
<EditableBook
5
key={book.id}
6
id={book.id}
7
title={book.title}
8
author={book.author}
9
description={book.description}
10
onDeleteClick={this.props.onDeleteClick}
11
onUpdateClick={this.props.onUpdateClick}
12
></EditableBook>
13
));
14
return (
15
<div>
16
{books}
17
</div>
18
);
19
}
20
}
21
class EditableBook extends React.Component {
23
state = {
24
inEditMode: false
25
};
26
enterEditMode = () => {
27
this.setState({inEditMode: true});
28
}
29
leaveEditMode = () => {
30
this.setState({inEditMode: false});
31
}
32
handleDelete = () => {
33
this.props.onDeleteClick(this.props.id);
34
}
35
handleUpdate = (book) => {
36
this.leaveEditMode()
37
book.id = this.props.id;
38
this.props.onUpdateClick(book);
39
}
40
render() {
41
const component = () => {
42
if(this.state.inEditMode) {
43
return (
44
<BookForm
45
id={this.props.id}
46
title={this.props.title}
47
author={this.props.author}
48
description={this.props.description}
49
onCancelClick={this.leaveEditMode}
50
onFormSubmit={this.handleUpdate}
51
/>
52
);
53
}
54
return (
55
<Book
56
title={this.props.title}
57
author={this.props.author}
58
description={this.props.description}
59
onEditClick={this.enterEditMode}
60
onDeleteClick={this.handleDelete}
61
/>
62
)
63
}
64
return (
65
<div className="mb-3 p-4" style={{boxShadow: '0 0 10px #ccc'}} >
66
{component()}
67
</div>
68
)
69
}
70
}
BookList
Компонент принимает книги в качестве опоры, карты над ней , и возвращает массив EditableBook
компонентов , которые затем оказываемые. EditableBook
является компонентом, который отображает книгу или BookForm, если пользователь нажимает кнопку редактирования книги на Book
компоненте. Это компонент с состоянием, который хранит состояние режима редактирования в состоянии и, используя это значение, знает, какой компонент визуализировать.
Компонент книги получает информацию о книге в качестве реквизита. Он также получает некоторые функции, которые обрабатывают нажатие кнопки редактирования и кнопки удаления.
Давайте создадим Book
и BookForm
компоненты.
HTML
1
class Book extends React.Component {
2
render() {
3
return (
4
<div className="card" /* style="width: 18rem;" *></div>
5
<div className="card-header d-flex justify-content-between">
6
<span>
7
<strong>Title: </strong>{this.props.title}
8
</span>
9
<div>
10
<span onClick={this.props.onEditClick} className="mr-2"><i className="far fa-edit"></i></span>
11
<span onClick={this.props.onDeleteClick}><i className="fas fa-trash"></i></span>
12
</div>
13
</div>
14
<div className="card-body">
15
{this.props.description}
16
</div>
17
<div className="card-footer">
18
<strong>Author:</strong> {this.props.author}
19
</div>
20
</div>
21
);
22
}
23
}
24
class BookForm extends React.Component {
26
state = {
27
title: this.props.title || '',
28
author: this.props.author || '',
29
description: this.props.description || ''
30
}
31
handleFormSubmit = (evt) => {
32
evt.preventDefault();
33
this.props.onFormSubmit({...this.state});
34
}
35
handleTitleUpdate = (evt) => {
36
this.setState({title: evt.target.value});
37
}
38
handleAuthorUpdate = (evt) => {
39
this.setState({author: evt.target.value});
40
}
41
handleDescriptionUpdate = (evt) => {
42
this.setState({description: evt.target.value});
43
}
44
render() {
45
const buttonText = this.props.id ? 'Update Book': 'Create Book';
46
return (
47
<form onSubmit={this.handleFormSubmit}>
48
<div className="form-group">
49
<label>
50
Title
51
</label>
52
<input type="text" placeholder="Enter a title"
53
value={this.state.title} onChange={this.handleTitleUpdate}
54
className="form-control"
55
/>
56
</div>
57
<div className="form-group">
59
<label>
60
Author
61
</label>
62
<input type="text" placeholder="Author's name"
63
value={this.state.author} onChange={this.handleAuthorUpdate}
64
className="form-control"
65
/>
66
</div>
67
<div className="form-group">
69
<label>
70
Description
71
</label>
72
<textarea className="form-control" placeholder="Book Description"
73
rows="5" value={this.state.description}
74
onChange={this.handleDescriptionUpdate}
75
>
76
{this.state.description}
77
</textarea>
78
</div>
79
<div className="form-group d-flex justify-content-between">
80
<button type="submit" className="btn btn-md btn-primary">
81
{buttonText}
82
</button>
83
<button type="button" className="btn btn-md btn-secondary" onClick={this.props.onCancelClick}>
84
Cancel
85
</button>
86
</div>
87
</form>
88
)
89
}
90
}
Book
Компонент просто делает разметку для отображения книг.
Все необходимые данные и обработчики для действий редактирования и удаления получены из родительского компонента - EditableBook
.
BookForm
Компонент, с другой стороны, делает форму , которая содержит книги деталь из реквизита для обновления операции.
Для операции создания , которая будет обрабатываться ToggleableBookForm
, никакие реквизиты не будут отправлены, форма становится реактивной, вызывая setState
при изменении поля ввода. Мы делаем это, создавая разные обработчики для каждого поля и прослушивая событие change.
Функция для обработки событий отправки в этой форме будет отправлена в виде подпорки от родительского компонента - EditableBook
или ToggleableBookForm
.
Последний компонент, который будет построен ToggleableBookForm
, это, этот компонент будет использоваться для создания книг.
JavaScript
xxxxxxxxxx
1
// index.js
2
class ToggleableBookForm extends React.Component {
3
state = {
4
inCreateMode: false
5
}
6
handleCreateClick = () => {
7
this.setState({inCreateMode: true});
8
}
9
leaveCreateMode = () => {
10
this.setState({inCreateMode: false});
11
}
12
handleCancleClick = () => {
13
this.leaveCreateMode();
14
}
15
handleFormSubmit = (book) => {
16
this.leaveCreateMode();
17
this.props.onBookCreate(book);
18
}
19
render() {
20
if (this.state.inCreateMode) {
21
return (
22
<div className="mb-3 p-4" style={{boxShadow: '0 0 10px #ccc'}} >
23
<BookForm
24
onFormSubmit={this.handleFormSubmit}
25
onCancelClick={this.handleCancleClick}></BookForm>
26
</div>
27
)
29
}
30
return (
31
<button onClick={this.handleCreateClick} className="btn btn-secondary">
32
<i className="fas fa-plus"></i>
33
</button>
34
);
35
}
36
}
Как описано ранее, ToggleableBookForm
отображается кнопка, которую пользователь нажимает, чтобы создать книгу. Когда кнопка нажата, BookForm
компонент отображается.
Наконец, мы визуализируем BookDashboard
компонент, используя ReactDOM.
JavaScript
xxxxxxxxxx
1
// index.js
2
ReactDOM.render(<BookDashboard />, document.getElementById('root'));
Перезагружая браузер, приложение book должно работать как положено.
Наше приложение работает !!!
Однако данные не сохраняются при перезагрузке страницы. Это потому, что данные хранятся в структурах данных в нашем приложении - состояние . Чтобы сохранить данные, нам нужно какое-то решение для хранения данных, с которым наше приложение React может взаимодействовать. Для этого мы создадим простой REST API с Python с использованием фреймворка Django.
Создание API книг с помощью Django
Мы создадим API книг, используя Django. Давайте создадим новый фиктивный проект:
Если вы еще не установили pipenv , вам нужно будет установить его, запустив в терминале следующее:
pip install pipenv
Используя встроенный терминал, активируйте оболочку pipenv для создания проекта Django:
pipenv shell
Когда закончите, выполните следующие команды для установки Django и Django REST Framework:
Оболочка
xxxxxxxxxx
1
pipenv install django
2
pipenv install django-rest-framework
Затем мы создаем проект Django и создаем приложение Django.
Оболочка
xxxxxxxxxx
1
django-admin startproject booksApi
2
cd booksApi/
3
django-admin startapp books
Это создаст новые папки в папке books-api . Обновите INSTALLLED_APPS в settings.py, чтобы включить книги и django-rest-framework.
питон
xxxxxxxxxx
1
# books-api/booksApi/booksApi/settings.py
2
INSTALLED_APPS = [
4
'django.contrib.admin',
5
'django.contrib.auth',
6
'django.contrib.contenttypes',
7
'django.contrib.sessions',
8
'django.contrib.messages',
9
'django.contrib.staticfiles',
10
'rest_framework',
11
'books',
12
]
Наша модель базы данных будет просто содержать Книжную модель. Добавьте следующее в models.py.
питон
xxxxxxxxxx
1
# books-api/booksApi/books/model.py.
2
from django.db import models
4
# Create your models here.
6
class Book(models.Model):
8
title = models.CharField(max_length=200)
9
description = models.TextField()
10
author = models.CharField(max_length=200)
11
created_at = models.DateTimeField(auto_now_add=True)
12
def __str__(self):
14
return '%s by %s' % (self.title, self.author)
Наша модель Book содержит 4 поля: название, описание, автор и create_at.
- В поле заголовка хранится название книги.
- Описание хранит описание книги.
- Поле автора хранит автора книги.
Эти поля будут предоставлены из нашего интерфейса - приложения реакции, которое мы создали ранее.
- Поле made_at будет автоматически вставлено в зависимости от времени создания книги.
Выполните следующую команду, чтобы создать необходимые миграции и таблицы.
В Terminal + убедитесь, что вы находитесь в books-api / booksApi /, затем запустите:
Оболочка
xxxxxxxxxx
1
python manage.py makemigrations
2
python manage.py migrate
Далее мы создадим сериализатор. Сериализаторы используются для преобразования наших данных в JSON, который будет возвращен, когда мы посетим конечные точки.
питон
xxxxxxxxxx
1
# books-api/booksApi/books/serializers.py.
2
from rest_framework import serializers
3
from .models import Book
4
class BookSerializer(serializers.ModelSerializer):
7
class Meta:
8
model = Book
9
fields = ('id', 'title', 'author', 'description', 'created_at')
Далее, мы обновляем views.py, чтобы добавить наши представления API.
питон
xxxxxxxxxx
1
# books-api/booksApi/books/views.py.
2
from .models import Book
4
from .serializers import BookSerializer
5
from rest_framework import generics
6
class BookList(generics.ListCreateAPIView):
8
queryset = Book.objects.all()
9
serializer_class = BookSerializer
10
class BookDetail(generics.RetrieveUpdateDestroyAPIView):
12
queryset = Book.objects.all()
13
serializer_class = BookSerializer
Наконец, нам нужно добавить наши конечные точки. Создайте urls.py в books-api / booksApi / books
питон
xxxxxxxxxx
1
# books-api/booksApi/books/urls.py.
2
from django.urls import path
3
from rest_framework.urlpatterns import format_suffix_patterns
4
from books import views
5
urlpatterns = [
7
path('books/', views.BookList.as_view()),
8
path('books/<int:pk>/', views.BookDetail.as_view()),
9
]
И обновите books-api / booksApi / booksApi / urls.py, чтобы они выглядели так:
питон
xxxxxxxxxx
1
# books-api/booksApi/booksApi/urls.py.
2
"""booksApi URL Configuration
3
The `urlpatterns` list routes URLs to views. For more information please see:
5
https://docs.djangoproject.com/en/3.0/topics/http/urls/
6
Examples:
7
Function views
8
1. Add an import: from my_app import views
9
2. Add a URL to urlpatterns: path('', views.home, name='home')
10
Class-based views
11
1. Add an import: from other_app.views import Home
12
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13
Including another URLconf
14
1. Import the include() function: from django.urls import include, path
15
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16
"""
17
from django.contrib import admin
18
from django.urls import path
19
urlpatterns = [
21
path('admin/', admin.site.urls),
22
path('api/', include('books.urls')),
23
]
Запустите сервер Django, запустив приведенный ниже фрагмент в Terminal +:
Оболочка
xxxxxxxxxx
1
python manage.py runserver
Теперь конечную точку можно посмотреть по адресу http: // localhost: 8000 / api / books / . Прежде чем использовать API в нашем приложении реагирования, нам нужно решить одну проблему, которая, вероятно, возникнет - CORS. Выполните следующее, чтобы установить пакет для помощи с CORS:
Оболочка
xxxxxxxxxx
1
pipenv install django-cors-headers
Когда это будет сделано, добавьте следующее в список INSTALLED_APPS в settings.py, как мы делали ранее. Нам также нужно будет добавить новое промежуточное программное обеспечение.
питон
xxxxxxxxxx
1
# books-api/booksApi/booksApi/settings.py
2
INSTALLED_APPS = [
4
'django.contrib.admin',
5
'django.contrib.auth',
6
'django.contrib.contenttypes',
7
'django.contrib.sessions',
8
'django.contrib.messages',
9
'django.contrib.staticfiles',
10
'rest_framework',
11
'books',
12
'corsheaders',
13
]
15
MIDDLEWARE = [
17
'django.middleware.security.SecurityMiddleware',
18
'django.contrib.sessions.middleware.SessionMiddleware',
19
'django.middleware.common.CommonMiddleware',
20
'django.middleware.csrf.CsrfViewMiddleware',
21
'django.contrib.auth.middleware.AuthenticationMiddleware',
22
'django.contrib.messages.middleware.MessageMiddleware',
23
'django.middleware.clickjacking.XFrameOptionsMiddleware',
24
'corsheaders.middleware.CorsMiddleware',
25
'django.middleware.common.CommonMiddleware',
26
]
И, наконец, добавьте строку ниже, чтобы разрешить всем источникам доступ к API:
питон
xxxxxxxxxx
1
# books-api/booksApi/booksApi/settings.py
2
CORS_ORIGIN_ALLOW_ALL = True
Запустите сервер, используя встроенный терминал:
Оболочка
xxxxxxxxxx
1
python manage.py runserver
Использование API с React
Мы будем использовать fetch для использования API книг. Добавьте следующее в index.js :
JavaScript
xxxxxxxxxx
1
class BookDashboard extends React.Component {
2
state = {
3
books: []
4
}
5
componentDidMount() {
7
fetch('http://localhost:8000/api/books/')
8
.then(response => response.json())
9
.then(data => {
10
this.setState({books: data});
11
});
12
}
13
14
}
Как показано выше, мы удаляем исходное содержимое массива books и просто инициализируем его пустым массивом.
Используя componentDidMount
hook, мы извлекаем содержимое книги api и в строке 10 обновляем состояние, чтобы использовать полученные данные. В настоящее время в базе данных нет книг, поэтому каждый вызов http: // localhost: 8000 / api / books / будет возвращать пустой массив.
Обновите createNewBook
функцию, чтобы использовать API для создания книг:
JavaScript
xxxxxxxxxx
1
createNewBook = (book) => {
2
fetch('http://localhost:8000/api/books/', {
3
method: 'POST',
4
headers: {
5
'Content-Type': 'application/json',
6
},
7
body: JSON.stringify(book),
8
})
9
.then(response => response.json())
10
.then(book => {
11
this.setState({books: this.state.books.concat([book])});
12
});
13
}
createNewBook
отправляет запрос POST в API книг с данными книги, полученными из формы. API отвечает вновь созданной книгой, содержащей некоторые дополнительные данные, такие как id и made_at.
Далее нам нужно изменить книги - обновить. Чтобы обновить книгу, нам нужно отправить запрос PUT.
JavaScript
xxxxxxxxxx
1
updateBook = (newBook) => {
2
fetch(`http://localhost:8000/api/books/${newBook.id}/`, {
3
method: 'PUT',
4
headers: {
5
'Content-Type': 'application/json',
6
},
7
body: JSON.stringify(newBook),
8
}).then(response => response.json())
9
.then(newBook => {
10
const newBooks = this.state.books.map(book => {
11
if(book.id === newBook.id) {
12
return Object.assign({}, newBook)
13
} else {
14
return book;
15
}
16
});
17
this.setState({books: newBooks});
18
});
Как видно выше, мы отправляем запрос на размещение по URL / <bookId>. Это показывает, какую книгу мы хотим обновить. Если обновление прошло успешно, мы обновляем состояние.
Мы используем тот же механизм обновления состояния, что и раньше. Возможно, вы захотите отправить еще один запрос, чтобы получить все книги из API, но это займет больше времени.
Последний метод, который нужно изменить - это действие удаления, например:
JavaScript
xxxxxxxxxx
1
deleteBook = (bookId) => {
2
fetch(`http://localhost:8000/api/books/${bookId}/`, {
3
method: 'DELETE',
4
headers: {
5
'Content-Type': 'application/json',
6
},
7
})
8
.then(() => {
9
this.setState({books: this.state.books.filter(book => book.id !== bookId)})
10
});
11
}
Действие удаления, вероятно, самое простое. Мы отправляем запрос DELETE на тот же URL-адрес, который использовался для обновления, действие delete не возвращает никаких данных, поэтому мы можем просто обновить состояние.
Заключение
С помощью этого функционального приложения CRUD мы только что узнали, как создать простой REST API с использованием инфраструктуры Django и внешнего интерфейса в React, который использует API. Если вы хотите поближе взглянуть на код, взгляните на Github-репозиторий для React-кодовой базы и для Django API .
Если вы еще не пробовали CodeMix , поэкспериментируйте со всеми функциями, которые он может предложить! Станьте частью сообщества с программой CodeMix Insiders или используйте встроенный LiveChat для получения полной поддержки от команды CodeMix прямо в вашей IDE.
У вас есть вопросы или предложения по поводу этой статьи или наших услуг? Дайте нам знать ваши мысли через Twitter или через форумы CodeMix . Удачного кодирования!