Personally, I think it’s pretty useful and elegant for type annotations to validate requests and inject args converted to native types.
It’s a headline feature of FastAPI, and ASP.NET Core has a similar feature called “model binding”:
Instagram shared how they did this with Django:
I also built a small DRF add-on:
My question is - how much of a need is there for something like this in Django’s ecosystem?
Understandably, it’s just an alternative way to do something that’s already done, not an enabler of all-new use cases. But my feeling is that it could make Django less overwhelming to newcomers (simpler than forms for validating requests) and help it keep pace with other frameworks in developer friendliness.
Given the existence of Forms / Model Forms in core Django and Serializers / ModelSerializers in DRF, I guess I’m not seeing where this might be needed. Can you explain what this provides that the standard tools don’t?
It seems to me that when I’m accepting data from or into a form, the validation belongs in the form, not a view which may be responsible for displaying that form. Forms aren’t useful for just one view - you can use them many places. Therefore, I would want the validation tied to the form or the models underlying the form - not to include that validation in every view rendering that form.
I think there’s some potential, but as Ken says, Django already has a system for that, so I’d like to ideally make the two of them be built around each other - you can imagine a class-based view that derived a form from annotations, for example.
I just want to avoid having two different, unrelated ways of doing something - that’s the opposite of being easy for beginners.
I take your points about forms already being the way to do validation, but I think annotations-as-validation could be better tuned for simpler use cases, such as GET listing views. Similar to how function-based and class-based views provide two different ways to do something, each suited to different situations.
I feel like it could make Django more appealing for developers who “just need a simple web API”, as it does seem to provide a very intuitive and convenient way to handle basic data validation. That said, I appreciate the concerns about it makes things confusing for beginners.
One way to integrate it with forms would be to automatically populate the form with POST data, eg:
def process_booking(form: BookingForm):
// logic
return render("template.html")
The idea about integrating it with class-based views is interesting. I’ll mull that.
I definitely think there’s something here, but I also want to make sure it’s not too “magical” - whatever we use annotations for should have a clear purpose, be user-extensible, and not confuse anyone trying to read the code later on.
I’m not quite sure what it would look like, unfortunately - it’s one of those “know it when I see it” things, where you can put an example in a simple code snippet and it’s immediately obvious what’s happening.
I do worry that views aren’t entirely the right place - what’s the story for error handling if something doesn’t typematch? Unlike an API you can’t return a hard error.
Yeah, I think there’s risk in trying to get too much out of types. Probably better to just use imperative code beyond a certain point. This is an idea I had for error handling:
def search(limit: int = 10, date: date = None, errors: RequestErrors = None):
// if errors exist, an object with properties describing errors (eg errors.limit)
if errors:
return render("error_template.html", {errors: errors})
// ORM logic
return render("results.html")
Forms seems like another place where they could be integrated, but that would be much more ambitious. Pydantic is an example of that approach.