Skip to content

Commit 8461d09

Browse files
Update documentation, remove unused exception type
1 parent 4747ba6 commit 8461d09

File tree

2 files changed

+9
-84
lines changed

2 files changed

+9
-84
lines changed

docs/customization.qmd

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -141,74 +141,6 @@ Pydantic is used for data validation and serialization. It ensures that the data
141141

142142
If a user-submitted form contains data that has the wrong number, names, or types of fields, Pydantic will raise a `RequestValidationError`, which is caught by middleware and converted into an HTTP 422 error response.
143143

144-
For other, custom validation logic, we add Pydantic `@field_validator` methods to our Pydantic request models and then add the models as dependencies in the signatures of corresponding POST routes. FastAPI's dependency injection system ensures that dependency logic is executed before the body of the route handler.
145-
146-
#### Defining request models and custom validators
147-
148-
For example, in the `UserRegister` request model in `routers/authentication.py`, we add a custom validation method to ensure that the `confirm_password` field matches the `password` field. If not, it raises a custom `PasswordMismatchError`:
149-
150-
```python
151-
class PasswordMismatchError(HTTPException):
152-
def __init__(self, field: str = "confirm_password"):
153-
super().__init__(
154-
status_code=422,
155-
detail={
156-
"field": field,
157-
"message": "The passwords you entered do not match"
158-
}
159-
)
160-
161-
class UserRegister(BaseModel):
162-
name: str
163-
email: EmailStr
164-
password: str
165-
confirm_password: str
166-
167-
# Custom validators are added as class attributes
168-
@field_validator("confirm_password", check_fields=False)
169-
def validate_passwords_match(cls, v: str, values: dict[str, Any]) -> str:
170-
if v != values["password"]:
171-
raise PasswordMismatchError()
172-
return v
173-
# ...
174-
```
175-
176-
We then add this request model as a dependency in the signature of our POST route:
177-
178-
```python
179-
@app.post("/register")
180-
async def register(request: UserRegister = Depends()):
181-
# ...
182-
```
183-
184-
When the user submits the form, Pydantic will first check that all expected fields are present and match the expected types. If not, it raises a `RequestValidationError`. Then, it runs our custom `field_validator`, `validate_passwords_match`. If it finds that the `confirm_password` field does not match the `password` field, it raises a `PasswordMismatchError`. These exceptions can then be caught and handled by our middleware.
185-
186-
(Note that these examples are simplified versions of the actual code.)
187-
188-
#### Converting form data to request models
189-
190-
In addition to custom validation logic, we also need to define a method on our request models that converts form data into the request model. Here's what that looks like in the `UserRegister` request model from the previous example:
191-
192-
```python
193-
class UserRegister(BaseModel):
194-
# ...
195-
196-
@classmethod
197-
async def as_form(
198-
cls,
199-
name: str = Form(...),
200-
email: EmailStr = Form(...),
201-
password: str = Form(...),
202-
confirm_password: str = Form(...)
203-
):
204-
return cls(
205-
name=name,
206-
email=email,
207-
password=password,
208-
confirm_password=confirm_password
209-
)
210-
```
211-
212144
### Middleware exception handling
213145

214146
Middlewares—which process requests before they reach the route handlers and responses before they are sent back to the client—are defined in `main.py`. They are commonly used in web development for tasks such as error handling, authentication token validation, logging, and modifying request/response objects.
@@ -220,8 +152,8 @@ Middleware functions are decorated with `@app.exception_handler(ExceptionType)`
220152
Here's a middleware for handling the `PasswordMismatchError` exception from the previous example, which renders the `errors/validation_error.html` template with the error details:
221153

222154
```python
223-
@app.exception_handler(PasswordMismatchError)
224-
async def password_mismatch_exception_handler(request: Request, exc: PasswordMismatchError):
155+
@app.exception_handler(PasswordValidationError)
156+
async def password_validation_exception_handler(request: Request, exc: PasswordValidationError):
225157
return templates.TemplateResponse(
226158
request,
227159
"errors/validation_error.html",

exceptions/http_exceptions.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,6 @@ def __init__(self, field: str, message: str):
3636
)
3737

3838

39-
class PasswordMismatchError(PasswordValidationError):
40-
def __init__(self, field: str = "confirm_password"):
41-
super().__init__(
42-
field=field,
43-
message="The passwords you entered do not match"
44-
)
45-
46-
4739
class InsufficientPermissionsError(HTTPException):
4840
def __init__(self):
4941
super().__init__(
@@ -52,6 +44,7 @@ def __init__(self):
5244
)
5345

5446

47+
# TODO: Consolidate these two into a single validation error
5548
class EmptyOrganizationNameError(HTTPException):
5649
def __init__(self):
5750
super().__init__(
@@ -60,19 +53,19 @@ def __init__(self):
6053
)
6154

6255

63-
class OrganizationNotFoundError(HTTPException):
56+
class OrganizationNameTakenError(HTTPException):
6457
def __init__(self):
6558
super().__init__(
66-
status_code=404,
67-
detail="Organization not found"
59+
status_code=400,
60+
detail="Organization name already taken"
6861
)
6962

7063

71-
class OrganizationNameTakenError(HTTPException):
64+
class OrganizationNotFoundError(HTTPException):
7265
def __init__(self):
7366
super().__init__(
74-
status_code=400,
75-
detail="Organization name already taken"
67+
status_code=404,
68+
detail="Organization not found"
7669
)
7770

7871

0 commit comments

Comments
 (0)