Setup

Run the following commands to setup a base project:

Windows:

git clone git@github.com:SamuliNatri/django-one-app-project.git project
cd project
setup.bat

MacOS / Linux:

git clone git@github.com:SamuliNatri/django-one-app-project.git project
cd project
chmod u+x setup.sh
./setup.sh

This sets up a simple one app project: Django - One App Project.

Create form

Create a file called forms.py in the mysite directory and add these lines to it:

from django.forms import ModelForm
from .models import Blog

class BlogForm(ModelForm):
    class Meta:
        model = Blog
        fields = ['title']
  • ModelForm is a helper class that allows us to create forms using existing models.
  • Use the fields attribute to define what fields we should be able to edit with the form.

Edit the main urls.py file and add these lines to it:

from django.contrib import admin
from django.urls import path

# HERE
from mysite.views import home, blog_create

urlpatterns = [
    path('', home, name='home'),
    # HERE
    path('blog/create/', blog_create, name='blog_create'),
    path('admin/', admin.site.urls),
]

Edit the views.py file and add a new view function called blog_create to it:

from django.shortcuts import render, redirect
from .forms import BlogForm
from mysite.models import Blog

def blog_create(request):
    if request.method == 'POST':
        form = BlogForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('blog_create')
    else:
        form = BlogForm()
    return render(request,
                  'mysite/blog_create.html',
                  {
                      'form': form
                  })
  • The if request.method == 'POST': line checks if a form has been posted using the POST method.
  • The form object is created from the request data using the BlogForm class we created earlier.
  • If the form validation succeeds, we save the object to the database using form.save().
  • The else clause is executed when we visit the blog/create/ URL without posting the form. We need to create the form object even if we are not posting any data so that the HTML form can be rendered on the page.
  • The form object is passed to the template in the render() function context. The context is a dictionary that maps template variable names to Python objects.

Create a file called blog_create.html in the mysite/templates/mysite directory and add these lines to it:

<h1>Add new blog</h1>
<form action="{% url 'blog_create' %}"
      method="post">
      {% csrf_token %}
      {{ form.as_p }}
<button class="button" type="submit">Create</button>
</form>
  • The csrf_token provides protection against Cross Site Request Forgeries.
  • {{ form.as_p }} renders the form fields using <p> tags.

Visit /blog/create/ to create items:

Edit form

Edit the urls.py file and add the following path to it:

# HERE
from mysite.views import home, blog_create, blog_edit

urlpatterns = [
    path('', home, name="home"),
    path('blog/create/', blog_create, name='blog_create'),
    # HERE
    path('blog/edit/<int:pk>/', blog_edit, name='blog_edit'),
    path('admin/', admin.site.urls),
]

Edit the views.py file and add a new view function called blog_edit to it:

from mysite.models import Blog
from django.shortcuts import render, redirect, get_object_or_404

def blog_edit(request, pk=None):
    blog = get_object_or_404(Blog, pk=pk)
    if request.method == "POST":
        form = BlogForm(request.POST,
                        instance=blog)
        if form.is_valid():
            form.save()
            return redirect('blog_create')
    else:
        form = BlogForm(instance=blog)

    return render(request,
                  'mysite/blog_edit.html',
                  {
                      'form': form,
                      'blog': blog
                  })
  • The get_objects_or_404() function is used to find the blog we want to edit using its primary key. It returns 404 Not Found error if the object doesn't exist.
  • We pass in the blog object to the BlogForm class using the instance=blog parameter. This makes the form.save() method update the object. Without it the method would create a new object, as happens with the blog_create view.
  • The instance=blog in the else clause populates the HTML form with the object data when we visit the page without sending a POST request.

Create a file called blog_edit.html in the mysite/templates/mysite directory and add these lines to it:

<h1>Edit blog</h1>
<form action="{% url 'blog_edit' blog.pk %}"
      method="post">
      {% csrf_token %}
      {{ form.as_p }}
<button class="button" type="submit">Update</button>
</form>

Visit /blog/edit/<id>/ to edit an item:

Delete form

Edit the forms.py file and add these lines to it:

from django.forms import ModelForm
from .models import Blog

class BlogForm(ModelForm):
    class Meta:
        model = Blog
        fields = ['title']

# START
class BlogDeleteForm(ModelForm):
    class Meta:
        model = Blog
        fields = []
# END

The BlogDeleteForm class is only used to validate the delete form.

Edit the urls.py file and add the following path to it:

from django.contrib import admin
from django.urls import path

from mysite.views import home, blog_create, blog_edit, blog_delete

urlpatterns = [
    path('', home, name="home"),
    path('blog/create/', blog_create, name='blog_create'),
    path('blog/edit/<int:pk>/', blog_edit, name='blog_edit'),
    # HERE
    path('blog/delete/<int:pk>/', blog_delete, name='blog_delete'),
    path('admin/', admin.site.urls),
]

Edit the views.py file and add a function called blog_delete to it:

from .forms import BlogForm, BlogDeleteForm

def blog_delete(request, pk=None):
    blog = get_object_or_404(Blog, pk=pk)
    if request.method == "POST":
        form = BlogDeleteForm(request.POST,
                              instance=blog)
        if form.is_valid():
            blog.delete()
            return redirect('blog_create')
    else:
        form = BlogDeleteForm(instance=blog)

    return render(request, 'mysite/blog_delete.html',
                  {
                      'form': form,
                      'blog': blog,
                  })
  • The blog_delete view is pretty much the same as the blog_edit view but instead of updating the blog post, we delete it.

Create a file called blog_delete.html in the mysite/templates/mysite directory and add these lines to it:

<h1>Delete blog</h1>
<form action="{% url 'blog_delete' blog.pk %}" method="post">

    {% csrf_token %}
    {{ form }}

    Are you sure you want to delete post:
    <br><br>
    {{ blog.title }}?
    <br><br>

    <button class="button" type="submit">Delete</button>

    <a href="{% url 'blog_create' %}">Cancel</a>

</form>

Visit blog/delete/<id>/ to delete an item: