django forms

Django Object Creation Forms: CHAPTER 7

Hi, in the last article we created an authentication system. If you have not gone through previous posts in this series then please do so here

In this post, we will archive three things:

  1. Create a ticket form in clients page to enable them to submit a ticket which is stored in the database.
  2. Load tickets that were created by the logged in user and display them on the client’s page. (As history)
  3. User can click on a ticket and view more details about it (like who is or worked on it)

Let’s get started!

Our client’s page currently looks like this :

Let us make it a bit better.

Replace templates/clients/home.html :

{% extends 'base.html' %}

{% load static %}

{% block content %}

<div class="container">

<div class="row">

<div class="col-md-3"></div>

<div class="col-md-6">

<h5 class="text-center">Create a new Ticket</h5>

</div>

<div class="col-md-3"></div>

</div>

</div>

{% endblock content%}

 

line 1: we are extending base.html which currently has a welcome message and login/logout links

line 2: we are loading static, things like images, CSS etc

line 3: we tell jinja template engine that this is where we want our content to be

line 4 – 12: I put everything inside a container and row. Then have three columns 3,6,3 just to have things at the centre hahaha that’s crafty but it works

Lets got to forms.py and create the form.

ticketing/forms.py:

class TicketCreateForm(forms.ModelForm):

   class Meta:

   model = Ticket

        fields = [
 
        'ticket_type',

        'description',
 
        'office_description',

   ]

What’s happening?

To create a model form we need we define a class and pass ModelForm to it then we specify which model we are working with. In this case its Ticket model then we define the fields we want the user to insert.

You might have noticed we didn’t pass most fields and we need them. We will add them before saving the form in views.

we continue if you look at Ticket model you will realize that it has no relation with User at all. Let us add a relationship there.

Change this line

opened_by = models.ForeignKey(User, on_delete=models.PROT

 

to

opened_by = models.ForeignKey(User, on_delete=models.PROTECT)

Then, as usual, we make migrations:

(venv) symons@symons-macbookair:~/STICKETING$ python manage.py makemigrations ticketing
Migrations for 'ticketing':
  ticketing/migrations/0002_auto_20200414_0736.py
    - Alter field closed_by on ticket
    - Alter field done_by on ticket
    - Alter field opened_by on ticket

and migrate:

(venv) symons@symons-macbookair:~/STICKETING$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, ticketing
Running migrations:
  Applying ticketing.0002_auto_20200414_0736... OK

Sweet! Lets now go to views.py

ticketing/views.py

We currently have this

def clients(request):

    return render(request, 'clients/home.html', {})

 

Let us have some functionalities

First import the form we created

from ticketing.forms import TicketCreateForm

then our home function becomes:

def clients(request):
    if request.method == "POST":
        form = TicketCreateForm(request.POST)
        if form.is_valid():
            TicketAdd = form.save(commit=False)
            TicketAdd.opened_by = request.user
            TicketAdd.save()
            return redirect('client_home')

    form = TicketCreateForm()
    context = {
        'form': form,
    }
    return render(request, 'clients/home.html', context)

 

line 2: check requests post. We have posted and get request, post meaning some data is coming in.

line 3: we create an instance of the form we created in forms.py. Note we are passing request.POST

4: Check if the form has everything as we expected

line 5: if it is correct we don’t save it immediately so we commit = False to allow us to add the user who created it

line 6: we adding logged in user as opened_by

line 7: we save.

Line 8: if saving is successful we redirect to the client page.

Line 9: in-case request is not posted, we just display the form.

is the dictionary we are sending to the templates.

You can see we replaced {} with context.

Next, we go back to the template

clients/home.html

{% extends 'base.html' %}
{% load static %}
{% block content %}
<!-- new -->
{% load crispy_forms_tags %}

<div class="container">
    <div class="row">
        <div class="col-md-3"></div>
        <div class="col-md-6">
            <h5 class="text-center">Create a new Ticket</h5>
            <!-- new -->
            <form method="post" novalidate>
                {% csrf_token %}
                <input type="hidden" name="next" value="{{ next }}">
                {{ form|crispy }}
                <br>
                <button type="submit" class="btn btn-success">Submit</button>
            </form>


        </div>
        <div class="col-md-3"></div>
    </div>
</div>
{% endblock content%}

I just added the form and crispy form.

This is what we get

Go to the admin panel and add a type. then come back and try this form.

If you have forgotten your admin username and password, simply create a new one.

python manage.py createsuperuser

I have added two. And submitted two tickets with the form we just created.

You can see they were added.

Sweet! lets now load only those tickets submitted by the logged-in user.

To do that we are simply going to query Tickets table but with a filter. in views add this line

my_tickets = Ticket.objects.filter(opened_by=request.user)

then in context, we add my_tickets:

'my_tickets': my_tickets,

final view should be.

def clients(request):
    my_tickets = Ticket.objects.filter(opened_by=request.user)
    if request.method == "POST":
        form = TicketCreateForm(request.POST)
        if form.is_valid():
            TicketAdd = form.save(commit=False)
            TicketAdd.opened_by = request.user
            TicketAdd.save()
            return redirect('client_home')

    form = TicketCreateForm()
    context = {
        'form': form,
        'my_tickets': my_tickets,
    }
    return render(request, 'clients/home.html', context)

Now we go to templates and display them:

 <ul class="list-group">
        {% for ticket in my_tickets %}

        <li class="list-group-item d-flex mb-1  list-group-item-dark justify-content-between align-items-center">
            TK{{ticket.id}}
            <span class="badge badge-primary badge-pill">{{ticket.status}}</span>
        </li>

        {% endfor %}
    </ul>

I just create a list group to display them I get this:

Looks like what I want. Now let’s make the more details page such that when a user clicks on the Id he or she is taken to a page where they can see more details about the ticket.

To do this, We will have a URL that passes an id to the responsible view. The view receives it and pulls only the ticket with that id from the database.

ticketing/urls.py

path('ticket/detail<int:id>', views.ticket_details, name='ticket_detail'),

ticketing/views.py

def ticket_details(request, id):
    ticket = get_object_or_404(Ticket, pk=id)
    return render(request, 'ticket_detail.html', {'ticket': ticket})

What’s happening?

Our ticket_details ,method is taking id which we passed in the URL then we get that specific ticket. this query gives one object so we won’t loop on it.

And we update client/home.html to have a link to this page

we will have href like this

href="{% url 'ticket_detail' id=ticket.pk %}"

final home.html is

{% extends 'base.html' %}
{% load static %}
{% block content %}
<!-- new -->
{% load crispy_forms_tags %}

<div class="container">
    <div class="row">
        <div class="col-md-3"></div>
        <div class="col-md-6">
            <h5 class="text-center">Create a new Ticket</h5>
            <!-- new -->
            <form method="post" novalidate>
                {% csrf_token %}
                <input type="hidden" name="next" value="{{ next }}">
                {{ form|crispy }}
                <br>
                <button type="submit" class="btn btn-success">Submit</button>
            </form>


        </div>
        <div class="col-md-3"></div>
    </div>
    <!-- new -->
    <br>
    <ul class="list-group">
        {% for ticket in my_tickets %}

        <li class="list-group-item d-flex mb-1  list-group-item-dark justify-content-between align-items-center">
            <a href="{% url 'ticket_detail' id=ticket.pk %}">TK{{ticket.id}}</a>
            <span class="badge badge-primary badge-pill">{{ticket.status}}</span>
        </li>

        {% endfor %}
    </ul>

    <br><br>
</div>
{% endblock content%}

Nice let us give it a try, click on TK1

oops! error.

We love errors that can be seen

Its actually a familiar one. Let us create the ticket_detail.html page inside the templates folder:

{% extends 'base.html' %}
{% block content %}
<div class="container">
    <ul>
        <li>Ticket Id: {{ticket.id}}</li>
        <li>Opened by: {{ticket.opened_by}}</li>
        <li>Opened On: {{ticket.opened_on}}</li>
        <li>Type: {{ticket.ticket_type}}</li>
        <li>Description: {{ticket.description}}</li>
    </ul>
</div>

{% endblock content %}

And that is how we access it in templates.

Style that page and let me see your design in the comments below. I’ll also put my design in the comment section.

If you are following this please let me know in the comment section it’ll mortivate me to continue writing. thank you

 

Done for now.

Get the code from github

You may join my whatsapp group

or Contact me personally info@omborisymons.com

close

1 thought on “Django Object Creation Forms: CHAPTER 7”

  1. Update. If you logout and signup as a new client you will get an error.

    ”TypeError: ‘AnonymousUser’ object is not iterable”

    To solve this see the next post. Chapter 8

    Reply

Leave a Comment

error

Enjoy this blog? Please spread the word :)