Django Bootstrap modal update form not showing data

I have created 2 modals in separate html files to handle creation and update of the applicant status. Both modals are triggered on the same page, the applicant detail, and are showing the form, and the update form is not displaying the data, even though the clickable button shows the correct link.

applicant_detail.html

<div class="row mb-1">
    <div class="col">
        <h4>Applicant Status</h4>
    </div>
    <div class="col col-auto">
        <a href="#applicantStatusCreate" class="btn btn-primary text-white float-end" data-bs-toggle="modal">
        Add Applicant Status
        </a>
        {% include 'recruitment/applicant_status_create.html' %}
    </div>
</div>
<!---Applicant Status Start--->
{% for status in applicant_status_detail %}
    <div class="card mb-4">
        <div class="card-body p-4">
            <div class="row">
                <div class="col-xl-5">
                    <h5>{{ status.applicant_status }}</h5>
                    <ul class="personal-info">
                        <li>
                            <div class="title">Status Date</div>
                            <div class="text">{{ status.status_date|date:"M d, Y" }}</div>
                        </li>
                        <li>
                            <div class="title">Rating</div>
                            <div class="text">{{ status.rating }}</div>
                        </li>
                    </ul>
                </div>
                <div class="col-xl-7">
                    <a href="{% url 'recruitment:applicant_status_edit' pk=status.id %}" class="edit-icon float-end" data-bs-toggle="modal" data-bs-target="#applicantStatusUpdate">
                        <i class="fas fa-pencil-alt"></i>
                    </a>
                    {% include 'recruitment/applicant_status_edit.html' %}
                </div>
            </div>
            <div class="row">
                <div class="col-xl-6">
                    <ul class="personal-info">
                        <li>
                            <h6>Notes</h6>
                            <div>{{ status.notes|safe }}</div>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
{% endfor %}

applicant_detail_view

@login_required()
def applicant_detail_view(request, pk):
	applicant_detail = Applicant.objects.get(id=pk)
	form = ApplicantStatusCreateForm
	update_form = ApplicantStatusUpdateForm
	applicant_status_detail = ApplicantStatus.objects.filter(applicant__id=applicant_detail.id)
	prev_post = Applicant.objects.filter(id__lt=pk).order_by('id').last()
	next_post = Applicant.objects.filter(id__gt=pk).order_by('id').first()

	context = {
		'applicant_detail': applicant_detail,
		'form': form,
		'update_form': update_form,
		'applicant_status_detail': applicant_status_detail,
		'prev_post': prev_post,
		'next_post': next_post,
	}
	return render(request, 'recruitment/applicant_detail.html', context)

applicant_status_create.html

<div class="modal fade" id="applicantStatusCreate" tabindex="-1" role="dialog">
    <div class="modal-dialog modal-dialog-centered">
        <form method="post" action="{% url 'recruitment:applicant_status_create' %}">
            {% csrf_token %}
            {{ form.media }}
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Add Applicant Status</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div class="row">
                        <div class="col">
                            <div class="mb-2">
                                {{ form.applicant.label }}
                            </div>
                            <div class="mb-2">
                                {{ form.applicant }}
                                {{ form.applicant.errors }}
                            </div>
                <--- code reduced for readability --->
                <div class="modal-footer">
                    <div class="row">
                        <div class="col">
                            <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal">Cancel</button>
                            <button type="submit" class="btn btn-primary">Add Applicant Status</button>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>

applicant_status_edit.html

<div class="modal fade" id="applicantStatusUpdate" tabindex="-1" role="dialog">
    <div class="modal-dialog modal-dialog-centered">
        <form enctype="multipart/form-data" method="post" action="{% url 'recruitment:applicant_status_edit' pk=status.id %}">
            {% csrf_token %}
            {{ form.media }}
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Update Applicant Status</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div class="row">
                        <div class="col">
                            <div class="mb-2">
                                {{ update_form.applicant.label }}
                            </div>
                            <div class="mb-2">
                                {{ update_form.applicant }}
                                {{ update_form.applicant.errors }}
                            </div>
                <--- code reduced for readability --->
                <div class="modal-footer">
                    <div class="row">
                        <div class="col">
                            <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal">Cancel</button>
                            <button type="submit" class="btn btn-primary">Update Applicant Status</button>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>

I am using bootstrap without custom jquery (it’s loaded in the base.html) and the create form is submitting the data correctly and it’s showing the data in the page after submission.

Here are the 2 views:

class ApplicantStatusCreateView(LoginRequiredMixin, CreateView):

    model = ApplicantStatus
    form_class = ApplicantStatusCreateForm
    template_name = 'recruitment/applicant_status_create.html'

    def get_success_url(self):
        return reverse_lazy('recruitment:applicant_detail', kwargs={'pk': self.object.applicant.id})
class ApplicantStatusUpdateView(LoginRequiredMixin, UpdateView):

    model = ApplicantStatus
    form_class = ApplicantStatusUpdateForm
    template_name = 'recruitment/applicant_status_edit.html'

    def get_success_url(self):
        return reverse_lazy('recruitment:applicant_status_detail', kwargs={'pk': self.object.applicant.id})

Here are the 2 forms:

class ApplicantStatusCreateForm(forms.ModelForm):
    notes = forms.CharField(required=False, widget=CKEditorWidget())
    rating = forms.IntegerField(max_value=5, min_value=0, widget=forms.NumberInput(attrs={'class': 'form-control mb-2'}))

    class Meta:
        model = ApplicantStatus
        fields = ['applicant', 'applicant_status', 'status_date', 'rating', 'notes']
        widgets = {
            'applicant': forms.Select(attrs={'class': 'form-select mb-2'}),
            'applicant_status': forms.Select(attrs={'class': 'form-select mb-2'}),
            'status_date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control mb-2'}),
        }


class ApplicantStatusUpdateForm(ApplicantStatusCreateForm):

    def __init__(self, *args, **kwargs):
        super(ApplicantStatusUpdateForm, self).__init__(*args, **kwargs)
        self.fields['applicant'].disabled = True

How can I get the existing data to show in the modal update form?

Can you provide a road map of what all these files are and what they’re supposed to be doing?

Exactly which of these views/templates are not rendering what you’re looking to see on the page?

How do these views relate to each other?

In application_detail_view you’re defining the forms as follows:

form = ApplicantStatusCreateForm
update_form = ApplicantStatusUpdateForm

That’s setting those variables as the classes. You want to instantiate those classes to create instances of the form.

form = ApplicantStatusCreateForm()
update_form = ApplicantStatusUpdateForm(instance=applicant_detail)

I may be wrong with what to pass in through instance, but I wanted to show you need to do that for an “update” form.

1 Like

Sure,

The idea behind the app is to have an applicant recruitment tracker.

The file structure (partial) looks like this:

+---< my_project >
    |    +---< my_project >
    |    |    |--- < settings >
    |    |    |--- __init__.py
    |    |    |--- asgi.py
    |    |    |--- urls.py
    |    |    |--- views.py
    |    |    |--- wsgi.py
    |    |
    |    +---< recruitment_app >
    |    |    |--- < migrations >
    |    |    |--- __init__.py
    |    |    |--- admin.py
    |    |    |--- apps.py
    |    |    |--- forms.py
    |    |    |--- models.py
    |    |    |--- urls.py
    |    |    |--- views.py
    |    +---< templates >

2 model forms displayed are based of the ApplicantStatus model which are used in class based views for CreateView and UpdateView. Every time there’s a new applicant, we use the CreateView to create a new applicant and UpdateView to update the data as needed. Both views work fine if the are used the regular Django way, where the forms are rendered on their own page template.

I wanted to explore the modals as it improves the user experience.

The applicant_detail view is displaying details of each applicant and I included the details of each status outlined in the <!—Applicant Status Start—> section of applicant_detail.html

The create view in modal is triggered by clicking the Add Applicant Status button and displays all form fields correctly and the data is successfully submitted, shows in the database, and immediately after renders on the applicant_detail page. Every new addition renders the same.

The challenge starts when trying to update the applicant status, which is also supposed to be rendered as a form once clicked on the pencil icon visible in the screenshot. The link is actually showing the right record, but once clicked, the modal displays a blank form instead of the existing record.

Thank you for the suggestions and a reminder on creating instances of the forms.

I tried the following instance for the update form:

def applicant_detail_view(request, pk):
	applicant_detail = Applicant.objects.get(id=pk)
	applicant_status_detail = ApplicantStatus.objects.filter(applicant__id=applicant_detail.id)
	form = ApplicantStatusCreateForm()
	update_form = ApplicantStatusUpdateForm(instance=applicant_status_detail)

but it’s throwing an AttributeError: ‘QuerySet’ object has no attribute ‘_meta’

applicant_detail	
<Applicant: Jim Stone Beam>
applicant_status_detail	
<QuerySet [<ApplicantStatus: Application>, <ApplicantStatus: Phone Screening>, <ApplicantStatus: Skill Test>, <ApplicantStatus: Hiring Manager Interview>]>
form	
<ApplicantStatusCreateForm bound=False, valid=Unknown, fields=(applicant;applicant_status;status_date;rating;notes)>
pk	
6
request	
<WSGIRequest: GET '/recruitment/applicant/6/'>

It looks like there might be a couple different things going on here. But my first reaction to what I’m seeing is that you’re not rendering an edit form for each status.

In your applicant_detail.html you have:

This is going to pull in your applicant_status_edit.html template, and render it as part of this page. That template is rendering fields from a context variable named update_form.

This is being done inside this loop:

However, I don’t see in your view where you’re creating an instance of the update_form for each instance of the applicant’s status being rendered.

Essentially, you need to do something along the lines of creating your update form for each instance of application_status_detail and pass that form as update_form into that child template.

(Never lose sight of the concept that all template rendering occurs on the server and that the page must be completed before sending it to the browser. Once the browser has received the HTML, the server has no further control over the page.)

That’s because you’re trying to pass a queryset (applicant_status_detail) as the instance parameter to ApplicantStatusUpdateForm instead of an instance of ApplicantStatus.

There’s another issue associated with this you need to be aware of, and that’s the potential issues associated with having multiple elements on a page with the same id attribute. Every one of your modals is going to have an id of applicantStatusUpdate, which is likely to create problems somewhere along the line.

For example, this expression:

does not have a unique resolution.

When you’re testing this, don’t just test the first modal. Also verify that every modal on the page works as expected.

I’ve been reading and trying to figure out a way to make this work. I even tried using a function based applicant status update view:

def applicant_status_update_view(request, pk):
	applicant_status = ApplicantStatus.objects.get(id=pk)

	if request.method == 'POST':
		update_form = ApplicantStatusUpdateForm(request.POST, instance=applicant_status)
		if update_form.is_valid():
			update_form.save()
			messages.success(request,
			                 f'{applicant_status.applicant} status was updated successfully.')
			return redirect(reverse_lazy('recruitment:applicant_detail', kwargs={'pk': applicant_status.applicant.id}))
	else:
		update_form = ApplicantStatusUpdateForm(instance=applicant_status)

	context = {'update_form': update_form, 'applicant_status': applicant_status}
	return render(request, 'recruitment/applicant_status_edit.html', context)

The applicant detail view is rendering well as before:

@login_required()
def applicant_detail_view(request, pk):
	applicant_detail = Applicant.objects.get(id=pk)
	form = ApplicantStatusCreateForm()
	update_form = ApplicantStatusUpdateForm()
	applicant_status_detail = ApplicantStatus.objects.filter(applicant__id=applicant_detail.id)
	prev_post = Applicant.objects.filter(id__lt=pk).order_by('id').last()
	next_post = Applicant.objects.filter(id__gt=pk).order_by('id').first()

	context = {
		'applicant_detail': applicant_detail,
		'form': form,
		'update_form': update_form,
		'applicant_status_detail': applicant_status_detail,
		'prev_post': prev_post,
		'next_post': next_post,
	}
	return render(request, 'recruitment/applicant_detail.html', context)

So I tried to have the applicant status update view load a separate page instead of the modal to verify if everything is working well and updating.

To do that, I just removed the action url from the applicant_status_edit.html

<!-- Vertically centered modal -->
<div class="modal fade" id="{{status.id}}" tabindex="-1" role="dialog">
    <div class="modal-dialog modal-dialog-centered">
        <form enctype="multipart/form-data" method="post">
            {% csrf_token %}
            {{ update_form.media }}
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Update Applicant Status</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <div class="row">
                        <div class="col">
                            <div class="mb-2">
                                {{ update_form.applicant.label }}
                            </div>
                            <div class="mb-2">
                                {{ update_form.applicant }}
                                {{ update_form.applicant.errors }}
                            </div>
                        </div>
                <div class="modal-footer">
                    <div class="row">
                        <div class="col">
                            <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal">Cancel</button>
                            <button type="submit" class="btn btn-primary">Update Applicant Status</button>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>

Once the action url is removed, page loads properly, with all fields pre-populated and it saves the data and renders the applicant detail view again, with the updates.

This is where I can’t figure out the modal. I tried several different action urls in the applicant_status_edit.html with variety of kwargs to take me to the applicant detail page:

action="{% url 'recruitment:applicant_detail' pk=applicant_status.applicant.id %}"
action="{% url 'recruitment:applicant_detail' pk=applicant.id %}"
action="{% url 'recruitment:applicant_detail' pk=status.id %}"

and many others but without success.

What am I doing wrong with the update modal? I keep getting the:
Reverse for ‘applicant_detail’ with keyword arguments ‘{‘pk’: ‘’}’ not found.

Given the error message Reverse for ‘applicant_detail’ with keyword arguments ‘{‘pk’: ‘’}’ not found., it means whatever you’re passing in for the pk kwarg in url is either None or set to an empty string. Since you’ve tried a number of variables and they all gave you the same result, the problem is that you’re not accessing the correct context variable. If you look at what the context you’re passing into the template:


	context = {
		'applicant_detail': applicant_detail,
		'form': form,
		'update_form': update_form,
		'applicant_status_detail': applicant_status_detail,
		'prev_post': prev_post,
		'next_post': next_post,
	}
	return render(request, 'recruitment/applicant_detail.html', context)

You can see that application_stats, application and status are not within it. However, there is applicant_detail which is created given the pk from the request’s kwargs. That’s what you should use:

action="{% url 'recruitment:applicant_detail' pk=applicant_detail.id %}"

As an FYI you can install the Django Debug Toolbar to be able to see what context is available in the template.

Thanks for the prompt response. I tried it and it still gives me the same error. I’ll install the debug toolbar and learn how to use it.

Ah sorry, I misread your previous post. You’re rendering the modal from the applicant_status_update_view. The context for that is:

{'update_form': update_form, 'applicant_status': applicant_status}

You can access the applicant_detail via applicant_status.applicant which would make your action:

action="{% url 'recruitment:applicant_detail' pk=applicant_status.applicant.id %}"

Or you can use the ID from the id field on applicant_status. That avoids another DB hit if you’re not using select_related. But if you’re using applicant_status.applicant anywhere else in the view, then it doesn’t really matter as it’ll have been cached on the instance already.

action="{% url 'recruitment:applicant_detail' pk=applicant_status.applicant_id %}"

Tried those too and it didn’t work.

I failed to mention that the error

is highlighting applicant_detail_view and this line in the traceback

These are the details listed:

applicant_detail <Applicant: Joan Martin Smith>
applicant_status_detail	<QuerySet [<ApplicantStatus: Application>...<ApplicantStatus: Rejected>]>
context {'applicant_detail': <Applicant: Joan Martin Smith>,
 'applicant_status_detail': <QuerySet [<ApplicantStatus: Application>, <ApplicantStatus: Phone Screening>, <ApplicantStatus: Skill Test>, <ApplicantStatus: Hiring Manager Interview>, <ApplicantStatus: Client Interview>, <ApplicantStatus: Offer>, <ApplicantStatus: Rejected>]>,
 'form': <ApplicantStatusCreateForm bound=False, valid=False, fields=(applicant;applicant_status;status_date;rating;notes)>,
 'next_post': <Applicant: John M Smith>,
 'prev_post': <Applicant: John Markus Smith>,
 'update_form': <ApplicantStatusUpdateForm bound=False, valid=Unknown, fields=(applicant;applicant_status;status_date;rating;notes)>}
form <ApplicantStatusCreateForm bound=False, valid=False, fields=(applicant;applicant_status;status_date;rating;notes)>
pk 2
request <WSGIRequest: GET '/recruitment/applicant/2/'>
update_form <ApplicantStatusUpdateForm bound=False, valid=Unknown, fields=(applicant;applicant_status;status_date;rating;notes)>

Based on this I tried:

pk=applicant_detail.applicant.id
pk=applicant_detail.id

and with those, the traceback is highlighting this line in the applicant_status_update_view

with these details:

applicant_status <ApplicantStatus: Application>
context {'applicant_status': <ApplicantStatus: Application>,
 'update_form': <ApplicantStatusUpdateForm bound=False, valid=Unknown, fields=(applicant;applicant_status;status_date;rating;notes)>}
pk 5
request <WSGIRequest: GET '/recruitment/applicant_status/5/edit'>
update_form <ApplicantStatusUpdateForm bound=False, valid=Unknown, fields=(applicant;applicant_status;status_date;rating;notes)>

I do have to mention that I’m still new with Django and as much as I learn along the way, sometimes, things like this bug me out (pun intended).