Django with HTMX Tutorial #10 - Form Errors

Net Ninja · Beginner ·🌐 Frontend Engineering ·1y ago

Key Takeaways

Handles form errors in a Django application using HTMX

Full Transcript

let's see how to handle form errors that may occur with HDMX and with this modal dialogue that we have for adding a new contact now we saw in the last video right at the end of the video that when we try and add an email address that's already in the contact list nothing happens and if we go back to the server here you can see the unique constraint has failed and that's because a user cannot add an email address that they've already added to the contact list but of course we need to handle this on the client side as well and we need to show some feedback to the user we're going to handle that in this video and we're going to start by going to forms.py and this is where we wrote the contact form in jangle what I'm going to do here is add a method for the email address validation and in Jango the convention inform forms is to call that method cleancore and then the name of the field in this case that's email and we can perform validation of the email address in this function so let's do that just now and we're going to start by getting the email from the cleaned data we can use self. cleaned data and extract the email address that was submitted from from that once we have the submitted email address we can check if that email already exists for this user so we're going to use the contact model for this let's create an if statement here and we're going to use the Jango orm and it's contact. objects. filter so we're going to use some initial data here to get the logged in user so we're going to use self. initial doget and we're going to get the user that we're going to pass in in a second and secondly we want to filter by the email address and look for email addresses that are equal to what has been submitted here and after we' filtered we're going to check if this exists and what we're going to do then is raise a validation error and don't worry if this user self. initial doget thing doesn't make sense at the moment we're going to explain that in a second and the message I want to pass to the validation error is that you already have a contact with this email address and if we don't get any validation error in other words if the if statement is false we can simply return the email address from the clean email function now let's import validation error at the top and that's going to come from jangle doc. exceptions and then if we go back down to the method let's now handle this self. initial doget what that's basically doing is it's going to get any initial data that's passed into the form instance and that's what we need to get the logged in user into this form remember the contacts that we're searching through here we want to get any email addresses that already exist but only for the authenticated user and that's because the unique constraint is on the user and the email field so we're allowing multiple users to have the same contact email address but for a single user we want to prevent them adding a duplicate email address as a contact so we're checking if that duplicate exists with this if statement and we're raising the validation error if that's the case now we need to pass the user in as this initial data so let's go to views.py and we're going to do that now when we instantiate the contact form with the posted data so forms in jangle they can take this initial parameter which is a dictionary and we can set the user here to the request. user property and request. user refers to the authenticated user so we're passing that in as initial data and then we're using that initial data here in the filter statements so we're going to get all contacts belonging to the user with an email address equal to the one that's already been submitted now I want to test this out so let's go back to the browser here and refresh the page and I'm going to try and add another duplicate here with the same email address that already exists now when we add this again nothing happens in the front end at the moment if we go to Jang though you can see that we get now a value error and this is telling us that the view did not return a response so why is this happening let's go back to views.py now we're checking here if the form is valid but the problem is that the form is no longer valid because of this new clean email function it's going to detect that we have a duplicate email address for the user or rather for the user's contact and therefore because this is going to raise a validation error the form do is valid function is going to return false which means all of this code is not going to be executed but we don't have an else block at the moment so what we need to do is add an else block to handle the case when the form is not valid and what we want to do here is we want to return the modal itself with the form already populated with the errors so let me explain this a second when we submit the form and we've got a problem what we want to do is take this existing modal and we want to replace it with the new model that contains the data already submitted by the user and also the error messages that have been generated on the server so I hope that makes sense I think it will make more sense after we write the code what I'm going to do is copy this render statement here and we're going to bring that down into this else block so we're generating a response but we're going to change the template that we're referencing remember we want to return the model so what we're going to do is go to our partials and it's this add contact model here that we want to use so let's reference that template here and we can then close the sidebar so when we have a form error what we're going to do is return the modal itself and we're going to change the code context here and we're just going to do this in line and we're going to add the form with the errors to the context so basically we have a form that we've instantiated with the data that the User submitted but for whatever reason the form is not valid if we get that else block so we're going to take this form and we're going to add it to the context and we're going to render this partial here with the modal that contains the form now we need to do some other things here to get this working now one thing we need to do is change the target of the response so what I'm going to do is go to the mod here that contains the form and this form when it's submitted you can see the target is the table body but when we have an error we don't want to place the model into the table we just want to replace the existing model so we need to retarget the response here when we have an error and we can retarget it by using this dialogue with the ID of contact model at the top now in HDMX you can retarget the response from the server by using another HTTP header so let's add that here and it's the HX retarget response header and we're going to set that to the ID of contact model and that's the dialogue's ID and that dialogue is what the modal essentially represents on the front end so if we look at our modal here it's this ID and we're replacing the entire content of the existing ID with the new one that contains the form with populated errors so for example here we are iterating over the form errors attached to the name field and we have something similar for the email field as well we are going to expect to see those now because the form has errors and we want to see them on the modal itself so that's the purpose of retargeting this to replace the existing model and we also want to change the swap mechanism because if we go back to this model here and go to the form currently the swap is set to after begin but that's only going to work with the table with the modal we want to replace the entire content so what we're going to do is add another header here called HX resap and we can set that to one of the HTM X swap mechanisms and this is going to be outer HTML so when we take the response and we use the outer HTML swap method what that's going to do is replace the entire Target here with the new data from the server now before we go on this mechanism here of referencing front-end IDs in your server side code I know this is a bit of a controversial technique some people don't like this lack of separation of concerns but I think there are trade-offs when you're working with HDMX and jangle compared to with react and jangle for example there are other ways of doing this kind of work on the back end where you don't need to reference a modow specifically but I think for these small kind of applications there's nothing wrong with this technique at all but if you have any opinions on this let us know in the comments now before we return the response here which I'm going to do just now I want to actually add one more header to the response and what that header is going to be is the HX trigger after settle header now we're going to explain this in a second but let's set this to fail we already saw above the HX trigger header which we set to to success and then we use that here with this HX on attribute to close the model when we successfully get a response in other words when we successfully add a contact using this form we're going to use that success event to close the modal and show the table but of course what happens when we don't successfully add a contact and we have an error well we're going to return the modal here and it's going to contain the form and the errors in that form but the problem is when we return that content the modal itself is not going to be opened unless we trigger that event programmatically so what we're going to do is use this attribute here called HX trigger after settle and a say attribute is actually a response header and this corresponds to one of the response headers that HTM X provides so if we look at these here HX trigger will allow you to trigger client side events but the HX trigger after settle header that's going to allow you to trigger those events after the setle step and that's important because when we return the modal with the form as the response here we need need to wait until that content is on the page before we can programmatically trigger the modal to open so what we're going to do is use this fail event that we're going to trigger after the response content has settled in the Dom so let's save this just now and what we're going to do is we're going to add a little bit of JavaScript here to programmatically open the new model that we're returning from the back end here before we add that let's test this out just for now what I'm going to do is refresh the page and if we add another contact with the same email address this time when we click add contact contact you can see that the model has disappeared and the reason for that is because the contact model that was originally on the page has been replaced with the new partial and we're swapping in the new modal by replacing entirely the content of the previous modal but the problem that we're going to try and solve in a second is that the new model that's been returned from the server is here on the page it just hasn't been opened yet so when we click add new contact to actually open the model you can see we get the previous form here including the error that you already have a contact with this given email address now what we want to happen when we have this kind of error is we want the old model to be replaced and we want to programmatically open the new one so that it's here on the page and the user can then edit the contact information so we're going to add a little bit of JavaScript here to handle this so let's go to contacts. HTML and this is the kind of parent template for that entire page what we're going to do within this block content here is go to the bottom and we're going to add a script tag here and we can close that off and we're going to write a little bit of JavaScript here to listen for that fail event so let's use document. add event listener here and we're going to listen for the Dom content loaded event which is fired by the browser when the page is loaded when we get that event we can write an arrow function here to handle what happens next and on the document.body I'm going to use another event listener here and we're going to listen for that fail event from the server and when we get that event we can write another arrow function to handle what happens when that fail event is returned now what I want to do here is get the contact mode out so we're going to use document. get element by ID and that has an ID of contact modal and that's going to be there on the page it's going to have replaced the previous model once we have that we can then call the show modal function from Desy UI in order to actually take that new modal that's been returned and programmatically Trigger the show event so that the user can see that and edit the contact form so with all that done let's try this one one more time and go back to the page when we try and add a new contact here and we have that duplicated email address when we click add contact we get back the new model and that's populated with the errors for the email field so we can show the new model because we've used that trigger after settle header from HTM X and the old modal is replaced with this new one which we then programmatically show using a little bit of JavaScript on the client side and note as well that if we go to forms.py we can add a clean name function and that's for the name field of the contact so I'm going to add a dummy function here what this is going to do is extract the name that's being submitted from the client and if that name starts with the letter X it's going to raisee a validation error here no names beginning with x so let's see that in action and we're going to see that this works with both of the fields when that form is returned so I'm going to add a contact beginning with x here and we're also going to use that duplicate email address and when we click add contact you can see that the two errors are appearing underneath the fields so we now have a system where we can add a new contact and when we do that that successfully adds the contact to the table that we see on the page but if we try and add a new contact that contains some errors in the form submission that's going to return that model to us and show it on the page so that we can then fix those errors and then resubmit the form and we've seen in this video how we can use different HTTP headers in the response to trigger actions on the client depending on the success and failure of given events in our system and all of the functionality that we bu here is working without any kind of page reloading and it's very easy to wire all this up using HTM X and the attributes that it provides so in the next video we're going to move on to deleting items we already have this delete button here we're going to learn how to use the HX delete attribute and wire up that kind of delete functionality in the next video

Original Description

In this Django with HTMX series, you'll learn how to make a contacts app using Django for the backend & HTMX for the frontend. You'll also learn how to upload files to Amazon S3 & deploy the app to Render. 🔥🥷🏼Get instant access to ALL premium courses on NetNinja.dev: https://netninja.dev/ 🔥🥷🏼Get instant access to This Course on NetNinja.dev: https://netninja.dev/p/django-htmx 📂🥷🏼 Access the course files on GitHub: https://github.com/bugbytes-io/htmx-contacthub 🧠🥷🏼HTMX for Beginners: https://netninja.dev/p/htmx-for-beginners 🔗👇 Install HTMX: https://htmx.org/docs/#installing 🔗👇 Install Daisy UI & Tailwind CSS: https://daisyui.com/docs/install/ 🔗👇 Django Storages: https://django-storages.readthedocs.io/en/latest/ 🔗👇 Render: https://render.com/ 🔗👇 Amazon S3 Storage: https://aws.amazon.com/s3/
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from Net Ninja · Net Ninja · 0 of 60

← Previous Next →
1 Regular Expressions (RegEx) Tutorial #14 - Matching a Username
Regular Expressions (RegEx) Tutorial #14 - Matching a Username
Net Ninja
2 Regular Expressions (RegEx) Tutorial #15 - Email RegEx Pattern
Regular Expressions (RegEx) Tutorial #15 - Email RegEx Pattern
Net Ninja
3 Regular Expressions (RegEx) Tutorial #16 - Finishing Touches
Regular Expressions (RegEx) Tutorial #16 - Finishing Touches
Net Ninja
4 GraphQL Tutorial #1 - Introduction to GraphQL
GraphQL Tutorial #1 - Introduction to GraphQL
Net Ninja
5 GraphQL Tutorial #2 - A Birdseye View of GraphQL
GraphQL Tutorial #2 - A Birdseye View of GraphQL
Net Ninja
6 GraphQL Tutorial #3 - Project (stack) Overview
GraphQL Tutorial #3 - Project (stack) Overview
Net Ninja
7 GraphQL Tutorial #4 - Making Queries (front-end preview)
GraphQL Tutorial #4 - Making Queries (front-end preview)
Net Ninja
8 GraphQL Tutorial #5 - Express App Setup
GraphQL Tutorial #5 - Express App Setup
Net Ninja
9 GraphQL Tutorial #6 - Setting up GraphQL
GraphQL Tutorial #6 - Setting up GraphQL
Net Ninja
10 GraphQL Tutorial #7 - GraphQL Schema
GraphQL Tutorial #7 - GraphQL Schema
Net Ninja
11 GraphQL Tutorial #8 - Root Query
GraphQL Tutorial #8 - Root Query
Net Ninja
12 GraphQL Tutorial #9 - The Resolve Function
GraphQL Tutorial #9 - The Resolve Function
Net Ninja
13 GraphQL Tutorial #10 - Testing Queries in Graphiql
GraphQL Tutorial #10 - Testing Queries in Graphiql
Net Ninja
14 GraphQL Tutorial #11 - GraphQL ID Type
GraphQL Tutorial #11 - GraphQL ID Type
Net Ninja
15 GraphQL Tutorial #12 - Author Type
GraphQL Tutorial #12 - Author Type
Net Ninja
16 GraphQL Tutorial #13 - Type Relations
GraphQL Tutorial #13 - Type Relations
Net Ninja
17 GraphQL Tutorial #14 - GraphQL Lists
GraphQL Tutorial #14 - GraphQL Lists
Net Ninja
18 GraphQL Tutorial #15 - More on Root Queries
GraphQL Tutorial #15 - More on Root Queries
Net Ninja
19 GraphQL Tutorial #16 - Connecting to mLab
GraphQL Tutorial #16 - Connecting to mLab
Net Ninja
20 GraphQL Tutorial #17 - Mongoose Models
GraphQL Tutorial #17 - Mongoose Models
Net Ninja
21 GraphQL Tutorial #18 - Mutations
GraphQL Tutorial #18 - Mutations
Net Ninja
22 GraphQL Tutorial #19 - More on Mutations
GraphQL Tutorial #19 - More on Mutations
Net Ninja
23 GraphQL Tutorial #20 - Updating the Resolve Functions
GraphQL Tutorial #20 - Updating the Resolve Functions
Net Ninja
24 GraphQL Tutorial #21 - GraphQL NonNull
GraphQL Tutorial #21 - GraphQL NonNull
Net Ninja
25 GraphQL Tutorial #22 - Adding a Front-end
GraphQL Tutorial #22 - Adding a Front-end
Net Ninja
26 GraphQL Tutorial #23 - Create React App
GraphQL Tutorial #23 - Create React App
Net Ninja
27 GraphQL Tutorial #24 - Book List Component
GraphQL Tutorial #24 - Book List Component
Net Ninja
28 GraphQL Tutorial #25 - Apollo Client Setup
GraphQL Tutorial #25 - Apollo Client Setup
Net Ninja
29 GraphQL Tutorial #26 - Making Queries from React
GraphQL Tutorial #26 - Making Queries from React
Net Ninja
30 GraphQL Tutorial #27 - Rendering Data in a Component
GraphQL Tutorial #27 - Rendering Data in a Component
Net Ninja
31 GraphQL Tutorial #28 - Add Book Component
GraphQL Tutorial #28 - Add Book Component
Net Ninja
32 GraphQL Tutorial #29 - External Query File
GraphQL Tutorial #29 - External Query File
Net Ninja
33 GraphQL Tutorial #30 - Updating Component State
GraphQL Tutorial #30 - Updating Component State
Net Ninja
34 GraphQL Tutorial #31 - Composing Queries
GraphQL Tutorial #31 - Composing Queries
Net Ninja
35 GraphQL Tutorial #32 - query variables
GraphQL Tutorial #32 - query variables
Net Ninja
36 GraphQL Tutorial #33 - Re-fetching Queries
GraphQL Tutorial #33 - Re-fetching Queries
Net Ninja
37 GraphQL Tutorial #34 - Book Details Component
GraphQL Tutorial #34 - Book Details Component
Net Ninja
38 GraphQL Tutorial #36 - Styling the App
GraphQL Tutorial #36 - Styling the App
Net Ninja
39 GraphQL Tutorial #35 - Making a Single Query
GraphQL Tutorial #35 - Making a Single Query
Net Ninja
40 Build Apps with Vue & Firebase - Udemy Course
Build Apps with Vue & Firebase - Udemy Course
Net Ninja
41 Updated Vue & Firebase Course (Udemy)
Updated Vue & Firebase Course (Udemy)
Net Ninja
42 Vue & Firebase Real-time Chat (Preview) #1 - Intro
Vue & Firebase Real-time Chat (Preview) #1 - Intro
Net Ninja
43 Vue & Firebase Real-time Chat (Preview) #2 - Project Structure
Vue & Firebase Real-time Chat (Preview) #2 - Project Structure
Net Ninja
44 Vue & Firebase Real-time Chat (Preview) #3 - Firestore Setup
Vue & Firebase Real-time Chat (Preview) #3 - Firestore Setup
Net Ninja
45 Vue & Firebase Real-time Chat (Preview) #4 - Welcome Screen
Vue & Firebase Real-time Chat (Preview) #4 - Welcome Screen
Net Ninja
46 Vue & Firebase Real-time Chat (Preview) #5 - Props in Routes
Vue & Firebase Real-time Chat (Preview) #5 - Props in Routes
Net Ninja
47 Vue & Firebase Real-time Chat (Preview) #6 - Route Guards
Vue & Firebase Real-time Chat (Preview) #6 - Route Guards
Net Ninja
48 Vue & Firebase Real-time Chat (Preview) #7 - Chat Window
Vue & Firebase Real-time Chat (Preview) #7 - Chat Window
Net Ninja
49 Vue & Firebase Real-time Chat (Preview) #8 - New Message Component
Vue & Firebase Real-time Chat (Preview) #8 - New Message Component
Net Ninja
50 Object Oriented JavaScript Tutorial #1 - Introduction
Object Oriented JavaScript Tutorial #1 - Introduction
Net Ninja
51 Object Oriented JavaScript Tutorial #2 - Object Literals
Object Oriented JavaScript Tutorial #2 - Object Literals
Net Ninja
52 Object Oriented JavaScript Tutorial #3 - Updating Properties
Object Oriented JavaScript Tutorial #3 - Updating Properties
Net Ninja
53 Object Oriented JavaScript Tutorial #4 - Classes
Object Oriented JavaScript Tutorial #4 - Classes
Net Ninja
54 Object Oriented JavaScript Tutorial #5  - Class Constructors
Object Oriented JavaScript Tutorial #5 - Class Constructors
Net Ninja
55 Object Oriented JavaScript Tutorial #6 - Class Methods
Object Oriented JavaScript Tutorial #6 - Class Methods
Net Ninja
56 Object Oriented JavaScript Tutorial #7 - Method Chaining
Object Oriented JavaScript Tutorial #7 - Method Chaining
Net Ninja
57 Object Oriented JavaScript Tutorial #8 - Class Inheritance
Object Oriented JavaScript Tutorial #8 - Class Inheritance
Net Ninja
58 Object Oriented JavaScript Tutorial #9 - Constructors (under the hood)
Object Oriented JavaScript Tutorial #9 - Constructors (under the hood)
Net Ninja
59 Object Oriented JavaScript Tutorial #10 - Prototype
Object Oriented JavaScript Tutorial #10 - Prototype
Net Ninja
60 Object Oriented JavaScript Tutorial #11 - Prototype Inheritance
Object Oriented JavaScript Tutorial #11 - Prototype Inheritance
Net Ninja

Related Reads

Up next
How to Speed Up Your WordPress Website with WP Rocket ⚡Tutorial 2026
Matt Tutorials
Watch →