As a developer, CRUD operations are one of the most fundamental concepts. Today, we’ll learn how to build a REST API using Django and Django Rest and a SPA with React, which we’ll use to perform the CRUD operations.
Project Setup
Firstly, we must set up the development environment. Pick up your favorite terminal and make sure you have virtualenv installed.
Once it’s done, create an environment and install Django and Django rest framework.
virtualenv --python=/usr/bin/python3.10 venvsource venv/bin/activatepip install django django-rest-frameworkvirtualenv --python=/usr/bin/python3.10 venv source venv/bin/activate pip install django django-rest-frameworkvirtualenv --python=/usr/bin/python3.10 venv source venv/bin/activate pip install django django-rest-framework
Enter fullscreen mode Exit fullscreen mode
After the installation of the packages, we can create the project and start working.
django-admin startproject restaurant .django-admin startproject restaurant .django-admin startproject restaurant .
Enter fullscreen mode Exit fullscreen mode
Note: Don’t forget the dot at the end of this command. It will generate the directories and files in the current directory instead of developing them in a new directory, restaurant
.
To ensure that the project has been well initiated, try python manage.py runserver
. And hit 127.0.0.1:8000
.
Now let’s create a Django app.
python manage.py startapp menupython manage.py startapp menupython manage.py startapp menu
Enter fullscreen mode Exit fullscreen mode
So make sure to add the menu
app and rest_framework
in the INSTALLED_APPS
in settings.py
file.
<span>#restaurant/settings.py </span><span>INSTALLED_APPS</span> <span>=</span> <span>[</span><span>'</span><span>django.contrib.admin</span><span>'</span><span>,</span><span>'</span><span>django.contrib.auth</span><span>'</span><span>,</span><span>'</span><span>django.contrib.contenttypes</span><span>'</span><span>,</span><span>'</span><span>django.contrib.sessions</span><span>'</span><span>,</span><span>'</span><span>django.contrib.messages</span><span>'</span><span>,</span><span>'</span><span>django.contrib.staticfiles</span><span>'</span><span>,</span><span>'</span><span>rest_framework</span><span>'</span><span>,</span><span>'</span><span>menu</span><span>'</span><span>]</span><span>#restaurant/settings.py </span><span>INSTALLED_APPS</span> <span>=</span> <span>[</span> <span>'</span><span>django.contrib.admin</span><span>'</span><span>,</span> <span>'</span><span>django.contrib.auth</span><span>'</span><span>,</span> <span>'</span><span>django.contrib.contenttypes</span><span>'</span><span>,</span> <span>'</span><span>django.contrib.sessions</span><span>'</span><span>,</span> <span>'</span><span>django.contrib.messages</span><span>'</span><span>,</span> <span>'</span><span>django.contrib.staticfiles</span><span>'</span><span>,</span> <span>'</span><span>rest_framework</span><span>'</span><span>,</span> <span>'</span><span>menu</span><span>'</span> <span>]</span>#restaurant/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'menu' ]
Enter fullscreen mode Exit fullscreen mode
Good. We can start working on the logic we want to achieve in this tutorial. So, we’ll write Menu
:
- Model
- Serializer
- ViewSet
- And finally, configure routes.
Model
The Menu
model will only contain 5 fields.
<span>#menu/models.py </span><span>from</span> <span>django.db</span> <span>import</span> <span>models</span><span>class</span> <span>Menu</span><span>(</span><span>models</span><span>.</span><span>Model</span><span>):</span><span>name</span> <span>=</span> <span>models</span><span>.</span><span>CharField</span><span>(</span><span>max_length</span><span>=</span><span>255</span><span>)</span><span>description</span> <span>=</span> <span>models</span><span>.</span><span>TextField</span><span>()</span><span>price</span> <span>=</span> <span>models</span><span>.</span><span>IntegerField</span><span>()</span><span>created</span> <span>=</span> <span>models</span><span>.</span><span>DateTimeField</span><span>(</span><span>auto_now_add</span><span>=</span><span>True</span><span>)</span><span>updated</span> <span>=</span> <span>models</span><span>.</span><span>DateTimeField</span><span>(</span><span>auto_now</span><span>=</span><span>True</span><span>)</span><span>def</span> <span>__str__</span><span>(</span><span>self</span><span>):</span><span>return</span> <span>self</span><span>.</span><span>name</span><span>#menu/models.py </span><span>from</span> <span>django.db</span> <span>import</span> <span>models</span> <span>class</span> <span>Menu</span><span>(</span><span>models</span><span>.</span><span>Model</span><span>):</span> <span>name</span> <span>=</span> <span>models</span><span>.</span><span>CharField</span><span>(</span><span>max_length</span><span>=</span><span>255</span><span>)</span> <span>description</span> <span>=</span> <span>models</span><span>.</span><span>TextField</span><span>()</span> <span>price</span> <span>=</span> <span>models</span><span>.</span><span>IntegerField</span><span>()</span> <span>created</span> <span>=</span> <span>models</span><span>.</span><span>DateTimeField</span><span>(</span><span>auto_now_add</span><span>=</span><span>True</span><span>)</span> <span>updated</span> <span>=</span> <span>models</span><span>.</span><span>DateTimeField</span><span>(</span><span>auto_now</span><span>=</span><span>True</span><span>)</span> <span>def</span> <span>__str__</span><span>(</span><span>self</span><span>):</span> <span>return</span> <span>self</span><span>.</span><span>name</span>#menu/models.py from django.db import models class Menu(models.Model): name = models.CharField(max_length=255) description = models.TextField() price = models.IntegerField() created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self): return self.name
Enter fullscreen mode Exit fullscreen mode
Once it’s done, let’s create a migration and apply it.
Migrations are Django’s way of propagating changes made to the models (adding a field, deleting a field, deleting a table, creating a table, etc.) into your database.
python manage.py makemigrationspython manage.py migratepython manage.py makemigrations python manage.py migratepython manage.py makemigrations python manage.py migrate
Enter fullscreen mode Exit fullscreen mode
Serializers
Serializers allows us to convert complex Django complex data structures such as querysets
or model instances in Python native objects that can be converted to JSON/XML format.
We’ll create a serializer to convert our data into JSON format.
<span>#menu/serializers.py </span><span>from</span> <span>rest_framework</span> <span>import</span> <span>serializers</span><span>from</span> <span>menu.models</span> <span>import</span> <span>Menu</span><span>class</span> <span>MenuSerializer</span><span>(</span><span>serializers</span><span>.</span><span>ModelSerializer</span><span>):</span><span>class</span> <span>Meta</span><span>:</span><span>model</span> <span>=</span> <span>Menu</span><span>fields</span> <span>=</span> <span>[</span><span>'</span><span>id</span><span>'</span><span>,</span> <span>'</span><span>name</span><span>'</span><span>,</span> <span>'</span><span>description</span><span>'</span><span>,</span> <span>'</span><span>price</span><span>'</span><span>,</span> <span>'</span><span>created</span><span>'</span><span>,</span> <span>'</span><span>updated</span><span>'</span><span>]</span><span>#menu/serializers.py </span> <span>from</span> <span>rest_framework</span> <span>import</span> <span>serializers</span> <span>from</span> <span>menu.models</span> <span>import</span> <span>Menu</span> <span>class</span> <span>MenuSerializer</span><span>(</span><span>serializers</span><span>.</span><span>ModelSerializer</span><span>):</span> <span>class</span> <span>Meta</span><span>:</span> <span>model</span> <span>=</span> <span>Menu</span> <span>fields</span> <span>=</span> <span>[</span><span>'</span><span>id</span><span>'</span><span>,</span> <span>'</span><span>name</span><span>'</span><span>,</span> <span>'</span><span>description</span><span>'</span><span>,</span> <span>'</span><span>price</span><span>'</span><span>,</span> <span>'</span><span>created</span><span>'</span><span>,</span> <span>'</span><span>updated</span><span>'</span><span>]</span>#menu/serializers.py from rest_framework import serializers from menu.models import Menu class MenuSerializer(serializers.ModelSerializer): class Meta: model = Menu fields = ['id', 'name', 'description', 'price', 'created', 'updated']
Enter fullscreen mode Exit fullscreen mode
Viewsets
Viewsets can be referred to as Controllers if you are coming from another framework.
ViewSet is a concept developed by DRF which consists of grouping a set of views for a given model in a single Python class.
This set of views corresponds to the predefined actions of CRUD type (Create, Read, Update, Delete), associated with HTTP methods.
Each of these actions is a ViewSet instance method. Among these default actions, we find:
- list
- retrieve
- update
- destroy
- partial_update
- create
<span>#menu/viewsets.py </span><span>from</span> <span>rest_framework</span> <span>import</span> <span>viewsets</span><span>from</span> <span>menu.models</span> <span>import</span> <span>Menu</span><span>from</span> <span>menu.serializers</span> <span>import</span> <span>MenuSerializer</span><span>class</span> <span>MenuViewSet</span><span>(</span><span>viewsets</span><span>.</span><span>ModelViewSet</span><span>):</span><span>serializer_class</span> <span>=</span> <span>MenuSerializer</span><span>def</span> <span>get_queryset</span><span>(</span><span>self</span><span>):</span><span>return</span> <span>Menu</span><span>.</span><span>objects</span><span>.</span><span>all</span><span>()</span><span>#menu/viewsets.py </span><span>from</span> <span>rest_framework</span> <span>import</span> <span>viewsets</span> <span>from</span> <span>menu.models</span> <span>import</span> <span>Menu</span> <span>from</span> <span>menu.serializers</span> <span>import</span> <span>MenuSerializer</span> <span>class</span> <span>MenuViewSet</span><span>(</span><span>viewsets</span><span>.</span><span>ModelViewSet</span><span>):</span> <span>serializer_class</span> <span>=</span> <span>MenuSerializer</span> <span>def</span> <span>get_queryset</span><span>(</span><span>self</span><span>):</span> <span>return</span> <span>Menu</span><span>.</span><span>objects</span><span>.</span><span>all</span><span>()</span>#menu/viewsets.py from rest_framework import viewsets from menu.models import Menu from menu.serializers import MenuSerializer class MenuViewSet(viewsets.ModelViewSet): serializer_class = MenuSerializer def get_queryset(self): return Menu.objects.all()
Enter fullscreen mode Exit fullscreen mode
Great. We have the logic set, but we must add the API endpoints.
First, create a file, routers.py
.
<span>#./routers.py </span><span>from</span> <span>rest_framework</span> <span>import</span> <span>routers</span><span>from</span> <span>menu.viewsets</span> <span>import</span> <span>MenuViewSet</span><span>router</span> <span>=</span> <span>routers</span><span>.</span><span>SimpleRouter</span><span>()</span><span>router</span><span>.</span><span>register</span><span>(</span><span>r</span><span>'</span><span>menu</span><span>'</span><span>,</span> <span>MenuViewSet</span><span>,</span> <span>basename</span><span>=</span><span>'</span><span>menu</span><span>'</span><span>)</span><span>#restaurant/urls.py </span><span>from</span> <span>django.contrib</span> <span>import</span> <span>admin</span><span>from</span> <span>django.urls</span> <span>import</span> <span>path</span><span>,</span> <span>include</span><span>from</span> <span>routers</span> <span>import</span> <span>router</span><span>urlpatterns</span> <span>=</span> <span>[</span><span># path('admin/', admin.site.urls), </span> <span>path</span><span>(</span><span>'</span><span>api/</span><span>'</span><span>,</span> <span>include</span><span>((</span><span>router</span><span>.</span><span>urls</span><span>,</span> <span>'</span><span>restaurant</span><span>'</span><span>),</span> <span>namespace</span><span>=</span><span>'</span><span>restaurant</span><span>'</span><span>))</span><span>]</span><span>#./routers.py </span><span>from</span> <span>rest_framework</span> <span>import</span> <span>routers</span> <span>from</span> <span>menu.viewsets</span> <span>import</span> <span>MenuViewSet</span> <span>router</span> <span>=</span> <span>routers</span><span>.</span><span>SimpleRouter</span><span>()</span> <span>router</span><span>.</span><span>register</span><span>(</span><span>r</span><span>'</span><span>menu</span><span>'</span><span>,</span> <span>MenuViewSet</span><span>,</span> <span>basename</span><span>=</span><span>'</span><span>menu</span><span>'</span><span>)</span> <span>#restaurant/urls.py </span><span>from</span> <span>django.contrib</span> <span>import</span> <span>admin</span> <span>from</span> <span>django.urls</span> <span>import</span> <span>path</span><span>,</span> <span>include</span> <span>from</span> <span>routers</span> <span>import</span> <span>router</span> <span>urlpatterns</span> <span>=</span> <span>[</span> <span># path('admin/', admin.site.urls), </span> <span>path</span><span>(</span><span>'</span><span>api/</span><span>'</span><span>,</span> <span>include</span><span>((</span><span>router</span><span>.</span><span>urls</span><span>,</span> <span>'</span><span>restaurant</span><span>'</span><span>),</span> <span>namespace</span><span>=</span><span>'</span><span>restaurant</span><span>'</span><span>))</span> <span>]</span>#./routers.py from rest_framework import routers from menu.viewsets import MenuViewSet router = routers.SimpleRouter() router.register(r'menu', MenuViewSet, basename='menu') #restaurant/urls.py from django.contrib import admin from django.urls import path, include from routers import router urlpatterns = [ # path('admin/', admin.site.urls), path('api/', include((router.urls, 'restaurant'), namespace='restaurant')) ]
Enter fullscreen mode Exit fullscreen mode
If you haven’t started your server yet.
python manage.py runserverpython manage.py runserverpython manage.py runserver
Enter fullscreen mode Exit fullscreen mode
Then hit http://127.0.0.1:8000/api/menu/
in your browser.
Your browsable API is ready.
Let’s add CORS responses. Adding CORS headers allows other domains to access the API ressources.
pip install django-cors-headerspip install django-cors-headerspip install django-cors-headers
Enter fullscreen mode Exit fullscreen mode
Then, add it to the INSTALLED_APPS
.
<span># restaurant/settings.py </span><span>INSTALLED_APPS</span> <span>=</span> <span>[</span><span>...</span><span>'</span><span>corsheaders</span><span>'</span><span>,</span><span>...</span><span>]</span><span># restaurant/settings.py </span><span>INSTALLED_APPS</span> <span>=</span> <span>[</span> <span>...</span> <span>'</span><span>corsheaders</span><span>'</span><span>,</span> <span>...</span> <span>]</span># restaurant/settings.py INSTALLED_APPS = [ ... 'corsheaders', ... ]
Enter fullscreen mode Exit fullscreen mode
You will also need to add a middleware class to listen in on responses.
<span>#restaurant/settings.py </span><span>MIDDLEWARE</span> <span>=</span> <span>[</span><span>'</span><span>corsheaders.middleware.CorsMiddleware</span><span>'</span><span>,</span><span>'</span><span>django.middleware.security.SecurityMiddleware</span><span>'</span><span>,</span><span>'</span><span>django.contrib.sessions.middleware.SessionMiddleware</span><span>'</span><span>,</span><span>'</span><span>django.middleware.common.CommonMiddleware</span><span>'</span><span>,</span><span>...</span><span>]</span><span>#restaurant/settings.py </span><span>MIDDLEWARE</span> <span>=</span> <span>[</span> <span>'</span><span>corsheaders.middleware.CorsMiddleware</span><span>'</span><span>,</span> <span>'</span><span>django.middleware.security.SecurityMiddleware</span><span>'</span><span>,</span> <span>'</span><span>django.contrib.sessions.middleware.SessionMiddleware</span><span>'</span><span>,</span> <span>'</span><span>django.middleware.common.CommonMiddleware</span><span>'</span><span>,</span> <span>...</span> <span>]</span>#restaurant/settings.py MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', ... ]
Enter fullscreen mode Exit fullscreen mode
We’ll allow requests coming from localhost:3000
and 127.0.0.1:3000
because the frontend React server will run at these addresses.
<span># restaurant/settings.py </span><span># CORS HEADERS </span><span>CORS_ALLOWED_ORIGINS</span> <span>=</span> <span>[</span><span>'</span><span>http://127.0.0.1:3000</span><span>'</span><span>,</span><span>'</span><span>http://localhost:3000</span><span>'</span><span>]</span><span># restaurant/settings.py </span> <span># CORS HEADERS </span><span>CORS_ALLOWED_ORIGINS</span> <span>=</span> <span>[</span> <span>'</span><span>http://127.0.0.1:3000</span><span>'</span><span>,</span> <span>'</span><span>http://localhost:3000</span><span>'</span> <span>]</span># restaurant/settings.py # CORS HEADERS CORS_ALLOWED_ORIGINS = [ 'http://127.0.0.1:3000', 'http://localhost:3000' ]
Enter fullscreen mode Exit fullscreen mode
React.js CRUD REST API Consumption
Make sure you have the latest version of create-react-app installed.
yarn create-react-app restaurant-menu-frontcd restaurant-menu-frontyarn startyarn create-react-app restaurant-menu-front cd restaurant-menu-front yarn startyarn create-react-app restaurant-menu-front cd restaurant-menu-front yarn start
Enter fullscreen mode Exit fullscreen mode
Then open http://localhost:3000/ to check the running application.
We can now add the dependencies of this project.
yarn add axios bootstrap react-router-domyarn add axios bootstrap react-router-domyarn add axios bootstrap react-router-dom
Enter fullscreen mode Exit fullscreen mode
With this line of command, we installed :
- axios : a promised based HTTP client
- bootstrap: a library to prototype an app without writing too much CSS
- react-router-dom : a React library for routes in our application.
Inside the src/
folder, ensure you have the following files and directories.
In the src/components/
directory, we have three components :
AddMenu.js
UpdateMenu.js
MenuList.js
And in src/services/
directory, create menu.service.js
and the following lines :
<span>export</span> <span>const</span> <span>baseURL</span> <span>=</span> <span>"</span><span>http://localhost:8000/api</span><span>"</span><span>;</span><span>export</span> <span>const</span> <span>headers</span> <span>=</span> <span>{</span><span>"</span><span>Content-type</span><span>"</span><span>:</span> <span>"</span><span>application/json</span><span>"</span><span>,</span><span>};</span><span>export</span> <span>const</span> <span>baseURL</span> <span>=</span> <span>"</span><span>http://localhost:8000/api</span><span>"</span><span>;</span> <span>export</span> <span>const</span> <span>headers</span> <span>=</span> <span>{</span> <span>"</span><span>Content-type</span><span>"</span><span>:</span> <span>"</span><span>application/json</span><span>"</span><span>,</span> <span>};</span>export const baseURL = "http://localhost:8000/api"; export const headers = { "Content-type": "application/json", };
Enter fullscreen mode Exit fullscreen mode
Make sure to import react-router-dom
in your index.js
file and wrap App
in BrowserRouter
object.
<span>import</span> <span>React</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span><span>import</span> <span>ReactDOM</span> <span>from</span> <span>"</span><span>react-dom/client</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>BrowserRouter</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span><span>import</span> <span>"</span><span>./index.css</span><span>"</span><span>;</span><span>import</span> <span>App</span> <span>from</span> <span>"</span><span>./App</span><span>"</span><span>;</span><span>const</span> <span>root</span> <span>=</span> <span>ReactDOM</span><span>.</span><span>createRoot</span><span>(</span><span>document</span><span>.</span><span>getElementById</span><span>(</span><span>"</span><span>root</span><span>"</span><span>));</span><span>root</span><span>.</span><span>render</span><span>(</span><span><</span><span>React</span><span>.</span><span>StrictMode</span><span>></span><span><</span><span>BrowserRouter</span><span>></span><span><</span><span>App</span> <span>/></span><span><</span><span>/BrowserRouter</span><span>> </span> <span><</span><span>/React.StrictMode</span><span>> </span><span>);</span><span>import</span> <span>React</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span> <span>import</span> <span>ReactDOM</span> <span>from</span> <span>"</span><span>react-dom/client</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>BrowserRouter</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span> <span>import</span> <span>"</span><span>./index.css</span><span>"</span><span>;</span> <span>import</span> <span>App</span> <span>from</span> <span>"</span><span>./App</span><span>"</span><span>;</span> <span>const</span> <span>root</span> <span>=</span> <span>ReactDOM</span><span>.</span><span>createRoot</span><span>(</span><span>document</span><span>.</span><span>getElementById</span><span>(</span><span>"</span><span>root</span><span>"</span><span>));</span> <span>root</span><span>.</span><span>render</span><span>(</span> <span><</span><span>React</span><span>.</span><span>StrictMode</span><span>></span> <span><</span><span>BrowserRouter</span><span>></span> <span><</span><span>App</span> <span>/></span> <span><</span><span>/BrowserRouter</span><span>> </span> <span><</span><span>/React.StrictMode</span><span>> </span><span>);</span>import React from "react"; import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import "./index.css"; import App from "./App"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> );
Enter fullscreen mode Exit fullscreen mode
Once it’s done, we can change the App.js
file by importing bootstrap
, writing routes, and building the home page and the navigation bar.
<span>import</span> <span>React</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span><span>import</span> <span>"</span><span>bootstrap/dist/css/bootstrap.min.css</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>Routes</span><span>,</span> <span>Route</span><span>,</span> <span>Link</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>AddMenu</span> <span>}</span> <span>from</span> <span>"</span><span>./components/AddMenu</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>MenuList</span> <span>}</span> <span>from</span> <span>"</span><span>./components/MenuList</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>UpdateMenu</span> <span>}</span> <span>from</span> <span>"</span><span>./components/UpdateMenu</span><span>"</span><span>;</span><span>function</span> <span>App</span><span>()</span> <span>{</span><span>return </span><span>(</span><span><</span><span>div</span><span>></span><span><</span><span>nav</span> <span>className</span><span>=</span><span>"</span><span>navbar navbar-expand navbar-dark bg-info</span><span>"</span><span>></span><span><</span><span>a</span> <span>href</span><span>=</span><span>"</span><span>/</span><span>"</span> <span>className</span><span>=</span><span>"</span><span>navbar-brand</span><span>"</span><span>></span><span>Restaurant</span> <span>Menu</span><span><</span><span>/a</span><span>> </span> <span><</span><span>div</span> <span>className</span><span>=</span><span>"</span><span>navbar-nav mr-auto</span><span>"</span><span>></span><span><</span><span>li</span> <span>className</span><span>=</span><span>"</span><span>nav-item</span><span>"</span><span>></span><span><</span><span>Link</span> <span>to</span><span>=</span><span>{</span><span>"</span><span>/add/</span><span>"</span><span>}</span> <span>className</span><span>=</span><span>"</span><span>nav-link</span><span>"</span><span>></span><span>Add</span> <span>a</span> <span>menu</span><span><</span><span>/Link</span><span>> </span> <span><</span><span>/li</span><span>> </span> <span><</span><span>/div</span><span>> </span> <span><</span><span>/nav</span><span>> </span><span><</span><span>div</span> <span>className</span><span>=</span><span>"</span><span>container m-10</span><span>"</span><span>></span><span>// Adding the routes </span><span><</span><span>/div</span><span>> </span> <span><</span><span>/div</span><span>> </span> <span>);</span><span>}</span><span>export</span> <span>default</span> <span>App</span><span>;</span><span>import</span> <span>React</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span> <span>import</span> <span>"</span><span>bootstrap/dist/css/bootstrap.min.css</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>Routes</span><span>,</span> <span>Route</span><span>,</span> <span>Link</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>AddMenu</span> <span>}</span> <span>from</span> <span>"</span><span>./components/AddMenu</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>MenuList</span> <span>}</span> <span>from</span> <span>"</span><span>./components/MenuList</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>UpdateMenu</span> <span>}</span> <span>from</span> <span>"</span><span>./components/UpdateMenu</span><span>"</span><span>;</span> <span>function</span> <span>App</span><span>()</span> <span>{</span> <span>return </span><span>(</span> <span><</span><span>div</span><span>></span> <span><</span><span>nav</span> <span>className</span><span>=</span><span>"</span><span>navbar navbar-expand navbar-dark bg-info</span><span>"</span><span>></span> <span><</span><span>a</span> <span>href</span><span>=</span><span>"</span><span>/</span><span>"</span> <span>className</span><span>=</span><span>"</span><span>navbar-brand</span><span>"</span><span>></span> <span>Restaurant</span> <span>Menu</span> <span><</span><span>/a</span><span>> </span> <span><</span><span>div</span> <span>className</span><span>=</span><span>"</span><span>navbar-nav mr-auto</span><span>"</span><span>></span> <span><</span><span>li</span> <span>className</span><span>=</span><span>"</span><span>nav-item</span><span>"</span><span>></span> <span><</span><span>Link</span> <span>to</span><span>=</span><span>{</span><span>"</span><span>/add/</span><span>"</span><span>}</span> <span>className</span><span>=</span><span>"</span><span>nav-link</span><span>"</span><span>></span> <span>Add</span> <span>a</span> <span>menu</span> <span><</span><span>/Link</span><span>> </span> <span><</span><span>/li</span><span>> </span> <span><</span><span>/div</span><span>> </span> <span><</span><span>/nav</span><span>> </span> <span><</span><span>div</span> <span>className</span><span>=</span><span>"</span><span>container m-10</span><span>"</span><span>></span> <span>// Adding the routes </span> <span><</span><span>/div</span><span>> </span> <span><</span><span>/div</span><span>> </span> <span>);</span> <span>}</span> <span>export</span> <span>default</span> <span>App</span><span>;</span>import React from "react"; import "bootstrap/dist/css/bootstrap.min.css"; import { Routes, Route, Link } from "react-router-dom"; import { AddMenu } from "./components/AddMenu"; import { MenuList } from "./components/MenuList"; import { UpdateMenu } from "./components/UpdateMenu"; function App() { return ( <div> <nav className="navbar navbar-expand navbar-dark bg-info"> <a href="/" className="navbar-brand"> Restaurant Menu </a> <div className="navbar-nav mr-auto"> <li className="nav-item"> <Link to={"/add/"} className="nav-link"> Add a menu </Link> </li> </div> </nav> <div className="container m-10"> // Adding the routes </div> </div> ); } export default App;
Enter fullscreen mode Exit fullscreen mode
We’ll need to write the routes that should map to a component we created.
<span><</span><span>div</span> <span>className</span><span>=</span><span>"</span><span>container m-10</span><span>"</span><span>></span><span><</span><span>Routes</span><span>></span><span><</span><span>Route</span> <span>path</span><span>=</span><span>"</span><span>/</span><span>"</span> <span>element</span><span>=</span><span>{</span><span><</span><span>MenuList</span> <span>/></span><span>}</span> <span>/</span><span>> </span> <span><</span><span>Route</span> <span>path</span><span>=</span><span>"</span><span>/add/</span><span>"</span> <span>element</span><span>=</span><span>{</span><span><</span><span>AddMenu</span> <span>/></span><span>}</span> <span>/</span><span>> </span> <span><</span><span>Route</span> <span>path</span><span>=</span><span>"</span><span>/menu/:id/update/</span><span>"</span> <span>element</span><span>=</span><span>{</span><span><</span><span>UpdateMenu</span> <span>/></span><span>}</span> <span>/</span><span>> </span> <span><</span><span>/Routes</span><span>> </span><span><</span><span>/div</span><span>> </span><span><</span><span>div</span> <span>className</span><span>=</span><span>"</span><span>container m-10</span><span>"</span><span>></span> <span><</span><span>Routes</span><span>></span> <span><</span><span>Route</span> <span>path</span><span>=</span><span>"</span><span>/</span><span>"</span> <span>element</span><span>=</span><span>{</span><span><</span><span>MenuList</span> <span>/></span><span>}</span> <span>/</span><span>> </span> <span><</span><span>Route</span> <span>path</span><span>=</span><span>"</span><span>/add/</span><span>"</span> <span>element</span><span>=</span><span>{</span><span><</span><span>AddMenu</span> <span>/></span><span>}</span> <span>/</span><span>> </span> <span><</span><span>Route</span> <span>path</span><span>=</span><span>"</span><span>/menu/:id/update/</span><span>"</span> <span>element</span><span>=</span><span>{</span><span><</span><span>UpdateMenu</span> <span>/></span><span>}</span> <span>/</span><span>> </span> <span><</span><span>/Routes</span><span>> </span><span><</span><span>/div</span><span>> </span><div className="container m-10"> <Routes> <Route path="/" element={<MenuList />} /> <Route path="/add/" element={<AddMenu />} /> <Route path="/menu/:id/update/" element={<UpdateMenu />} /> </Routes> </div>
Enter fullscreen mode Exit fullscreen mode
The next step is to write the CRUD logic and the HTML for our components.
Let’s start by listing the menu from the API in MenuList.js
.
For this script, we’ll have two states :
-
menus
which will store the response object from the API -
deleted
that will contain a Boolean object to show a message
And three methods :
-
retrieveAllMenus()
to retrieve all menus from the API and set the response objects in menus usingsetMenus
. -
deleteMenu()
to delete a menu and set thedeleted
state totrue
, which will help us show a simple message every time a menu is deleted. -
handleUpdateClick()
to navigate to a new page to update a menu.
<span>import</span> <span>axios</span> <span>from</span> <span>"</span><span>axios</span><span>"</span><span>;</span><span>import</span> <span>React</span><span>,</span> <span>{</span> <span>useState</span><span>,</span> <span>useEffect</span> <span>}</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>baseURL</span><span>,</span> <span>headers</span> <span>}</span> <span>from</span> <span>"</span><span>./../services/menu.service</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>useNavigate</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span><span>export</span> <span>const</span> <span>MenuList</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>const</span> <span>[</span><span>menus</span><span>,</span> <span>setMenus</span><span>]</span> <span>=</span> <span>useState</span><span>([]);</span><span>const</span> <span>navigate</span> <span>=</span> <span>useNavigate</span><span>();</span><span>const</span> <span>[</span><span>deleted</span><span>,</span> <span>setDeleted</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>false</span><span>);</span><span>const</span> <span>retrieveAllMenus</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>axios</span><span>.</span><span>get</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/`</span><span>,</span> <span>{</span><span>headers</span><span>:</span> <span>{</span><span>headers</span><span>,</span><span>},</span><span>})</span><span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span><span>setMenus</span><span>(</span><span>response</span><span>.</span><span>data</span><span>);</span><span>console</span><span>.</span><span>log</span><span>(</span><span>menus</span><span>);</span><span>})</span><span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span><span>});</span><span>};</span><span>const</span> <span>deleteMenu</span> <span>=</span> <span>(</span><span>id</span><span>)</span> <span>=></span> <span>{</span><span>axios</span><span>.</span><span>delete</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/</span><span>${</span><span>id</span><span>}</span><span>/`</span><span>,</span> <span>{</span><span>headers</span><span>:</span> <span>{</span><span>headers</span><span>,</span><span>},</span><span>})</span><span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span><span>setDeleted</span><span>(</span><span>true</span><span>);</span><span>retrieveAllMenus</span><span>();</span><span>})</span><span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span><span>});</span><span>};</span><span>useEffect</span><span>(()</span> <span>=></span> <span>{</span><span>retrieveAllMenus</span><span>();</span><span>},</span> <span>[</span><span>retrieveAllMenus</span><span>]);</span><span>const</span> <span>handleUpdateClick</span> <span>=</span> <span>(</span><span>id</span><span>)</span> <span>=></span> <span>{</span><span>navigate</span><span>(</span><span>`/menu/</span><span>${</span><span>id</span><span>}</span><span>/update/`</span><span>);</span><span>};</span><span>return </span><span>(</span><span>// ...</span><span>);</span><span>};</span><span>import</span> <span>axios</span> <span>from</span> <span>"</span><span>axios</span><span>"</span><span>;</span> <span>import</span> <span>React</span><span>,</span> <span>{</span> <span>useState</span><span>,</span> <span>useEffect</span> <span>}</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>baseURL</span><span>,</span> <span>headers</span> <span>}</span> <span>from</span> <span>"</span><span>./../services/menu.service</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>useNavigate</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span> <span>export</span> <span>const</span> <span>MenuList</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>const</span> <span>[</span><span>menus</span><span>,</span> <span>setMenus</span><span>]</span> <span>=</span> <span>useState</span><span>([]);</span> <span>const</span> <span>navigate</span> <span>=</span> <span>useNavigate</span><span>();</span> <span>const</span> <span>[</span><span>deleted</span><span>,</span> <span>setDeleted</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>false</span><span>);</span> <span>const</span> <span>retrieveAllMenus</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>axios</span> <span>.</span><span>get</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/`</span><span>,</span> <span>{</span> <span>headers</span><span>:</span> <span>{</span> <span>headers</span><span>,</span> <span>},</span> <span>})</span> <span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span> <span>setMenus</span><span>(</span><span>response</span><span>.</span><span>data</span><span>);</span> <span>console</span><span>.</span><span>log</span><span>(</span><span>menus</span><span>);</span> <span>})</span> <span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span> <span>});</span> <span>};</span> <span>const</span> <span>deleteMenu</span> <span>=</span> <span>(</span><span>id</span><span>)</span> <span>=></span> <span>{</span> <span>axios</span> <span>.</span><span>delete</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/</span><span>${</span><span>id</span><span>}</span><span>/`</span><span>,</span> <span>{</span> <span>headers</span><span>:</span> <span>{</span> <span>headers</span><span>,</span> <span>},</span> <span>})</span> <span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span> <span>setDeleted</span><span>(</span><span>true</span><span>);</span> <span>retrieveAllMenus</span><span>();</span> <span>})</span> <span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span> <span>});</span> <span>};</span> <span>useEffect</span><span>(()</span> <span>=></span> <span>{</span> <span>retrieveAllMenus</span><span>();</span> <span>},</span> <span>[</span><span>retrieveAllMenus</span><span>]);</span> <span>const</span> <span>handleUpdateClick</span> <span>=</span> <span>(</span><span>id</span><span>)</span> <span>=></span> <span>{</span> <span>navigate</span><span>(</span><span>`/menu/</span><span>${</span><span>id</span><span>}</span><span>/update/`</span><span>);</span> <span>};</span> <span>return </span><span>(</span> <span>// ...</span> <span>);</span> <span>};</span>import axios from "axios"; import React, { useState, useEffect } from "react"; import { baseURL, headers } from "./../services/menu.service"; import { useNavigate } from "react-router-dom"; export const MenuList = () => { const [menus, setMenus] = useState([]); const navigate = useNavigate(); const [deleted, setDeleted] = useState(false); const retrieveAllMenus = () => { axios .get(`${baseURL}/menu/`, { headers: { headers, }, }) .then((response) => { setMenus(response.data); console.log(menus); }) .catch((e) => { console.error(e); }); }; const deleteMenu = (id) => { axios .delete(`${baseURL}/menu/${id}/`, { headers: { headers, }, }) .then((response) => { setDeleted(true); retrieveAllMenus(); }) .catch((e) => { console.error(e); }); }; useEffect(() => { retrieveAllMenus(); }, [retrieveAllMenus]); const handleUpdateClick = (id) => { navigate(`/menu/${id}/update/`); }; return ( // ... ); };
Enter fullscreen mode Exit fullscreen mode
Once it’s done, let’s put in place the return()
method:
<span><div</span> <span>className=</span><span>"row justify-content-center"</span><span>></span><span><div</span> <span>className=</span><span>"col"</span><span>></span>{deleted <span>&&</span> (<span><div</span><span>className=</span><span>"alert alert-danger alert-dismissible fade show"</span><span>role=</span><span>"alert"</span><span>></span>Menu deleted!<span><button</span><span>type=</span><span>"button"</span><span>className=</span><span>"close"</span><span>data-dismiss=</span><span>"alert"</span><span>aria-label=</span><span>"Close"</span><span>></span><span><span</span> <span>aria-hidden=</span><span>"true"</span><span>></span><span>×</span><span></span></span><span></button></span><span></div></span>)}{menus <span>&&</span>menus.map((menu, index) => (<span><div</span> <span>className=</span><span>"card my-3 w-25 mx-auto"</span><span>></span><span><div</span> <span>className=</span><span>"card-body"</span><span>></span><span><h2</span> <span>className=</span><span>"card-title font-weight-bold"</span><span>></span>{menu.name}<span></h2></span><span><h4</span> <span>className=</span><span>"card-subtitle mb-2"</span><span>></span>{menu.price}<span></h4></span><span><p</span> <span>className=</span><span>"card-text"</span><span>></span>{menu.description}<span></p></span><span></div></span><span><div</span> <span>classNameName=</span><span>"card-footer"</span><span>></span><span><div</span><span>className=</span><span>"btn-group justify-content-around w-75 mb-1 "</span><span>data-toggle=</span><span>"buttons"</span><span>></span><span><span></span><span><button</span><span>className=</span><span>"btn btn-info"</span><span>onClick=</span><span>{()</span> <span>=</span><span>></span> handleUpdateClick(menu.id)}>Update<span></button></span><span></span></span><span><span></span><span><button</span><span>className=</span><span>"btn btn-danger"</span><span>onClick=</span><span>{()</span> <span>=</span><span>></span> deleteMenu(menu.id)}>Delete<span></button></span><span></span></span><span></div></span><span></div></span><span></div></span>))}<span></div></span><span></div></span><span><div</span> <span>className=</span><span>"row justify-content-center"</span><span>></span> <span><div</span> <span>className=</span><span>"col"</span><span>></span> {deleted <span>&&</span> ( <span><div</span> <span>className=</span><span>"alert alert-danger alert-dismissible fade show"</span> <span>role=</span><span>"alert"</span> <span>></span> Menu deleted! <span><button</span> <span>type=</span><span>"button"</span> <span>className=</span><span>"close"</span> <span>data-dismiss=</span><span>"alert"</span> <span>aria-label=</span><span>"Close"</span> <span>></span> <span><span</span> <span>aria-hidden=</span><span>"true"</span><span>></span><span>×</span><span></span></span> <span></button></span> <span></div></span> )} {menus <span>&&</span> menus.map((menu, index) => ( <span><div</span> <span>className=</span><span>"card my-3 w-25 mx-auto"</span><span>></span> <span><div</span> <span>className=</span><span>"card-body"</span><span>></span> <span><h2</span> <span>className=</span><span>"card-title font-weight-bold"</span><span>></span>{menu.name}<span></h2></span> <span><h4</span> <span>className=</span><span>"card-subtitle mb-2"</span><span>></span>{menu.price}<span></h4></span> <span><p</span> <span>className=</span><span>"card-text"</span><span>></span>{menu.description}<span></p></span> <span></div></span> <span><div</span> <span>classNameName=</span><span>"card-footer"</span><span>></span> <span><div</span> <span>className=</span><span>"btn-group justify-content-around w-75 mb-1 "</span> <span>data-toggle=</span><span>"buttons"</span> <span>></span> <span><span></span> <span><button</span> <span>className=</span><span>"btn btn-info"</span> <span>onClick=</span><span>{()</span> <span>=</span><span>></span> handleUpdateClick(menu.id)} > Update <span></button></span> <span></span></span> <span><span></span> <span><button</span> <span>className=</span><span>"btn btn-danger"</span> <span>onClick=</span><span>{()</span> <span>=</span><span>></span> deleteMenu(menu.id)} > Delete <span></button></span> <span></span></span> <span></div></span> <span></div></span> <span></div></span> ))} <span></div></span> <span></div></span><div className="row justify-content-center"> <div className="col"> {deleted && ( <div className="alert alert-danger alert-dismissible fade show" role="alert" > Menu deleted! <button type="button" className="close" data-dismiss="alert" aria-label="Close" > <span aria-hidden="true">×</span> </button> </div> )} {menus && menus.map((menu, index) => ( <div className="card my-3 w-25 mx-auto"> <div className="card-body"> <h2 className="card-title font-weight-bold">{menu.name}</h2> <h4 className="card-subtitle mb-2">{menu.price}</h4> <p className="card-text">{menu.description}</p> </div> <div classNameName="card-footer"> <div className="btn-group justify-content-around w-75 mb-1 " data-toggle="buttons" > <span> <button className="btn btn-info" onClick={() => handleUpdateClick(menu.id)} > Update </button> </span> <span> <button className="btn btn-danger" onClick={() => deleteMenu(menu.id)} > Delete </button> </span> </div> </div> </div> ))} </div> </div>
Enter fullscreen mode Exit fullscreen mode
Add a menu
The AddMenu.js
component has a Form to submit a new menu. It contains three fields : name
, description
& price
.
<span>import</span> <span>axios</span> <span>from</span> <span>"</span><span>axios</span><span>"</span><span>;</span><span>import</span> <span>React</span><span>,</span> <span>{</span> <span>useState</span> <span>}</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>baseURL</span><span>,</span> <span>headers</span> <span>}</span> <span>from</span> <span>"</span><span>./../services/menu.service</span><span>"</span><span>;</span><span>export</span> <span>const</span> <span>AddMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>const</span> <span>initialMenuState</span> <span>=</span> <span>{</span><span>id</span><span>:</span> <span>null</span><span>,</span><span>name</span><span>:</span> <span>""</span><span>,</span><span>description</span><span>:</span> <span>""</span><span>,</span><span>price</span><span>:</span> <span>0</span><span>,</span><span>};</span><span>const</span> <span>[</span><span>menu</span><span>,</span> <span>setMenu</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>initialMenuState</span><span>);</span><span>const</span> <span>[</span><span>submitted</span><span>,</span> <span>setSubmitted</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>false</span><span>);</span><span>const</span> <span>handleMenuChange</span> <span>=</span> <span>(</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>const</span> <span>{</span> <span>name</span><span>,</span> <span>value</span> <span>}</span> <span>=</span> <span>e</span><span>.</span><span>target</span><span>;</span><span>setMenu</span><span>({</span> <span>...</span><span>menu</span><span>,</span> <span>[</span><span>name</span><span>]:</span> <span>value</span> <span>});</span><span>};</span><span>const</span> <span>submitMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>let</span> <span>data</span> <span>=</span> <span>{</span><span>name</span><span>:</span> <span>menu</span><span>.</span><span>name</span><span>,</span><span>description</span><span>:</span> <span>menu</span><span>.</span><span>description</span><span>,</span><span>price</span><span>:</span> <span>menu</span><span>.</span><span>price</span><span>,</span><span>};</span><span>axios</span><span>.</span><span>post</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/`</span><span>,</span> <span>data</span><span>,</span> <span>{</span><span>headers</span><span>:</span> <span>{</span><span>headers</span><span>,</span><span>},</span><span>})</span><span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span><span>setMenu</span><span>({</span><span>id</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>id</span><span>,</span><span>name</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>name</span><span>,</span><span>description</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>description</span><span>,</span><span>price</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>price</span><span>,</span><span>});</span><span>setSubmitted</span><span>(</span><span>true</span><span>);</span><span>console</span><span>.</span><span>log</span><span>(</span><span>response</span><span>.</span><span>data</span><span>);</span><span>})</span><span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span><span>});</span><span>};</span><span>const</span> <span>newMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>setMenu</span><span>(</span><span>initialMenuState</span><span>);</span><span>setSubmitted</span><span>(</span><span>false</span><span>);</span><span>};</span><span>return </span><span>(</span><span>// ...</span><span>);</span><span>};</span><span>import</span> <span>axios</span> <span>from</span> <span>"</span><span>axios</span><span>"</span><span>;</span> <span>import</span> <span>React</span><span>,</span> <span>{</span> <span>useState</span> <span>}</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>baseURL</span><span>,</span> <span>headers</span> <span>}</span> <span>from</span> <span>"</span><span>./../services/menu.service</span><span>"</span><span>;</span> <span>export</span> <span>const</span> <span>AddMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>const</span> <span>initialMenuState</span> <span>=</span> <span>{</span> <span>id</span><span>:</span> <span>null</span><span>,</span> <span>name</span><span>:</span> <span>""</span><span>,</span> <span>description</span><span>:</span> <span>""</span><span>,</span> <span>price</span><span>:</span> <span>0</span><span>,</span> <span>};</span> <span>const</span> <span>[</span><span>menu</span><span>,</span> <span>setMenu</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>initialMenuState</span><span>);</span> <span>const</span> <span>[</span><span>submitted</span><span>,</span> <span>setSubmitted</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>false</span><span>);</span> <span>const</span> <span>handleMenuChange</span> <span>=</span> <span>(</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>const</span> <span>{</span> <span>name</span><span>,</span> <span>value</span> <span>}</span> <span>=</span> <span>e</span><span>.</span><span>target</span><span>;</span> <span>setMenu</span><span>({</span> <span>...</span><span>menu</span><span>,</span> <span>[</span><span>name</span><span>]:</span> <span>value</span> <span>});</span> <span>};</span> <span>const</span> <span>submitMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>let</span> <span>data</span> <span>=</span> <span>{</span> <span>name</span><span>:</span> <span>menu</span><span>.</span><span>name</span><span>,</span> <span>description</span><span>:</span> <span>menu</span><span>.</span><span>description</span><span>,</span> <span>price</span><span>:</span> <span>menu</span><span>.</span><span>price</span><span>,</span> <span>};</span> <span>axios</span> <span>.</span><span>post</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/`</span><span>,</span> <span>data</span><span>,</span> <span>{</span> <span>headers</span><span>:</span> <span>{</span> <span>headers</span><span>,</span> <span>},</span> <span>})</span> <span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span> <span>setMenu</span><span>({</span> <span>id</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>id</span><span>,</span> <span>name</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>name</span><span>,</span> <span>description</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>description</span><span>,</span> <span>price</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>price</span><span>,</span> <span>});</span> <span>setSubmitted</span><span>(</span><span>true</span><span>);</span> <span>console</span><span>.</span><span>log</span><span>(</span><span>response</span><span>.</span><span>data</span><span>);</span> <span>})</span> <span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span> <span>});</span> <span>};</span> <span>const</span> <span>newMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>setMenu</span><span>(</span><span>initialMenuState</span><span>);</span> <span>setSubmitted</span><span>(</span><span>false</span><span>);</span> <span>};</span> <span>return </span><span>(</span> <span>// ...</span> <span>);</span> <span>};</span>import axios from "axios"; import React, { useState } from "react"; import { baseURL, headers } from "./../services/menu.service"; export const AddMenu = () => { const initialMenuState = { id: null, name: "", description: "", price: 0, }; const [menu, setMenu] = useState(initialMenuState); const [submitted, setSubmitted] = useState(false); const handleMenuChange = (e) => { const { name, value } = e.target; setMenu({ ...menu, [name]: value }); }; const submitMenu = () => { let data = { name: menu.name, description: menu.description, price: menu.price, }; axios .post(`${baseURL}/menu/`, data, { headers: { headers, }, }) .then((response) => { setMenu({ id: response.data.id, name: response.data.name, description: response.data.description, price: response.data.price, }); setSubmitted(true); console.log(response.data); }) .catch((e) => { console.error(e); }); }; const newMenu = () => { setMenu(initialMenuState); setSubmitted(false); }; return ( // ... ); };
Enter fullscreen mode Exit fullscreen mode
For this script, we’ll have two states :
-
menu
, which will contain by default the value ofinitialMenuState
object -
submitted
will contain a Boolean object to show a message when a menu is added.
And three methods :
-
handleInputChange()
to track the input value and set the state for change. -
saveMenu()
to send aPOST
request to the API. -
newMenu()
allows the user to add a new menu again once the success message has been shown.
<span><div</span> <span>className=</span><span>"submit-form"</span><span>></span>{submitted ? (<span><div></span><span><div</span><span>className=</span><span>"alert alert-success alert-dismissible fade show"</span><span>role=</span><span>"alert"</span><span>></span>Menu Added!<span><button</span><span>type=</span><span>"button"</span><span>className=</span><span>"close"</span><span>data-dismiss=</span><span>"alert"</span><span>aria-label=</span><span>"Close"</span><span>></span><span><span</span> <span>aria-hidden=</span><span>"true"</span><span>></span><span>×</span><span></span></span><span></button></span><span></div></span><span><button</span> <span>className=</span><span>"btn btn-success"</span> <span>onClick=</span><span>{newMenu}</span><span>></span>Add<span></button></span><span></div></span>) : (<span><div></span><span><div</span> <span>className=</span><span>"form-group"</span><span>></span><span><label</span> <span>htmlFor=</span><span>"name"</span><span>></span>Name<span></label></span><span><input</span><span>type=</span><span>"text"</span><span>className=</span><span>"form-control"</span><span>id=</span><span>"name"</span><span>required</span><span>value=</span><span>{menu.name}</span><span>onChange=</span><span>{handleMenuChange}</span><span>name=</span><span>"name"</span><span>/></span><span></div></span><span><div</span> <span>className=</span><span>"form-group"</span><span>></span><span><label</span> <span>htmlFor=</span><span>"description"</span><span>></span>Description<span></label></span><span><input</span><span>type=</span><span>"text"</span><span>className=</span><span>"form-control"</span><span>id=</span><span>"description"</span><span>required</span><span>value=</span><span>{menu.description}</span><span>onChange=</span><span>{handleMenuChange}</span><span>name=</span><span>"description"</span><span>/></span><span></div></span><span><div</span> <span>className=</span><span>"form-group"</span><span>></span><span><label</span> <span>htmlFor=</span><span>"price"</span><span>></span>Price<span></label></span><span><input</span><span>type=</span><span>"number"</span><span>className=</span><span>"form-control"</span><span>id=</span><span>"price"</span><span>required</span><span>value=</span><span>{menu.price}</span><span>onChange=</span><span>{handleMenuChange}</span><span>name=</span><span>"price"</span><span>/></span><span></div></span><span><button</span><span>type=</span><span>"submit"</span><span>onClick=</span><span>{submitMenu}</span><span>className=</span><span>"btn btn-success mt-2"</span><span>></span>Submit<span></button></span><span></div></span>)}<span></div></span><span><div</span> <span>className=</span><span>"submit-form"</span><span>></span> {submitted ? ( <span><div></span> <span><div</span> <span>className=</span><span>"alert alert-success alert-dismissible fade show"</span> <span>role=</span><span>"alert"</span> <span>></span> Menu Added! <span><button</span> <span>type=</span><span>"button"</span> <span>className=</span><span>"close"</span> <span>data-dismiss=</span><span>"alert"</span> <span>aria-label=</span><span>"Close"</span> <span>></span> <span><span</span> <span>aria-hidden=</span><span>"true"</span><span>></span><span>×</span><span></span></span> <span></button></span> <span></div></span> <span><button</span> <span>className=</span><span>"btn btn-success"</span> <span>onClick=</span><span>{newMenu}</span><span>></span> Add <span></button></span> <span></div></span> ) : ( <span><div></span> <span><div</span> <span>className=</span><span>"form-group"</span><span>></span> <span><label</span> <span>htmlFor=</span><span>"name"</span><span>></span>Name<span></label></span> <span><input</span> <span>type=</span><span>"text"</span> <span>className=</span><span>"form-control"</span> <span>id=</span><span>"name"</span> <span>required</span> <span>value=</span><span>{menu.name}</span> <span>onChange=</span><span>{handleMenuChange}</span> <span>name=</span><span>"name"</span> <span>/></span> <span></div></span> <span><div</span> <span>className=</span><span>"form-group"</span><span>></span> <span><label</span> <span>htmlFor=</span><span>"description"</span><span>></span>Description<span></label></span> <span><input</span> <span>type=</span><span>"text"</span> <span>className=</span><span>"form-control"</span> <span>id=</span><span>"description"</span> <span>required</span> <span>value=</span><span>{menu.description}</span> <span>onChange=</span><span>{handleMenuChange}</span> <span>name=</span><span>"description"</span> <span>/></span> <span></div></span> <span><div</span> <span>className=</span><span>"form-group"</span><span>></span> <span><label</span> <span>htmlFor=</span><span>"price"</span><span>></span>Price<span></label></span> <span><input</span> <span>type=</span><span>"number"</span> <span>className=</span><span>"form-control"</span> <span>id=</span><span>"price"</span> <span>required</span> <span>value=</span><span>{menu.price}</span> <span>onChange=</span><span>{handleMenuChange}</span> <span>name=</span><span>"price"</span> <span>/></span> <span></div></span> <span><button</span> <span>type=</span><span>"submit"</span> <span>onClick=</span><span>{submitMenu}</span> <span>className=</span><span>"btn btn-success mt-2"</span> <span>></span> Submit <span></button></span> <span></div></span> )} <span></div></span><div className="submit-form"> {submitted ? ( <div> <div className="alert alert-success alert-dismissible fade show" role="alert" > Menu Added! <button type="button" className="close" data-dismiss="alert" aria-label="Close" > <span aria-hidden="true">×</span> </button> </div> <button className="btn btn-success" onClick={newMenu}> Add </button> </div> ) : ( <div> <div className="form-group"> <label htmlFor="name">Name</label> <input type="text" className="form-control" id="name" required value={menu.name} onChange={handleMenuChange} name="name" /> </div> <div className="form-group"> <label htmlFor="description">Description</label> <input type="text" className="form-control" id="description" required value={menu.description} onChange={handleMenuChange} name="description" /> </div> <div className="form-group"> <label htmlFor="price">Price</label> <input type="number" className="form-control" id="price" required value={menu.price} onChange={handleMenuChange} name="price" /> </div> <button type="submit" onClick={submitMenu} className="btn btn-success mt-2" > Submit </button> </div> )} </div>
Enter fullscreen mode Exit fullscreen mode
Update a Menu
The component will be a little bit identical to AddMenu
component. But, it will contain a get method to retrieve the object’s current value by making a GET
request to the API with the id
of the object.
We use the useHistory()
hook to pass the id
to the UpdateMenu
component and retrieve it with useParams
hook.
<span>import</span> <span>axios</span> <span>from</span> <span>"</span><span>axios</span><span>"</span><span>;</span><span>import</span> <span>React</span><span>,</span> <span>{</span> <span>useState</span><span>,</span> <span>useEffect</span> <span>}</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>useParams</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span><span>import</span> <span>{</span> <span>baseURL</span><span>,</span> <span>headers</span> <span>}</span> <span>from</span> <span>"</span><span>./../services/menu.service</span><span>"</span><span>;</span><span>export</span> <span>const</span> <span>UpdateMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>const</span> <span>initialMenuState</span> <span>=</span> <span>{</span><span>id</span><span>:</span> <span>null</span><span>,</span><span>name</span><span>:</span> <span>""</span><span>,</span><span>description</span><span>:</span> <span>""</span><span>,</span><span>price</span><span>:</span> <span>0</span><span>,</span><span>};</span><span>const</span> <span>{</span> <span>id</span> <span>}</span> <span>=</span> <span>useParams</span><span>();</span><span>const</span> <span>[</span><span>currentMenu</span><span>,</span> <span>setCurrentMenu</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>initialMenuState</span><span>);</span><span>const</span> <span>[</span><span>submitted</span><span>,</span> <span>setSubmitted</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>false</span><span>);</span><span>useEffect</span><span>(()</span> <span>=></span> <span>{</span><span>retrieveMenu</span><span>();</span><span>},</span> <span>[]);</span><span>const</span> <span>handleMenuChange</span> <span>=</span> <span>(</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>const</span> <span>{</span> <span>name</span><span>,</span> <span>value</span> <span>}</span> <span>=</span> <span>e</span><span>.</span><span>target</span><span>;</span><span>setCurrentMenu</span><span>({</span> <span>...</span><span>currentMenu</span><span>,</span> <span>[</span><span>name</span><span>]:</span> <span>value</span> <span>});</span><span>};</span><span>const</span> <span>retrieveMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>axios</span><span>.</span><span>get</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/</span><span>${</span><span>id</span><span>}</span><span>/`</span><span>,</span> <span>{</span><span>headers</span><span>:</span> <span>{</span><span>headers</span><span>,</span><span>},</span><span>})</span><span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span><span>setCurrentMenu</span><span>({</span><span>id</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>id</span><span>,</span><span>name</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>name</span><span>,</span><span>description</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>description</span><span>,</span><span>price</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>price</span><span>,</span><span>});</span><span>console</span><span>.</span><span>log</span><span>(</span><span>currentMenu</span><span>);</span><span>})</span><span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span><span>});</span><span>};</span><span>const</span> <span>updateMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>let</span> <span>data</span> <span>=</span> <span>{</span><span>name</span><span>:</span> <span>currentMenu</span><span>.</span><span>name</span><span>,</span><span>description</span><span>:</span> <span>currentMenu</span><span>.</span><span>description</span><span>,</span><span>price</span><span>:</span> <span>currentMenu</span><span>.</span><span>price</span><span>,</span><span>};</span><span>axios</span><span>.</span><span>put</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/</span><span>${</span><span>id</span><span>}</span><span>/`</span><span>,</span> <span>data</span><span>,</span> <span>{</span><span>headers</span><span>:</span> <span>{</span><span>headers</span><span>,</span><span>},</span><span>})</span><span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span><span>setCurrentMenu</span><span>({</span><span>id</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>id</span><span>,</span><span>name</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>name</span><span>,</span><span>description</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>description</span><span>,</span><span>price</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>price</span><span>,</span><span>});</span><span>setSubmitted</span><span>(</span><span>true</span><span>);</span><span>console</span><span>.</span><span>log</span><span>(</span><span>response</span><span>.</span><span>data</span><span>);</span><span>})</span><span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span><span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span><span>});</span><span>};</span><span>const</span> <span>newMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span><span>setCurrentMenu</span><span>(</span><span>initialMenuState</span><span>);</span><span>setSubmitted</span><span>(</span><span>false</span><span>);</span><span>};</span><span>return </span><span>(</span><span>// ...</span><span>);</span><span>};</span><span>import</span> <span>axios</span> <span>from</span> <span>"</span><span>axios</span><span>"</span><span>;</span> <span>import</span> <span>React</span><span>,</span> <span>{</span> <span>useState</span><span>,</span> <span>useEffect</span> <span>}</span> <span>from</span> <span>"</span><span>react</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>useParams</span> <span>}</span> <span>from</span> <span>"</span><span>react-router-dom</span><span>"</span><span>;</span> <span>import</span> <span>{</span> <span>baseURL</span><span>,</span> <span>headers</span> <span>}</span> <span>from</span> <span>"</span><span>./../services/menu.service</span><span>"</span><span>;</span> <span>export</span> <span>const</span> <span>UpdateMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>const</span> <span>initialMenuState</span> <span>=</span> <span>{</span> <span>id</span><span>:</span> <span>null</span><span>,</span> <span>name</span><span>:</span> <span>""</span><span>,</span> <span>description</span><span>:</span> <span>""</span><span>,</span> <span>price</span><span>:</span> <span>0</span><span>,</span> <span>};</span> <span>const</span> <span>{</span> <span>id</span> <span>}</span> <span>=</span> <span>useParams</span><span>();</span> <span>const</span> <span>[</span><span>currentMenu</span><span>,</span> <span>setCurrentMenu</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>initialMenuState</span><span>);</span> <span>const</span> <span>[</span><span>submitted</span><span>,</span> <span>setSubmitted</span><span>]</span> <span>=</span> <span>useState</span><span>(</span><span>false</span><span>);</span> <span>useEffect</span><span>(()</span> <span>=></span> <span>{</span> <span>retrieveMenu</span><span>();</span> <span>},</span> <span>[]);</span> <span>const</span> <span>handleMenuChange</span> <span>=</span> <span>(</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>const</span> <span>{</span> <span>name</span><span>,</span> <span>value</span> <span>}</span> <span>=</span> <span>e</span><span>.</span><span>target</span><span>;</span> <span>setCurrentMenu</span><span>({</span> <span>...</span><span>currentMenu</span><span>,</span> <span>[</span><span>name</span><span>]:</span> <span>value</span> <span>});</span> <span>};</span> <span>const</span> <span>retrieveMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>axios</span> <span>.</span><span>get</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/</span><span>${</span><span>id</span><span>}</span><span>/`</span><span>,</span> <span>{</span> <span>headers</span><span>:</span> <span>{</span> <span>headers</span><span>,</span> <span>},</span> <span>})</span> <span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span> <span>setCurrentMenu</span><span>({</span> <span>id</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>id</span><span>,</span> <span>name</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>name</span><span>,</span> <span>description</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>description</span><span>,</span> <span>price</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>price</span><span>,</span> <span>});</span> <span>console</span><span>.</span><span>log</span><span>(</span><span>currentMenu</span><span>);</span> <span>})</span> <span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span> <span>});</span> <span>};</span> <span>const</span> <span>updateMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>let</span> <span>data</span> <span>=</span> <span>{</span> <span>name</span><span>:</span> <span>currentMenu</span><span>.</span><span>name</span><span>,</span> <span>description</span><span>:</span> <span>currentMenu</span><span>.</span><span>description</span><span>,</span> <span>price</span><span>:</span> <span>currentMenu</span><span>.</span><span>price</span><span>,</span> <span>};</span> <span>axios</span> <span>.</span><span>put</span><span>(</span><span>`</span><span>${</span><span>baseURL</span><span>}</span><span>/menu/</span><span>${</span><span>id</span><span>}</span><span>/`</span><span>,</span> <span>data</span><span>,</span> <span>{</span> <span>headers</span><span>:</span> <span>{</span> <span>headers</span><span>,</span> <span>},</span> <span>})</span> <span>.</span><span>then</span><span>((</span><span>response</span><span>)</span> <span>=></span> <span>{</span> <span>setCurrentMenu</span><span>({</span> <span>id</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>id</span><span>,</span> <span>name</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>name</span><span>,</span> <span>description</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>description</span><span>,</span> <span>price</span><span>:</span> <span>response</span><span>.</span><span>data</span><span>.</span><span>price</span><span>,</span> <span>});</span> <span>setSubmitted</span><span>(</span><span>true</span><span>);</span> <span>console</span><span>.</span><span>log</span><span>(</span><span>response</span><span>.</span><span>data</span><span>);</span> <span>})</span> <span>.</span><span>catch</span><span>((</span><span>e</span><span>)</span> <span>=></span> <span>{</span> <span>console</span><span>.</span><span>error</span><span>(</span><span>e</span><span>);</span> <span>});</span> <span>};</span> <span>const</span> <span>newMenu</span> <span>=</span> <span>()</span> <span>=></span> <span>{</span> <span>setCurrentMenu</span><span>(</span><span>initialMenuState</span><span>);</span> <span>setSubmitted</span><span>(</span><span>false</span><span>);</span> <span>};</span> <span>return </span><span>(</span> <span>// ...</span> <span>);</span> <span>};</span>import axios from "axios"; import React, { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; import { baseURL, headers } from "./../services/menu.service"; export const UpdateMenu = () => { const initialMenuState = { id: null, name: "", description: "", price: 0, }; const { id } = useParams(); const [currentMenu, setCurrentMenu] = useState(initialMenuState); const [submitted, setSubmitted] = useState(false); useEffect(() => { retrieveMenu(); }, []); const handleMenuChange = (e) => { const { name, value } = e.target; setCurrentMenu({ ...currentMenu, [name]: value }); }; const retrieveMenu = () => { axios .get(`${baseURL}/menu/${id}/`, { headers: { headers, }, }) .then((response) => { setCurrentMenu({ id: response.data.id, name: response.data.name, description: response.data.description, price: response.data.price, }); console.log(currentMenu); }) .catch((e) => { console.error(e); }); }; const updateMenu = () => { let data = { name: currentMenu.name, description: currentMenu.description, price: currentMenu.price, }; axios .put(`${baseURL}/menu/${id}/`, data, { headers: { headers, }, }) .then((response) => { setCurrentMenu({ id: response.data.id, name: response.data.name, description: response.data.description, price: response.data.price, }); setSubmitted(true); console.log(response.data); }) .catch((e) => { console.error(e); }); }; const newMenu = () => { setCurrentMenu(initialMenuState); setSubmitted(false); }; return ( // ... ); };
Enter fullscreen mode Exit fullscreen mode
And this is the code inside the return
:
<span><div</span> <span>className=</span><span>"submit-form"</span><span>></span>{submitted ? (<span><div></span><span><div</span><span>className=</span><span>"alert alert-success alert-dismissible fade show"</span><span>role=</span><span>"alert"</span><span>></span>Menu Updated!<span><button</span><span>type=</span><span>"button"</span><span>className=</span><span>"close"</span><span>data-dismiss=</span><span>"alert"</span><span>aria-label=</span><span>"Close"</span><span>></span><span><span</span> <span>aria-hidden=</span><span>"true"</span><span>></span><span>×</span><span></span></span><span></button></span><span></div></span><span><button</span> <span>className=</span><span>"btn btn-success"</span> <span>onClick=</span><span>{newMenu}</span><span>></span>Update<span></button></span><span></div></span>) : (<span><div></span><span><div</span> <span>className=</span><span>"form-group"</span><span>></span><span><label</span> <span>htmlFor=</span><span>"name"</span><span>></span>Name<span></label></span><span><input</span><span>type=</span><span>"text"</span><span>className=</span><span>"form-control"</span><span>id=</span><span>"name"</span><span>required</span><span>value=</span><span>{currentMenu.name}</span><span>onChange=</span><span>{handleMenuChange}</span><span>name=</span><span>"name"</span><span>/></span><span></div></span><span><div</span> <span>className=</span><span>"form-group"</span><span>></span><span><label</span> <span>htmlFor=</span><span>"description"</span><span>></span>Description<span></label></span><span><input</span><span>type=</span><span>"text"</span><span>className=</span><span>"form-control"</span><span>id=</span><span>"description"</span><span>required</span><span>value=</span><span>{currentMenu.description}</span><span>onChange=</span><span>{handleMenuChange}</span><span>name=</span><span>"description"</span><span>default</span><span>/></span><span></div></span><span><div</span> <span>className=</span><span>"form-group"</span><span>></span><span><label</span> <span>htmlFor=</span><span>"price"</span><span>></span>Price<span></label></span><span><input</span><span>type=</span><span>"number"</span><span>className=</span><span>"form-control"</span><span>id=</span><span>"price"</span><span>required</span><span>value=</span><span>{currentMenu.price}</span><span>onChange=</span><span>{handleMenuChange}</span><span>name=</span><span>"price"</span><span>/></span><span></div></span><span><button</span> <span>onClick=</span><span>{updateMenu}</span> <span>className=</span><span>"btn btn-success"</span><span>></span>Submit<span></button></span><span></div></span>)}<span></div></span><span><div</span> <span>className=</span><span>"submit-form"</span><span>></span> {submitted ? ( <span><div></span> <span><div</span> <span>className=</span><span>"alert alert-success alert-dismissible fade show"</span> <span>role=</span><span>"alert"</span> <span>></span> Menu Updated! <span><button</span> <span>type=</span><span>"button"</span> <span>className=</span><span>"close"</span> <span>data-dismiss=</span><span>"alert"</span> <span>aria-label=</span><span>"Close"</span> <span>></span> <span><span</span> <span>aria-hidden=</span><span>"true"</span><span>></span><span>×</span><span></span></span> <span></button></span> <span></div></span> <span><button</span> <span>className=</span><span>"btn btn-success"</span> <span>onClick=</span><span>{newMenu}</span><span>></span> Update <span></button></span> <span></div></span> ) : ( <span><div></span> <span><div</span> <span>className=</span><span>"form-group"</span><span>></span> <span><label</span> <span>htmlFor=</span><span>"name"</span><span>></span>Name<span></label></span> <span><input</span> <span>type=</span><span>"text"</span> <span>className=</span><span>"form-control"</span> <span>id=</span><span>"name"</span> <span>required</span> <span>value=</span><span>{currentMenu.name}</span> <span>onChange=</span><span>{handleMenuChange}</span> <span>name=</span><span>"name"</span> <span>/></span> <span></div></span> <span><div</span> <span>className=</span><span>"form-group"</span><span>></span> <span><label</span> <span>htmlFor=</span><span>"description"</span><span>></span>Description<span></label></span> <span><input</span> <span>type=</span><span>"text"</span> <span>className=</span><span>"form-control"</span> <span>id=</span><span>"description"</span> <span>required</span> <span>value=</span><span>{currentMenu.description}</span> <span>onChange=</span><span>{handleMenuChange}</span> <span>name=</span><span>"description"</span> <span>default</span> <span>/></span> <span></div></span> <span><div</span> <span>className=</span><span>"form-group"</span><span>></span> <span><label</span> <span>htmlFor=</span><span>"price"</span><span>></span>Price<span></label></span> <span><input</span> <span>type=</span><span>"number"</span> <span>className=</span><span>"form-control"</span> <span>id=</span><span>"price"</span> <span>required</span> <span>value=</span><span>{currentMenu.price}</span> <span>onChange=</span><span>{handleMenuChange}</span> <span>name=</span><span>"price"</span> <span>/></span> <span></div></span> <span><button</span> <span>onClick=</span><span>{updateMenu}</span> <span>className=</span><span>"btn btn-success"</span><span>></span> Submit <span></button></span> <span></div></span> )} <span></div></span><div className="submit-form"> {submitted ? ( <div> <div className="alert alert-success alert-dismissible fade show" role="alert" > Menu Updated! <button type="button" className="close" data-dismiss="alert" aria-label="Close" > <span aria-hidden="true">×</span> </button> </div> <button className="btn btn-success" onClick={newMenu}> Update </button> </div> ) : ( <div> <div className="form-group"> <label htmlFor="name">Name</label> <input type="text" className="form-control" id="name" required value={currentMenu.name} onChange={handleMenuChange} name="name" /> </div> <div className="form-group"> <label htmlFor="description">Description</label> <input type="text" className="form-control" id="description" required value={currentMenu.description} onChange={handleMenuChange} name="description" default /> </div> <div className="form-group"> <label htmlFor="price">Price</label> <input type="number" className="form-control" id="price" required value={currentMenu.price} onChange={handleMenuChange} name="price" /> </div> <button onClick={updateMenu} className="btn btn-success"> Submit </button> </div> )} </div>
Enter fullscreen mode Exit fullscreen mode
And we are set now.
If you click on Update
button on a menu card, you’ll be redirected to a new page, with this component, with the default values in the fields.
Docker build (Optional)
Docker + Docker Compose (Optional)
Docker is an open platform for developing, shipping, and running applications inside containers.
Why use Docker?
It helps you separate your applications from your infrastructure and helps in delivering code faster.
If it’s your first time working with Docker, I highly recommend you go through a quick tutorial and read some documentation about it.
Here are some great resources that helped me:
Docker configuration for the API
The Dockerfile
represents a text document containing all the commands that could call on the command line to create an image.
Add a Dockerfile at the root of the Django project:
<span># pull official base image</span><span>FROM</span><span> python:3.10-alpine</span><span># set work directory</span><span>WORKDIR</span><span> /app</span><span># set environment variables</span><span>ENV</span><span> PYTHONDONTWRITEBYTECODE 1</span><span>ENV</span><span> PYTHONUNBUFFERED 1</span><span># install psycopg2 dependencies</span><span>RUN </span>apk update <span>\ </span> <span>&&</span> apk add gcc python3-dev<span># install python dependencies</span><span>COPY</span><span> requirements.txt /app/requirements.txt</span><span>RUN </span>pip <span>install</span> <span>--upgrade</span> pip<span>RUN </span>pip <span>install</span> <span>--no-cache-dir</span> <span>-r</span> requirements.txt<span># copy project</span><span>COPY</span><span> . .</span><span># pull official base image</span> <span>FROM</span><span> python:3.10-alpine</span> <span># set work directory</span> <span>WORKDIR</span><span> /app</span> <span># set environment variables</span> <span>ENV</span><span> PYTHONDONTWRITEBYTECODE 1</span> <span>ENV</span><span> PYTHONUNBUFFERED 1</span> <span># install psycopg2 dependencies</span> <span>RUN </span>apk update <span>\ </span> <span>&&</span> apk add gcc python3-dev <span># install python dependencies</span> <span>COPY</span><span> requirements.txt /app/requirements.txt</span> <span>RUN </span>pip <span>install</span> <span>--upgrade</span> pip <span>RUN </span>pip <span>install</span> <span>--no-cache-dir</span> <span>-r</span> requirements.txt <span># copy project</span> <span>COPY</span><span> . .</span># pull official base image FROM python:3.10-alpine # set work directory WORKDIR /app # set environment variables ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # install psycopg2 dependencies RUN apk update \ && apk add gcc python3-dev # install python dependencies COPY requirements.txt /app/requirements.txt RUN pip install --upgrade pip RUN pip install --no-cache-dir -r requirements.txt # copy project COPY . .
Enter fullscreen mode Exit fullscreen mode
Here, we started with an Alpine-based Docker Image for Python. It’s a lightweight Linux distribution designed for security and resource efficiency.
After that, we set a working directory followed by two environment variables:
1 – PYTHONDONTWRITEBYTECODE
to prevent Python from writing .pyc
files to disc
2 – PYTHONUNBUFFERED
to prevent Python from buffering stdout
and stderr
After that, we perform operations like:
- Setting up environment variables
- Installing the PostgreSQL server package
- Copying their
requirements.txt
file to the app path, upgrading pip, and installing the python package to run our application - And last copying the entire project
Also, let’s add a .dockerignore
file.
<span>env </span>venvDockerfile<span>env </span>venv Dockerfileenv venv Dockerfile
Enter fullscreen mode Exit fullscreen mode
Docker Compose for the API
Docker Compose is a great tool (<3). You can use it to define and run multi-container Docker applications.
What do we need? Well, just a YAML file containing all the configuration of our application’s services.
Then, with the docker-compose
command, we can create and start all those services.
This file will be used for development.
<span>version</span><span>:</span> <span>'</span><span>3.9'</span><span>services</span><span>:</span><span>api</span><span>:</span><span>container_name</span><span>:</span> <span>menu_api</span><span>build</span><span>:</span> <span>.</span><span>restart</span><span>:</span> <span>always</span><span>env_file</span><span>:</span> <span>.env</span><span>ports</span><span>:</span><span>-</span> <span>"</span><span>8000:8000"</span><span>command</span><span>:</span> <span>></span><span>sh -c " python manage.py migrate &&</span><span>gunicorn restaurant.wsgi:application --bind 0.0.0.0:8000"</span><span>volumes</span><span>:</span><span>-</span> <span>.:/app</span><span>version</span><span>:</span> <span>'</span><span>3.9'</span> <span>services</span><span>:</span> <span>api</span><span>:</span> <span>container_name</span><span>:</span> <span>menu_api</span> <span>build</span><span>:</span> <span>.</span> <span>restart</span><span>:</span> <span>always</span> <span>env_file</span><span>:</span> <span>.env</span> <span>ports</span><span>:</span> <span>-</span> <span>"</span><span>8000:8000"</span> <span>command</span><span>:</span> <span>></span> <span>sh -c " python manage.py migrate &&</span> <span>gunicorn restaurant.wsgi:application --bind 0.0.0.0:8000"</span> <span>volumes</span><span>:</span> <span>-</span> <span>.:/app</span>version: '3.9' services: api: container_name: menu_api build: . restart: always env_file: .env ports: - "8000:8000" command: > sh -c " python manage.py migrate && gunicorn restaurant.wsgi:application --bind 0.0.0.0:8000" volumes: - .:/app
Enter fullscreen mode Exit fullscreen mode
Let’s add gunicorn
and some configurations before building our image.
pip install gunicornpip install gunicornpip install gunicorn
Enter fullscreen mode Exit fullscreen mode
And add it as a requirement as well in the requirements.txt
.
Here’s what my requirements.txt
file looks like :
django==4.0.4django-cors-headers==3.12.0djangorestframework==3.13.1gunicorn==20.1.0django==4.0.4 django-cors-headers==3.12.0 djangorestframework==3.13.1 gunicorn==20.1.0django==4.0.4 django-cors-headers==3.12.0 djangorestframework==3.13.1 gunicorn==20.1.0
Enter fullscreen mode Exit fullscreen mode
The setup is completed. Let’s build our containers and test if everything works locally.
docker-compose up -d --builddocker-compose up -d --builddocker-compose up -d --build
Enter fullscreen mode Exit fullscreen mode
Your project will be running on https://localhost:8000/
.
Dockerfile for the React App
Add a Dockerfile at the root of the React project:
<span>FROM</span><span> node:17-alpine</span><span>WORKDIR</span><span> /app</span><span>COPY</span><span> package.json ./</span><span>COPY</span><span> yarn.lock ./</span><span>RUN </span>yarn <span>install</span> <span>--frozen-lockfile</span><span>COPY</span><span> . .</span><span>FROM</span><span> node:17-alpine</span> <span>WORKDIR</span><span> /app</span> <span>COPY</span><span> package.json ./</span> <span>COPY</span><span> yarn.lock ./</span> <span>RUN </span>yarn <span>install</span> <span>--frozen-lockfile</span> <span>COPY</span><span> . .</span>FROM node:17-alpine WORKDIR /app COPY package.json ./ COPY yarn.lock ./ RUN yarn install --frozen-lockfile COPY . .
Enter fullscreen mode Exit fullscreen mode
Here, we started with an Alpine-based Docker Image for JavaScript. It’s a lightweight Linux distribution designed for security and resource efficiency.
Also, let’s add a .dockerignore
file.
node_modulesnpm-debug.logDockerfileyarn-error.lognode_modules npm-debug.log Dockerfile yarn-error.lognode_modules npm-debug.log Dockerfile yarn-error.log
Enter fullscreen mode Exit fullscreen mode
And let’s add the code for the docker-compose.yaml
.
<span>version</span><span>:</span> <span>"</span><span>3.9"</span><span>services</span><span>:</span><span>react-app</span><span>:</span><span>container_name</span><span>:</span> <span>react_app</span><span>restart</span><span>:</span> <span>on-failure</span><span>build</span><span>:</span> <span>.</span><span>volumes</span><span>:</span><span>-</span> <span>./src:/app/src</span><span>ports</span><span>:</span><span>-</span> <span>"</span><span>3000:3000"</span><span>command</span><span>:</span> <span>></span><span>sh -c "yarn start"</span><span>version</span><span>:</span> <span>"</span><span>3.9"</span> <span>services</span><span>:</span> <span>react-app</span><span>:</span> <span>container_name</span><span>:</span> <span>react_app</span> <span>restart</span><span>:</span> <span>on-failure</span> <span>build</span><span>:</span> <span>.</span> <span>volumes</span><span>:</span> <span>-</span> <span>./src:/app/src</span> <span>ports</span><span>:</span> <span>-</span> <span>"</span><span>3000:3000"</span> <span>command</span><span>:</span> <span>></span> <span>sh -c "yarn start"</span>version: "3.9" services: react-app: container_name: react_app restart: on-failure build: . volumes: - ./src:/app/src ports: - "3000:3000" command: > sh -c "yarn start"
Enter fullscreen mode Exit fullscreen mode
The setup is completed. Let’s build our containers and test if everything works locally.
docker-compose up -d --builddocker-compose up -d --builddocker-compose up -d --build
Enter fullscreen mode Exit fullscreen mode
Your project will be running on https://localhost:3000/
. And voilà! We’ve dockerized the API and the React applications.
Conclusion
In this article, We learned to build a CRUD application web with Django and React. And as every article can be made better, your suggestion or questions are welcome in the comment section.
Check the code of all these articles in this repo.
This article has been originally posted on my blog
原文链接:Build a CRUD application using Django, React & Docker – 2022
暂无评论内容