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:
- Django-Ace-Integration GitHub repository — DirectMe
- 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.