diff --git a/src/backend/InvenTree/InvenTree/metadata.py b/src/backend/InvenTree/InvenTree/metadata.py index 15733fa3de5d..e7fb922d482a 100644 --- a/src/backend/InvenTree/InvenTree/metadata.py +++ b/src/backend/InvenTree/InvenTree/metadata.py @@ -420,6 +420,13 @@ def get_field_info(self, field): if field_info['type'] == 'dependent field': field_info['depends_on'] = field.depends_on + # Extends with extra attributes from the serializer + extra_field_attributes = ['allow_blank', 'allow_null'] + + for attr in extra_field_attributes: + if hasattr(field, attr): + field_info[attr] = getattr(field, attr) + # Extend field info if the field has a get_field_info method if ( not field_info.get('read_only') diff --git a/src/frontend/lib/types/Forms.tsx b/src/frontend/lib/types/Forms.tsx index 364dc38e6fb5..f37a291921be 100644 --- a/src/frontend/lib/types/Forms.tsx +++ b/src/frontend/lib/types/Forms.tsx @@ -48,6 +48,8 @@ export type ApiFormFieldHeader = { * @param model : The model to use for related fields * @param filters : Optional API filters to apply to related fields * @param required : Whether the field is required + * @param allow_null: Whether the field allows null values + * @param allow_blank: Whether the field allows blank values * @param hidden : Whether the field is hidden * @param disabled : Whether the field is disabled * @param error : Optional error message to display @@ -103,6 +105,8 @@ export type ApiFormFieldType = { choices?: ApiFormFieldChoice[]; hidden?: boolean; disabled?: boolean; + allow_null?: boolean; + allow_blank?: boolean; exclude?: boolean; read_only?: boolean; placeholder?: string; diff --git a/src/frontend/src/components/forms/ApiForm.tsx b/src/frontend/src/components/forms/ApiForm.tsx index a9a8606363dd..c88e4a592e41 100644 --- a/src/frontend/src/components/forms/ApiForm.tsx +++ b/src/frontend/src/components/forms/ApiForm.tsx @@ -24,7 +24,11 @@ import { type NavigateFunction, useNavigate } from 'react-router-dom'; import { isTrue } from '@lib/functions/Conversion'; import { getDetailUrl } from '@lib/functions/Navigation'; -import type { ApiFormFieldSet, ApiFormProps } from '@lib/types/Forms'; +import type { + ApiFormFieldSet, + ApiFormFieldType, + ApiFormProps +} from '@lib/types/Forms'; import { useApi } from '../../contexts/ApiContext'; import { type NestedDict, @@ -375,16 +379,28 @@ export function ApiForm({ Object.keys(data).forEach((key: string) => { let value: any = data[key]; - const field_type = fields[key]?.field_type; - const exclude = fields[key]?.exclude; + const field: ApiFormFieldType = fields[key] ?? {}; + const field_type = field?.field_type; + const exclude = field?.exclude; if (field_type == 'file upload' && !!value) { hasFiles = true; } - // Ensure any boolean values are actually boolean - if (field_type === 'boolean') { - value = isTrue(value) || false; + // Special consideration for various field types + switch (field_type) { + case 'boolean': + // Ensure boolean values are actually boolean + value = isTrue(value) || false; + break; + case 'string': + // Replace null string values with an empty string + if (value === null && field?.allow_null == false) { + value = ''; + } + break; + default: + break; } // Stringify any JSON objects @@ -393,7 +409,9 @@ export function ApiForm({ case 'file upload': break; default: - value = JSON.stringify(value); + if (value !== null && value !== undefined) { + // value = JSON.stringify(value); + } break; } } @@ -402,6 +420,7 @@ export function ApiForm({ // Remove the field from the data delete jsonData[key]; } else if (value != undefined) { + // jsonData[key] = value; formData.append(key, value); } });