Grocery Bag using Django (Part-4) - CRUD Operations
Tags: webdev, python, django
Published: 2021-12-01 04:29:15

---
title: Grocery Bag using Django (Part-4) - CRUD Operations
published: true
series: Grocery Bag Application Using Django
date: 2021-11-30 13:17:39 UTC
tags: webdev, Python, Django
canonical_url: http://iread.ga/posts/72/grocery-bag-using-django-part-4-crud-operations
---

# **Introduction**

In the previous post, we had completed User Authentication in our web application. If you haven't read it till now, make sure you read it [here](https://iread.ga/posts/71/grocery-bag-using-django-part-3-user-authentication). In this blog, we are going to add CRUD operation functionality to our web app. It means, now a logged-in or an authenticated user can _view all the items_, _add items_ in his grocery bag, _update a previously added item_ and _delete an item_. Let's roll it.  

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9o4q6l4au76s8z7sybfm.png)
 

# **Creating an Item Model**

The first thing we need to do is to create a model for our items. Can you guess what all fields should be there for an item? Well, they are - _name_, _quantity_, _status_ and _date_. Let's create the **Item** model class in the `bag/models.py` file:

```py
from django.contrib.auth.models import User
from django.db import models

# Create your models here.

STATUS_CHOICES = (
    ('BOUGHT', 'Bought'),
    ('PENDING', 'Pending'),
    ('NOT AVAILABLE', 'Not Available'),
)

class Item(models.Model):
    name = models.CharField(max_length=127)
    quantity = models.CharField(max_length=63)
    status = models.CharField(
        max_length=15, choices=STATUS_CHOICES, default='PENDING')
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    date = models.DateField()
    updated_at = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)

```

In the above script, we have created an `Item` class that extends the `models.Model` class. Inside the class, we have mentioned the fields as discussed above. `name` will be a _CharField_ with a maximum length of up to 127 characters. Similarly, we have set `quantity` to _CharField_ too, and not IntegerField, just for the fact that quantity can be 3 Kgs as well as 3 pcs. Next, we have a `status` field that is also a _CharField_. The `status` can be one of the three- **BOUGHT** , **PENDING** and **NOT AVAILABLE**. To limit the choices, we have set the `choices` attribute to a **STATUS\_CHOICES** dictionary and the default value would be **PENDING**. We have a `user` field in the model which is a _ForeignKey_ to the **User** model provided by Django. This field tells that which user has created this item and will help us later in sorting them out. The next field is `date` which is a _DateField_. The next two fields are `updated_at` and `created_at` which are used to maintain timestamps when the object is created and when it's updated.

After you have created the model, let's run the migrations:

```bash
$ python manage.py makemigrations
$ python manage.py migrate
```

 

# **Add a new item**

The first thing we need to do is to add a new item into our bag. Note that this operation is only available for authenticated users. Let's create a view function to add a new item in `bag/views.py` file:

```py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import Item

@login_required
def add_item(request):
    if request.method == "POST":
        name = request.POST.get("name")
        quantity = request.POST.get("quantity")
        status = request.POST.get("status")
        date = request.POST.get("date")
        if name and quantity and status and date:
            new_item = Item(name=name, quantity=quantity,
                            status=status, date=date, user=request.user)
            new_item.save()
            messages.success(request, 'Item added successfully!')
            return redirect('index')
        messages.error(request, "One or more field(s) is missing!")
        return redirect('add-item')
    return render(request, "add.html")
```

Whenever a GET request is made, we serve the `add.html` file. If a POST request is made, we get the values of the four fields - **name** , **quantity** , **status** and **date**. If any of the fields is empty, we flash an error and redirect to the same page. If every field is present, we create a new object of the Item model class and save it, and then redirect to the index page.

Let us create a route for this view function in our bag/urls.py file:

```py
from django.urls import path

from bag.views import add_item, index

urlpatterns = [
    path('', index, name='index'),
    path('add-item', add_item, name='add-item'),
]

```

Now let us make the required changes in the HTML form in the `add.html` file:

```html
{% extends "base.html" %}{% load static %} 

{% block title %}Add to bag{% endblock title %} 

{% block content %}
<body>
  <div class="container mt-5">
    <h1>Add to Grocery Bag</h1>
    {% include 'partials/alerts.html' %}
    <form method="post" action="{% url 'add-item' %}">
      {% csrf_token %}
      <div class="form-group mt-2">
        <label>Item name</label>
        <input type="text" name="name" class="form-control" placeholder="Item name" />
      </div>
      <div class="form-group mt-2">
        <label>Item quantity</label>
        <input type="text" name="quantity" class="form-control" placeholder="Item quantity" />
      </div>
      <div class="form-group mt-2">
        <label>Item status</label>
        <select class="form-control" name="status">
          <option value="PENDING">PENDING</option>
          <option value="BOUGHT">BOUGHT</option>
          <option value="NOT AVAILABLE">NOT AVAILABLE</option>
        </select>
      </div>
      <div class="form-group mt-2">
        <label>Date</label>
        <input type="date" name="date" class="form-control" placeholder="Date" />
      </div>
      <div class="form-group mt-2">
        <input type="submit" value="Add" class="btn btn-danger" />
      </div>
    </form>
  </div>
</body>
{% endblock content %}

```

The form **method**  and **action** is changed as required, as well as CSRF token is added. Notice that we have added **name** attribute in each form field, as this is required to fetch the value in our backend.   
 

# **Update an item**

Once an item is added, we can further update it too. Suppose an item was PENDING, but now we have bought that item. So, we can update it and set the status to BOUGHT. Let's add this view function in our `bag/views.py` file:

```py
@login_required
def update_item(request, item_id):
    item = Item.objects.get(id=item_id)
    date = item.date.strftime("%d-%m-%Y")
    if request.method == 'POST':
        item.name = request.POST.get("name")
        item.quantity = request.POST.get("quantity")
        item.status = request.POST.get("status")
        item.date = request.POST.get("date")
        item.save()
        messages.success(request, 'Item updated successfully!')
        return redirect('index')
    return render(request, "update.html", {'item': item, 'date': date})
```

This view function is quite similar to the `add_item` function. But there is a change. With the request, we are getting the `item_id` in the request URL. Using that `item_id`, we first fetch the item from the database so that the user can view the previous values in the update form. Whenever a GET request is made, the item and date is passed with the request. If a POST request is made, we get the values from the form and update the appropriate fields and save the item.

A route for this view function will look like this:

```py
from django.urls import path

from bag.views import add_item, index, update_item

urlpatterns = [
    path('', index, name='index'),
    path('add-item', add_item, name='add-item'),
    path('update-item/<int:item_id>', update_item, name='update-item'),
]

```

Notice that we have added `<int:item_id>` so that we get the `item_id` in our view function.

Now let's make the required changes in the `update.html` file:

```html
{% extends "base.html" %}{% load static %} 

{% block title %}Update bag{% endblock title %} 

{% block content %}
<body>
  <div class="container mt-5">
    <h1>Update Grocery Bag</h1>
    <form method="post" action="{% url 'update-item' item.id %}">
      {% csrf_token %}
      <div class="form-group mt-2">
        <label>Item name</label>
        <input
          type="text"
          class="form-control"
          placeholder="Item name"
          name="name"
          value="{{item.name}}"
        />
      </div>
      <div class="form-group mt-2">
        <label>Item quantity</label>
        <input
          type="text"
          class="form-control"
          placeholder="Item quantity"
          name="quantity"
          value="{{item.quantity}}"
        />
      </div>
      <div class="form-group mt-2">
        <label>Item status</label>
        <select class="form-control" name="status" id="status">
          <option value="PENDING">PENDING</option>
          <option value="BOUGHT" selected>BOUGHT</option>
          <option value="NOT AVAILABLE">NOT AVAILABLE</option>
        </select>
      </div>
      <div class="form-group mt-2">
        <label>Date</label>
        <input
          type="date"
          class="form-control"
          placeholder="Date"
          name="date"
          id="date"
        />
      </div>
      <div class="form-group mt-2">
        <input type="submit" value="Update" class="btn btn-danger" />
      </div>
    </form>
  </div>

  <script>
    // Select appropriate option
    var options = document.getElementById('status').options;
    for (let index = 0; index < options.length; index++) {
      if(options[index].value == '{{item.status}}'){
        options[index].selected = true;
      }
    }

    // Select date
    var fullDate = '{{date}}';
    var dateField = document.getElementById('date');
    dateField.value = `${fullDate.substring(6,12)}-${fullDate.substring(3,5)}-${fullDate.substring(0,2)}`

  </script>
</body>
{% endblock content %}

```

The form **method**  and **action** is changed as required, as well as CSRF token is added. The action URL has also `item.id` passed with it. Notice that we have added **name** attribute in each form field, as this is required to fetch the value in our backend. The next thing to be considered is the Javascript code at the below of the above script. The Javascript code is used to select the status and date automatically.  
 

# **Delete an item**

This is the easiest task in the CRUD operation. Let's see the view function:

```py
@login_required
def delete_item(request, item_id):
    item = Item.objects.get(id=item_id)
    item.delete()
    messages.error(request, 'Item deleted successfully!')
    return redirect('index')
```

As in the update view function, we again get the `item_id` in the `delete_item` view function. We fetch that item and delete it and then redirect the user to the index page with a flash.

Let's add this view function in our urls.py:

```py
from django.urls import path

from bag.views import add_item, delete_item, index, update_item

urlpatterns = [
    path('', index, name='index'),
    path('add-item', add_item, name='add-item'),
    path('update-item/<int:item_id>', update_item, name='update-item'),
    path('delete-item/<int:item_id>', delete_item, name='delete-item'),
]

```

 

# **Updating Index Page**

Till now we're just rendering the `index.html` file. But now we need to fetch all the items added by the user and send it to the frontend. So, let's update the index view function:

```py
@login_required
def index(request):
    items = Item.objects.filter(user=request.user).order_by('-id')
    context = {
        'items': items
    }
    return render(request, "index.html", context)
```

We are filtering the items according to the logged-in user, and then ordered it by the id in descending order.

Let's update the index.html file:

```html
{% extends "base.html" %}{% load static %} 

{% block title %}View Bag{% endblock title %} 

{% block content %}
<body>
  <div class="container mt-5">
    <!-- top -->
    <div class="row">
      <div class="col-lg-6">
        <h1>View Grocery List</h1>
        <a href="{% url 'add-item' %}">
          <button type="button" class="btn btn-success">Add Item</button>
        </a>
      </div>
      <div class="col-lg-6 float-right">
        <div class="row">
          <div class="col-lg-6">
            <!-- Date Filtering-->
            <input type="date" class="form-control" />
          </div>
          <div class="col-lg-4">
            <input type="submit" class="btn btn-danger" value="filter" />
          </div>
          <div class="col-lg-2">
            <p class="mt-1"><a href="{% url 'signout' %}">Log Out</a></p>
          </div>
        </div>
      </div>
    </div>
    <br>
    {% include 'partials/alerts.html' %}
    <!-- Grocery Cards -->
    <div class="row mt-4">
      <!-- Loop This -->
      {% for item in items %}
      <div class="col-lg-4 mb-3">
        <div class="card">
          <div class="card-body">
            <h5 class="card-title">{{item.name}}</h5>
            <h6 class="card-subtitle mb-2 text-muted">{{item.quantity}}</h6>
            {% if item.status == 'PENDING' %}
            <p class="text-info">{{item.status}}</p>
            {% elif item.status == 'BOUGHT' %}
            <p class="text-success">{{item.status}}</p>
            {% else %}
            <p class="text-danger">{{item.status}}</p>
            {% endif %}
            <div class="row">
              <div class="col-md-6">
                <a href="{% url 'update-item' item.id %}">
                  <button type="button" class="btn btn-primary">UPDATE</button>
                </a>
              </div>
              <div class="col-md-6">
                <a href="{% url 'delete-item' item.id %}">
                  <button type="button" class="btn btn-danger">DELETE</button>
                </a>
              </div>
            </div>
          </div>
        </div>
      </div>
      {% endfor %}
    </div>
  </div>
</body>
{% endblock content %}

```

In Django templates, we can iterate over the data passed from the backend using a for loop. Thats what we are doing here. We are iterating over the `items` using an iterator `item`. Also we have used **if condition** to add colors to the status. We have two buttons - update and delete with the URLs set to the update and delete route respectively.  
 

# **Admin Panel**

Django comes with a pre-built admin panel. We just need to create a superuser to access the admin panel. Let's create that superuser using the below command:

```bash
$ python manage.py createsuperuser
```

This will ask you username, email(optional) and the password. Just give those details and a superuser will be created. Then you can go to the `http://127.0.0.1:8000/admin` route, and login with your credentials.  
 

# **Demo Video**

You can watch the demo video:

{% vimeo 651550164 %} 
 

# **Conclusion**

In this part, we have completed the most important step, i.e. CRUD operations on the grocery items. In the next part, we'll see how we can use the filter option available on the index page. Stay tuned!

Code till now: [https://github.com/ashutoshkrris/Grocery-Bag/tree/blog4](https://github.com/ashutoshkrris/Grocery-Bag/tree/blog4)