Open liminspace opened 1 year ago
Hi @liminspace
can you bring some pseudocode vision how would you integrate django-forms into APIs / Routes/ Vlidators ?
class ProductSchema(ModelSchema):
class Config:
model = Product
model_fields = ['name', 'created_at']
class ProductForm(ModelForm):
class Meta:
model = Product
model_fields = ['name', 'created_at']
class CreateProductFormSchema(FormSchema):
class Config:
schema = ProductSchema
form = ProductForm
@api.post("/product")
def create_product(request, payload: CreateProductFormSchema):
# payload is validated by ProductSchema in a regular way
print(payload.data) # access to the data object
form = payload.get_form(user=request.user) # create a form instance with passing extra init args
if not form.is_valid():
raise payload.create_form_error(form.errors) # generate an error response from form errors
product = form.save()
return {"id": product.pk}
It's just my first thought, I think I will have better view after more xp of using django-ninja. Usually the logic of full validation and saving objects are implemented in Django Forms. In this case we can reuse that code. It make sense if we have something that works in django admin and other parts of the project and just want to add that to API. Note, that we have Form/ModelForm and Schema/ModelSchema, so it should support any combination of them. In some general cases it can be implemented as very generic code without defining a lot of classes and manual calling a lot of methods. @vitalik
It would be very nice to have a
single format of validation error and single interface to get validated data
There is a debate in my team about this: what should the schema be responsible for vs what Django forms should be? For now we are using the schemas only to validate data types, giving a 418 error to the developer when the schema does not validate, error that is not supposed to happen at runtime, and a standard 422 error on Django form validation error for business logic level errors
I had a similar issue of migrating and old form with many custom validators. I solved it by writing custom pydantic validators in my ModelSchema. Such, the whole validation was integrated into the single pydantic object and there was no need to switch from the pydantic model to django forms.
Therefore, I just replaced the clean_custom_field_functions with:
from pydantic import validator
class LeadSchema(ModelSchema):
class Config:
model = Lead
model_fields = ["custom_bool_field",]
@validator('custom_bool_field')
def my_validation_function(cls, v: bool):
if not v:
raise ValueError(_("Please confirm our custom bool."))
return v
Unfortunately, the pydantic validator did not find the fields and threw me an error. Instead, I created a schema with the create_schema
function and derived a subclass:
from pydantic import validator
from ninja.orm.factory import create_schema
LeadSchema = create_schema(
Lead,
name="LeadSchema",
fields= ["custom_bool_field",]
)
class LeadValidationSchema(LeadSchema):
@validator('custom_bool_field')
def my_validation_function(cls, v: bool):
if not v:
raise ValueError(_("Please confirm our custom bool."))
return v
This works great.
Nevertheless, the step in between with the subclass feels a bit unnecessary...
@vitalik Can I help somehow to beautify this?
The django-ninja is awesome, but I found something that is not supported. Django has validators that used in models and forms. It would be very helpful to support that feature in django-ninja. Of course after data-validation we can put the data into a form and validate it. But it's better to have it in a Schema class or so. It can validate data in regular way django-ninja does and then put an object into a django form and validate there. The main point of it is having single format of validation error and single interface to get validated data. And also reusing already written code in forms and validators, especially when we use 3rd party libraries. Any thought about it?