Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation & Error Bags #30

Open
antoniosarosi opened this issue Nov 7, 2023 · 3 comments
Open

Validation & Error Bags #30

antoniosarosi opened this issue Nov 7, 2023 · 3 comments

Comments

@antoniosarosi
Copy link

antoniosarosi commented Nov 7, 2023

This doesn't seem to be implemented. Is anybody working on it? If not, I can submit a PR myself. I'm thinking something like this will work just fine:

from django import forms
from django.http import HttpRequest, HttpResponse
from django.views import View
from django.shortcuts import redirect

import inertia


class ExampleForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()


class ExampleView(View):
    def post(self, request: HttpRequest) -> HttpResponse:
        form = ExampleForm(request.POST)

        # Option A, more explicit
        if not form.is_valid():
            raise inertia.ValidationError(form.errors)

        # Option B, automatically raise the error
        inertia.validate(form)

        # use form.data here...

        return redirect("some_route_name")

For this to work properly in all cases we need to know the route where the user is coming from. Django doesn't store the previous route in the session like Laravel does, so there's no equivalent of Laravel's return back()->withErrors(). You can emulate the return back() part using the HTTP Referer header as suggested here or by sending a next=URL query param in the request as suggested here.

However, the Referer header can't be trusted in all cases and the next query param requires changing the frontend code. So I'm thinking of 2 possible solutions, either implement Laravel's behavior of storing the previous URL in the session to provide a return back() function or just provide the route at validation time:

inertia.validate(form, error_url="some_route_name_or_url")

Second option is more verbose but easier to implement, and it can coexist with the first option since we could make error_url=None by default and grab the route from the session. I guess all this could be a separate middleware that basically catches the inertia.ValidationError, allowing the users to opt-in by adding the middleware to settings.py. Otherwise it could be integrated into the current inertia.middleware.InertiaMiddleware.

Edit:
Issue #21 is related to this.

@BrandonShar
Copy link
Collaborator

Hey @antoniosarosi thanks for raising this again. I'm very much a Rails/Laravel-minded developer who's built a few apps in Django so I'm sure I missed some of the intricacies of how Django's built in forms work.

My first thought is that explicitly providing the error url seems very pythonic to me; it's obvious and doesn't rely on magic to implement.

I also think it makes sense to integrate this into the existing middleware. It seems like this would be core functionality and, as a user, I wouldn't expect to have to add additional middleware to use it.

@antoniosarosi
Copy link
Author

@BrandonShar I already have a basic working example using a separate middleware in my Django app. I'll copy-paste-refactor the code into the built-in Inertia middleware and submit a PR so we can discuss this further. I also love Laravel, but Django does things differently, particularly when validating forms they don't return back()->withErrors() like we would in Laravel, the example code that I linked doesn't even redirect, it just renders the form again with the errors in the POST response. That's fine if the URL is the same for GETing the form and submitting it, but it might not always be the case. The point is, I don't wanna make Django look like Laravel, but I think integrating Inertia requires some built-in functionality that Django doesn't provide.

@tchappui
Copy link

I'm not using Laravel, but the Inertia documentation suggests flashing form errors into the session and then redirecting to the form page. Render could automatically extract errors from the session into page.props.errors. It could do the same with the session messages key, enabling the Django messages framework to work with Django-Inertia.

Having build_props in render to automatically extract errors and messages from request.session might be a naive implementation of this.

At the moment, I wrap inertia.render to do exactly this. Then, we just have to put form.errors in the session and redirect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants