Arsalan Shahid

Adding CRUD – DjangoAce Part 2

My dear reader, how are you? السلام عليكم

The spread of civilisation may be likened to a fire; first, a feeble spark, next a flickering flame, then a mighty blaze, ever increasing in speed and power – Nikola Tesla

This post extends the DjangoAce application with creating, retrieve, update and delete (CRUD) functionalities using Django class-based views.


Few useful links to practically follow the project:

  1. Django-Ace-Integration GitHub repository — DirectMe
  2. Clone the application from GitHub and set project to DjangoAce part 1 using
$ git clone https://github.com/ArsalanShahid116/Django-Ace-Integration

$ git fetch origin 013570dc3f677a5a917dd160d100f567eceb90a3

Update Code Model

First of all, we update the code model as shown below

# open coder/models.py and update it with following program

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.urls import reverse

class Code(models.Model):
    title = models.CharField(max_length=250)
    body = models.TextField()
    description = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User,
            on_delete=models.CASCADE,
            related_name='coder_code')

    def get_absolute_url(self):
        return reverse('coder:code_detail', kwargs={"id": self.id})

    class Meta:
        ordering = ('-created',)

    def __str__(self):
        return self.title

(aceenv)Django-Ace-Integration/DjangoAce$ python3 manage.py makemigrations
(aceenv)Django-Ace-Integration/DjangoAce$ python3 manage.py migrate

We define some new fields in our code model, that include,

1) author: This is a foreign key to Django default user models which is imported from auth models.

2) Created: This is a date-time field to capture the time when a user created a program.

3) Description: It is a simple text field to let the user add the code description.

4) If a Get method is performed, the model will return the instances in the order of creation that is defined in metaclass.

5) To be able to get a path for update and detail view for a code instance, we define get_absolute_url method.

Update Code form

We will now update the CodeForm by including the additional fields. We would like to have a hidden author field using a widget to not allow the users to post on behalf of others.

# open coder/forms.py and udate the following

from django import forms
from .models import Code
from django.contrib.auth import get_user_model

class CodeForm(forms.ModelForm):
    author = forms.ModelChoiceField(
            widget=forms.HiddenInput,
            queryset=get_user_model().
            objects.all(),
            disabled=True,
            )

    class Meta:
        model = Code
        fields = ('author', 'title', 'description', 'body')
        widgets = {
                  "body": forms.Textarea(),
                }

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('author')
        super(CodeForm, self).__init__(*args, **kwargs)

Adding CRUD Views

Let us now add views for creating, retrieving, updating and deleting codes as shown below

# open coder/views.py and add the following programs

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .forms import CodeForm
from .models import Code
from django.urls import reverse
from django.views.generic import CreateView, DeleteView, UpdateView, ListView, DetailView
from django.shortcuts import redirect

class code_list(ListView):
    template_name = 'coder/programs/list.html'
    queryset = Code.objects.all() 

class code_detail(DetailView):
    template_name = 'coder/programs/detail.html'

    def get_object(self):
        id_ = self.kwargs.get("id")
        return get_object_or_404(Code, id=id_)

class CodeCreate(CreateView):
    template_name = 'coder/add_code.html'
    form_class = CodeForm
    success_url = 'coder:code_list'

    def get_initial(self):
        initial = super().get_initial()
        initial['author'] = self.request.user.id
        return initial

    def form_valid(self, form):
        self.object = form.save()
        self.object.save()
        code_list_url = reverse('coder:code_list')
        return redirect(
                to=code_list_url)

    def get_form_kwargs(self, *args, **kwargs):
        kwargs = super(CodeCreate, self).get_form_kwargs(*args, **kwargs)
        kwargs['author'] = self.request.user
        return kwargs

class CodeUpdate(UpdateView):
    template_name = 'coder/add_code.html'
    form_class = CodeForm
    success_url = 'coder:code_list'

    def get_initial(self):
        initial = super().get_initial()
        initial['author'] = self.request.user.id
        return initial

    def get_object(self):
        id_ = self.kwargs.get("id")
        return get_object_or_404(Code, id=id_)

    def form_valid(self, form):
        self.object = form.save()
        self.object.save()
        code_list_url = reverse('coder:code_list')
        return redirect(
                to=code_list_url)

    def get_form_kwargs(self, *args, **kwargs):
        kwargs = super(CodeUpdate, self).get_form_kwargs(*args, **kwargs)
        kwargs['author'] = self.request.user
        return kwargs

class CodeDelete(DeleteView):
    template_name = 'coder/delete_code.html'

    def get_initial(self):
        initial = super().get_initial()
        initial['author'] = self.request.user.id
        return initial

    def get_object(self):
        id_ = self.kwargs.get("id")
        return get_object_or_404(Code, id=id_)

    def get_success_url(self):
        return reverse('coder:code_list')

One can see that we used class-based views to allow user to perform CRUD operations that are CreateView, DeleteView, UpdateView, ListView, and DetailView. All of these are imported from generic views as shown above.

Adding Templates

We will now add the templates as shown below

# Update coder/templates/coder/add_code.html and add the following program 

{% extends "base.html" %}

<title>{% block title %}DjangoAce{% endblock %}</title>

{% block content %}

<form id="coder_form" class="form" method="post">
        {% csrf_token %}

        <div>
                Program title: <input type="text" name="title" required id="id_slug"> <br> <br>
        </div>

        <div>
                Program Description: <textarea name="description" cols="200" rows="2" required id="id_description">
        </textarea>
        </div>

        <div id="mydiv">
                <textarea data-editor="twilight" name="body" cols="200" rows="20" required id="id_body">
        </textarea>
        </div>

        <div>
        <input type="Submit" name="Submit" value="Submit"/>
        </div>
</form>

{% endblock %}

# Create coder/templates/coder/delete_code.html and add the following program 

{% extends 'base.html' %}

{% block content %}

<form action='.' method='POST'>
{% csrf_token %}
<h1>Do you want to delete the post "{{ object.title }}"?</h1>
<p><input type='submit' value='Yes' />  <a href='../'>Cancel</a></p>

</form>

{% endblock %}

# Create coder/templates/coder/programs/list.html and add the following program 

{% extends "base.html" %}
{% block title %}List of Programs{% endblock %}

{% block content %}

<h1>Programs</h1>

{% for instance in object_list %}
<h2>
        <a href="{{ instance.get_absolute_url }}">
                {{ instance.title }}
        </a>
</h2>

{{ instance.desription|truncatewords:30|linebreaks }}

{% endfor %}
{% endblock %}

# Create coder/templates/coder/programs/detail.html and add the following program 

{% extends "base.html" %}
{% block title %}{{ code.title }}{% endblock %}

{% block content %}
<h1>{{ code.title }}</h1> <br>

<p> Created by {{ code.author }} on {{ code.created }} </p> <br>

<h4> Dscription </h4> <br>
<p> {{ code.description }} </p> <br>

<h4> Program </h4> <br>
<p>
{{ code.body|linebreaks }}
</p>

<a href="{% url "coder:code_update" code.id %}">
Update Code
</a>

<br>

<a href="{% url "coder:code_delete" code.id %}">
Delete Code
</a>

{% endblock %}

Add the routes

Finally, update the routes as shown below

# open coder/urls.py and add the following program 

from django.urls import path
from .views import (
        code_list,
        code_detail,
        CodeCreate,
        CodeUpdate,
        CodeDelete)
from django.contrib.auth import views as auth_views

app_name = 'coder'

urlpatterns = [
        path('', code_list.as_view(), name='code_list'),
        path('<int:id>/', code_detail.as_view(), name='code_detail'),
        path('create/', CodeCreate.as_view(), name='code_create'),
        path('<int:id>/update/', CodeUpdate.as_view(), name='code_update'),
        path('<int:id>/delete/', CodeDelete.as_view(), name='code_delete'),
        ]

If you run the development server at this point in time you should be able to see the added functionalities at http://127.0.0.1:8000/coder/


I hope you find this tutorial useful. If you find any errors or feel any need for improvement, let me know in your comments below.

Signing off for today. Stay tuned and I will see you next week! Happy learning.

Exit mobile version