{{announcement.body}}
{{announcement.title}}

How to Build a CRUD Application Using React and Django

DZone 's Guide to

How to Build a CRUD Application Using React and Django

This article demonstrates how to build a simple REST API using the Django framework, and a frontend in React which consumes the API.

· Web Dev Zone ·
Free Resource

In this tutorial, we are going to build a CRUD application for storing books with React and Django. CRUD stands for Create, Read, Update and Delete, so basically this app would work like a library shelf where we can take a look at each book data being able to modify as we want or need.

Technologies Used

For this application, we will be using two very popular solutions for building user interfaces and APIs. The user interface of the application will be built using React – prior knowledge of React is not required for this tutorial.

The back-end for this application will be built using Django, which is a high-level Python web framework that encourages rapid development and clean pragmatic design. Building APIs in Django is made easy using the Django Rest Framework.

We’ll be building the application in three stages:

1. Creating the front-end in React

2. Creating the APIs in Django 

3. Connecting the front-end with the APIs

Getting Started: Configuring the Development Environment

For this tutorial, we will be using the Eclipse IDE with the CodeMix plugin installed. However, feel free to follow along in any IDE that supports both React and Python development.

  • Download Eclipse IDE here
  • CodeMix can be installed from the Eclipse marketplace or via genuitec.com.
  • If the React pack isn’t already installed, install it using the CodeMix Extension Browser(Help > CodeMix Extensions). It can also be installed directly from the Eclipse marketplace.

If you’re looking for an IDE to take on development with frameworks like Angular, React, and Vue.js, or languages like Python and Rust, CodeMix has you covered. Codemix is compatible with all Eclipse-based IDEs and tools, such as MyEclipse, Spring Tools Suite, and JBoss Tools.



Also, we need to have Python installed in our system, so let’s be sure about having it correctly installed. It can be downloaded here.

Creating the Front-End In React

We will create our React application using the CodeMix wizard.

To create the application, navigate to File > New > Project > CodeMix > React Project.

Enter react-book-app as the name of the application in the dialog box that appears, then click finish.

Create React application

Create React application


The folder structure for your new CodeMix-created react application will look like:

Folder structure

Folder structure


Additional Packages

As you can see from the folder structure, there is no node_modules folder. Hence, we need to install the node packages. Using Terminal+, run:

npm install

We will be using bootstrap and fontawesome for some additional styling and for some icons.

Add the following to the <head> section in src/index.js:

HTML
 




x


1
<link rel="stylesheet" href="https://bootswatch.com/4/lux/bootstrap.min.css">
2
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">


Building the Interface

React is a component-driven library. So we need to break our app into different components. Here is the interface we are trying to build:

Interface

Interface


The interface can be broken into different components:

Book Dashboard Component: This is the parent component in the application. It contains the book listing component and the Toggleable Book Form Component.

Book Listing Component: This component is saddled with the responsibility of listing all books that have been created in the application. It contains a list of Editable Book Components.

  • Editable Book component: Renders a Book component and a Book Form component. If the edit icon is clicked, then the Editable Book component will render a Book Form component that allows books that have been created to be edited.
  • Book component: This component shows the details of the book.
  • Book Form component: This component shows a form that allows a book to be created or edited.

Toggleable Book Form Component: This component has the responsibility of creating new books. When the  +  button is clicked, the Book Form component will be rendered which enables us to create a new book.

The application will be able to handle all 4 CRUD (Create Read Update Delete) operations.

Creating Components

We will create the components hierarchically — from the parent component. The first component to be created is the Book Dashboard component.

Modify src/index.js to look like:

HTML
 




xxxxxxxxxx
1
62


 
1
class BookDashboard extends React.Component {
2
    state = {
3
        books: [
4
            {
5
                id: 1,
6
                title: 'A simple book',
7
                author: 'Jude Ben',
8
                description: `Lorem ipsum dolor sit amet, consectetur
9
                    adipiscing elit, sed do eiusmod tempor incididunt
10
                    ut labore et dolore magna aliqua. Ut enim ad minim
11
                    veniam, quis nostrud`
12
            },
13
            {
14
                id: 2,
15
                title: 'A book of secrets',
16
                author: 'James John',
17
                description: `Sed ut perspiciatis unde omnis iste natus
18
                    error sit voluptatem accusantium doloremque laudantium,
19
                    totam rem aperiam, eaque ipsa quae ab illo inventore
20
                    veritatis et quasi architecto beatae vitae dicta sunt
21
                    explicabo.`
22
            }
23
      ]
24
  }
25
 
          
26
  createNewBook = (book) => {
27
      book.id = Math.floor(Math.random() * 1000);
28
      this.setState({books: this.state.books.concat([book])});
29
  }
30
 
          
31
  updateBook = (newBook) => {
32
      const newBooks = this.state.books.map(book => {
33
          if(book.id === newBook.id) {
34
                return Object.assign({}, newBook)
35
          } else {
36
                return book;
37
          }
38
      });
39
 
          
40
      this.setState({books: newBooks});
41
  }
42
 
          
43
  deleteBook = (bookId) => {
44
      this.setState({books: this.state.books.filter(book => book.id !== bookId)})
45
  }
46
  render() {
47
      return (
48
          <main className="d-flex justify-content-center my-4">
49
              <div  className="col-5">
50
                  <BookList
51
                      books={this.state.books}
52
                      onDeleteClick={this.deleteBook}
53
                      onUpdateClick={this.updateBook}
54
                  ></BookList>
55
                  <ToggleableBookForm
56
                      onBookCreate={this.createNewBook}
57
                  ></ToggleableBookForm>
58
              </div>
59
          </main>
60
      )
61
  }
62
}


As seen above, the BookDashboard component stores the books in state. The books are then passed as props to the BookList component along with some functions to handle book deletion and updates.

BookDashboard also renders ToggleableBookForm and passes a function as prop which will handle book creation. React should be immutable, so we go to our handler functions:  createNewBook ,  updateBook,  deleteBook  which update the state without mutating the initial contents of a book.

Next, we need to create the BookList component. For that, add the following code to index.js.

HTML
 




xxxxxxxxxx
1
70


 
1
class BookList extends React.Component {
2
  render() {
3
    const books = this.props.books.map(book => (
4
      <EditableBook
5
        key={book.id}
6
        id={book.id}
7
        title={book.title}
8
        author={book.author}
9
        description={book.description}
10
        onDeleteClick={this.props.onDeleteClick}
11
        onUpdateClick={this.props.onUpdateClick}
12
      ></EditableBook>
13
    ));
14
    return (
15
      <div>
16
        {books}
17
      </div>
18
    );
19
  }
20
}
21
 
          
22
class EditableBook extends React.Component {
23
  state = {
24
    inEditMode: false
25
  };
26
  enterEditMode = () => {
27
    this.setState({inEditMode: true});
28
  }
29
  leaveEditMode = () => {
30
    this.setState({inEditMode: false});
31
  }
32
  handleDelete = () => {
33
    this.props.onDeleteClick(this.props.id);
34
  }
35
  handleUpdate = (book) => {
36
    this.leaveEditMode()
37
    book.id = this.props.id;
38
    this.props.onUpdateClick(book);
39
  }
40
  render() {
41
    const component = () => {
42
      if(this.state.inEditMode) {
43
        return (
44
          <BookForm
45
            id={this.props.id}
46
            title={this.props.title}
47
            author={this.props.author}
48
            description={this.props.description}
49
            onCancelClick={this.leaveEditMode}
50
            onFormSubmit={this.handleUpdate}
51
          />
52
        );
53
      }
54
      return (
55
        <Book
56
          title={this.props.title}
57
          author={this.props.author}
58
          description={this.props.description}
59
          onEditClick={this.enterEditMode}
60
          onDeleteClick={this.handleDelete}
61
        />
62
      )
63
    }
64
    return (
65
      <div className="mb-3 p-4" style={{boxShadow: '0 0 10px #ccc'}} >
66
        {component()}
67
      </div>
68
    )
69
  }
70
}


The  BookList component receives books as prop, maps over it and returns an array of EditableBook components which is then rendered. EditableBook is a component that renders a Book or a BookForm if the user clicks the edit book button on the  Book component. It is a stateful component that stores the edit mode status in state and using that value, knows which component to render.

The book component receives the details about the book as props. It also receives some functions which handle the clicking of the edit button and the delete button.

Let’s create the Book and  BookForm components.

HTML
 




x
90


 
1
class Book extends React.Component {
2
  render() {
3
    return (
4
      <div className="card" /* style="width: 18rem;" *></div>
5
        <div className="card-header d-flex justify-content-between">
6
          <span>
7
            <strong>Title: </strong>{this.props.title}
8
          </span>
9
          <div>
10
            <span onClick={this.props.onEditClick} className="mr-2"><i className="far fa-edit"></i></span>
11
            <span onClick={this.props.onDeleteClick}><i className="fas fa-trash"></i></span>
12
          </div>
13
        </div>
14
        <div className="card-body">
15
          {this.props.description}
16
        </div>
17
        <div className="card-footer">
18
          <strong>Author:</strong>  {this.props.author}
19
        </div>
20
      </div>
21
    );
22
  }
23
}
24
 
          
25
class BookForm extends React.Component {
26
  state = {
27
    title: this.props.title || '',
28
    author: this.props.author || '',
29
    description: this.props.description || ''
30
  }
31
  handleFormSubmit = (evt) => {
32
    evt.preventDefault();
33
    this.props.onFormSubmit({...this.state});
34
  }
35
  handleTitleUpdate = (evt) => {
36
    this.setState({title: evt.target.value});
37
  }
38
  handleAuthorUpdate = (evt) => {
39
    this.setState({author: evt.target.value});
40
  }
41
  handleDescriptionUpdate = (evt) => {
42
    this.setState({description: evt.target.value});
43
  }
44
  render() {
45
    const buttonText = this.props.id ? 'Update Book': 'Create Book';
46
    return (
47
      <form onSubmit={this.handleFormSubmit}>
48
        <div className="form-group">
49
          <label>
50
            Title
51
          </label>
52
          <input type="text" placeholder="Enter a title"
53
            value={this.state.title} onChange={this.handleTitleUpdate}
54
            className="form-control"
55
          />
56
        </div>
57
 
          
58
        <div className="form-group">
59
          <label>
60
            Author
61
          </label>
62
          <input type="text" placeholder="Author's name"
63
            value={this.state.author} onChange={this.handleAuthorUpdate}
64
            className="form-control"
65
          />
66
        </div>
67
 
          
68
        <div className="form-group">
69
          <label>
70
            Description
71
          </label>
72
          <textarea className="form-control" placeholder="Book Description"
73
            rows="5" value={this.state.description}
74
            onChange={this.handleDescriptionUpdate}
75
          >
76
            {this.state.description}
77
          </textarea>
78
        </div>
79
        <div className="form-group d-flex justify-content-between">
80
          <button type="submit" className="btn btn-md btn-primary">
81
            {buttonText}
82
          </button>
83
          <button type="button" className="btn btn-md btn-secondary" onClick={this.props.onCancelClick}>
84
            Cancel
85
          </button>
86
        </div>
87
      </form>
88
    )
89
  }
90
}


The Book component just renders the markup for displaying books. 

All the data it needs and handlers for edit and delete actions are gotten from the parent component — EditableBook .

The BookForm component, on the other hand, renders a form that contains the book details from props for an update operation. 

For a create operation, which will be handled by ToggleableBookForm, no props will be sent the form is made reactive by calling setState when the input field is modified. We do this by creating different handlers for each field and listen for the change event.

The function to handle submit events on this form will be sent as a prop from the parent component —  EditableBook or ToggleableBookForm.

The last component to be built is the ToggleableBookForm, this component will be used for creating books.

JavaScript
 




xxxxxxxxxx
1
36


1
// index.js
2
class ToggleableBookForm extends React.Component {
3
  state = {
4
    inCreateMode: false
5
  }
6
  handleCreateClick = () => {
7
    this.setState({inCreateMode: true});
8
  }
9
  leaveCreateMode = () => {
10
    this.setState({inCreateMode: false});
11
  }
12
  handleCancleClick = () => {
13
    this.leaveCreateMode();
14
  }
15
  handleFormSubmit = (book) => {
16
    this.leaveCreateMode();
17
    this.props.onBookCreate(book);
18
  }
19
  render() {
20
    if (this.state.inCreateMode) {
21
      return (
22
        <div className="mb-3 p-4" style={{boxShadow: '0 0 10px #ccc'}} >
23
          <BookForm
24
            onFormSubmit={this.handleFormSubmit}
25
            onCancelClick={this.handleCancleClick}></BookForm>
26
        </div>
27
 
          
28
      )
29
    }
30
    return (
31
      <button onClick={this.handleCreateClick} className="btn btn-secondary">
32
        <i className="fas fa-plus"></i>
33
      </button>
34
    );
35
  }
36
}


As described earlier, ToggleableBookForm renders a button which the user clicks to create a book. When the button gets clicked, the BookForm component is rendered.

Finally, we render the BookDashboard component using ReactDOM.

JavaScript
 




xxxxxxxxxx
1


 
1
// index.js
2
ReactDOM.render(<BookDashboard />, document.getElementById('root'));


Reloading the browser, the book app should be functioning as expected.

Functioning Book app

Functioning Book app

Our app works!!!

However, the data is not persisted when the page reloads. This is because the data is stored in data structures in our application — state. To persist the data, we need some kind of data storage solution that our React app can communicate with. To do this, we will build a simple REST API with Python using the Django framework.

Building The Books API with Django

We’ll create the books API using Django. Let’s create a new dummy project:

New dummy project

New dummy project

If you don’t have pipenv installed already, you will need to install it by running the following in the terminal:

pip install pipenv

Using the integrated terminal, activate the pipenv shell to create the Django project:

pipenv shell

When done, run the following commands to install Django and Django REST Framework:

Shell
 




xxxxxxxxxx
1


 
1
pipenv install django
2
pipenv install django-rest-framework


Next, we create a Django project and create a Django app.

Shell
 




xxxxxxxxxx
1


 
1
django-admin startproject booksApi
2
cd booksApi/
3
django-admin startapp books


This will create new folders in the  books-api folder. Update INSTALLLED_APPS in settings.py to include books and django-rest-framework.

Python
 




xxxxxxxxxx
1
12


1
# books-api/booksApi/booksApi/settings.py
2
 
          
3
INSTALLED_APPS = [
4
    'django.contrib.admin',
5
    'django.contrib.auth',
6
    'django.contrib.contenttypes',
7
    'django.contrib.sessions',
8
    'django.contrib.messages',
9
    'django.contrib.staticfiles',
10
    'rest_framework',
11
    'books',
12
]


Our database model will just contain the Book Model. Add the following to models.py.

Python
 




xxxxxxxxxx
1
14


 
1
# books-api/booksApi/books/model.py.
2
 
          
3
from django.db import models
4
 
          
5
# Create your models here.
6
 
          
7
class Book(models.Model):
8
    title = models.CharField(max_length=200)
9
    description = models.TextField()
10
    author = models.CharField(max_length=200)
11
    created_at = models.DateTimeField(auto_now_add=True)
12
 
          
13
    def __str__(self):
14
        return '%s by %s' % (self.title, self.author)


Book model

Book model

Our Book model contains 4 fields: title, description, author, and created_at.

  • The title field stores the title of the book.
  • The description stores the description of a book.
  • The author field stores the author of the book.

These fields will be provided from our frontend — the react app we built earlier.

  • The created_at field will be auto inserted based on the time the book is created.

Run the following command to create the necessary migrations and tables.

In Terminal+,  ensure you are at books-api/booksApi/ then run:

Shell
 




xxxxxxxxxx
1


1
python manage.py makemigrations
2
python manage.py migrate


Next, we will create a serializer. Serializers are used to convert our data to JSON which will be returned when we visit the endpoints.

Python
 




xxxxxxxxxx
1


 
1
# books-api/booksApi/books/serializers.py.
2
from rest_framework import serializers
3
from .models import Book
4
 
          
5
 
          
6
class BookSerializer(serializers.ModelSerializer):
7
  class Meta:
8
    model = Book
9
    fields = ('id', 'title', 'author', 'description', 'created_at')


Next, we update views.py to add our API views.

Python
 




xxxxxxxxxx
1
13
          
           
          
 
1
# books-api/booksApi/books/views.py.
2
 
          
3
from .models import Book
4
from .serializers import BookSerializer
5
from rest_framework import generics
6
 
          
7
class BookList(generics.ListCreateAPIView):
8
    queryset = Book.objects.all()
9
    serializer_class = BookSerializer
10
 
          
11
class BookDetail(generics.RetrieveUpdateDestroyAPIView):
12
    queryset = Book.objects.all()
13
    serializer_class = BookSerializer


Finally, we need to add our endpoints. Create a  urls.py in books-api/booksApi/books

Python
 




xxxxxxxxxx
1


 
1
# books-api/booksApi/books/urls.py.
2
from django.urls import path
3
from rest_framework.urlpatterns import format_suffix_patterns
4
from books import views
5
 
          
6
urlpatterns = [
7
    path('books/', views.BookList.as_view()),
8
    path('books/<int:pk>/', views.BookDetail.as_view()),
9
]


And update books-api/booksApi/booksApi/urls.py to look like:

Python
 




xxxxxxxxxx
1
23


 
1
# books-api/booksApi/booksApi/urls.py.
2
"""booksApi URL Configuration
3
 
          
4
The `urlpatterns` list routes URLs to views. For more information please see:
5
    https://docs.djangoproject.com/en/3.0/topics/http/urls/
6
Examples:
7
Function views
8
    1. Add an import:  from my_app import views
9
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
10
Class-based views
11
    1. Add an import:  from other_app.views import Home
12
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
13
Including another URLconf
14
    1. Import the include() function: from django.urls import include, path
15
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
16
"""
17
from django.contrib import admin
18
from django.urls import path
19
 
          
20
urlpatterns = [
21
    path('admin/', admin.site.urls),
22
    path('api/', include('books.urls')),
23
]


Start the Django server by running the snippet below in Terminal+:

Shell
 




xxxxxxxxxx
1


 
1
python manage.py runserver


The endpoint is can now be viewed at http://localhost:8000/api/books/. Before we consume the API in our react app, we need to solve one issue that will probably arise — CORS. Run the following to install a package to help with CORS:

Shell
 




xxxxxxxxxx
1


 
1
pipenv install django-cors-headers


When that is done, add the following to the list of INSTALLED_APPS in settings.py as we did earlier. We will also need to add some new middleware.

Python
 




xxxxxxxxxx
1
26


 
1
# books-api/booksApi/booksApi/settings.py
2
 
          
3
INSTALLED_APPS = [
4
    'django.contrib.admin',
5
    'django.contrib.auth',
6
    'django.contrib.contenttypes',
7
    'django.contrib.sessions',
8
    'django.contrib.messages',
9
    'django.contrib.staticfiles',
10
    'rest_framework',
11
    'books',
12
    'corsheaders',
13
 
          
14
]
15
 
          
16
MIDDLEWARE = [
17
    'django.middleware.security.SecurityMiddleware',
18
    'django.contrib.sessions.middleware.SessionMiddleware',
19
    'django.middleware.common.CommonMiddleware',
20
    'django.middleware.csrf.CsrfViewMiddleware',
21
    'django.contrib.auth.middleware.AuthenticationMiddleware',
22
    'django.contrib.messages.middleware.MessageMiddleware',
23
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
24
    'corsheaders.middleware.CorsMiddleware',
25
    'django.middleware.common.CommonMiddleware',
26
]


And finally, add the line below to allow all origins to access the API:

Python
 




xxxxxxxxxx
1


 
1
# books-api/booksApi/booksApi/settings.py
2
 
          
3
CORS_ORIGIN_ALLOW_ALL = True


Run the server using the integrated terminal:

Shell
 




xxxxxxxxxx
1


 
1
python manage.py runserver


Consuming the API with React

We will be using fetch to consume the books API. Add the following to index.js:

JavaScript
 




xxxxxxxxxx
1
14


1
class BookDashboard extends React.Component {
2
    state = {
3
        books: []
4
    }
5
 
          
6
    componentDidMount() {
7
        fetch('http://localhost:8000/api/books/')
8
            .then(response => response.json())
9
            .then(data => {
10
                this.setState({books: data});
11
            });
12
    }
13
...
14
}


As seen above, we remove the initial content of the books array and we just initialize it with an empty array.

Using  componentDidMount hook, we fetch the content of the book api and on line 10, update the state to use the data recieved. Currently, there is no book in the database, so every call to http://localhost:8000/api/books/ will return an empty array.

Update the  createNewBook function to use the API for book creation:

JavaScript
 




xxxxxxxxxx
1
13


1
createNewBook = (book) => {
2
    fetch('http://localhost:8000/api/books/', {
3
        method: 'POST',
4
        headers: {
5
                'Content-Type': 'application/json',
6
        },
7
        body: JSON.stringify(book),
8
    })
9
    .then(response => response.json())
10
    .then(book => {
11
        this.setState({books: this.state.books.concat([book])});
12
    });
13
}


 createNewBook sends a POST request to the books API with the book data gotten from the form. The API responds with the newly created book containing some additional data like id and created_at. 

Next up, we need to modify books — update. To update a book, we need to send a PUT request.

JavaScript
 




xxxxxxxxxx
1
18


 
1
updateBook = (newBook) => {
2
    fetch(`http://localhost:8000/api/books/${newBook.id}/`, {
3
        method: 'PUT',
4
        headers: {
5
                'Content-Type': 'application/json',
6
        },
7
        body: JSON.stringify(newBook),
8
    }).then(response => response.json())
9
    .then(newBook => {
10
        const newBooks = this.state.books.map(book => {
11
            if(book.id === newBook.id) {
12
                return Object.assign({}, newBook)
13
            } else {
14
                return book;
15
            }
16
        });
17
        this.setState({books: newBooks});
18
    });


As seen above, we are sending a put request to the url/<bookId>. This shows which book we are looking to update. If the update is successful, we update the state.

We are using the same state update mechanism as before. You might want to send another request to fetch all the books from the API but that will take more time.

The last method to modify is the delete action, just like this:

JavaScript
 




xxxxxxxxxx
1
11


 
1
deleteBook = (bookId) => {
2
    fetch(`http://localhost:8000/api/books/${bookId}/`, {
3
        method: 'DELETE',
4
        headers: {
5
            'Content-Type': 'application/json',
6
        },
7
    })
8
    .then(() => {
9
        this.setState({books: this.state.books.filter(book => book.id !== bookId)})
10
    });
11
}


The delete action is probably the simplest. We send a DELETE request to the same URL as used for update, the delete action does not return any data so we can just update the state.

App running on Live Preview

App running on Live Preview

Conclusion

With this functional CRUD application, we have just learned how to build a simple REST API using the Django framework, and a frontend in React which consumes the API. In case you want to take a closer look at the code, take a look at the Github repository for the React codebase, and for the Django API.

If you haven’t tried CodeMix yet, go and experiment with all the features it has to offer! Be part of the community with the CodeMix Insiders program, or use the integrated LiveChat to receive full support from the CodeMix team right inside your IDE.

Have any questions or suggestions about this article or any of our services? Let us know your thoughts via Twitter or through the CodeMix forums. Happy Coding!

Topics:
beginner ,crud ,django ,reactjs ,tutorial ,web dev

Published at DZone with permission of George Anderson , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}