From acc0748ec4f1487ecdf09cc602eb7b1659f88563 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Fri, 29 Mar 2024 10:37:27 +0100 Subject: [PATCH 01/16] chore: current work, relation v2->v1 not ended --- src/DSC/schema_v2_utils.py | 362 +++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 src/DSC/schema_v2_utils.py diff --git a/src/DSC/schema_v2_utils.py b/src/DSC/schema_v2_utils.py new file mode 100644 index 000000000..4388bb4ee --- /dev/null +++ b/src/DSC/schema_v2_utils.py @@ -0,0 +1,362 @@ +from typing import Dict, List, Literal + +from forestadmin.agent_toolkit.utils.forest_schema.filterable import FrontendFilterableUtils +from forestadmin.agent_toolkit.utils.forest_schema.type import ( + ForestServerAction, + ForestServerCollection, + ForestServerField, + ForestServerSegment, + ServerValidationType, +) +from forestadmin.datasource_toolkit.interfaces.fields import ColumnAlias, Operator, PrimitiveType +from typing_extensions import NotRequired, TypedDict + + +# TYPING +class SchemaV2Relation(TypedDict): + name: str + type: Literal["ManyToMany", "ManyToOne", "OneToOne", "OneToMany"] + foreignCollection: str + throughCollection: NotRequired[str] + foreignKey: NotRequired[str] + foreignKeyTarget: NotRequired[str] + originKey: NotRequired[str] + originKeyTarget: NotRequired[str] + + +class SchemaV2Field(TypedDict): + name: str + type: ColumnAlias + filterOperators: List[str] + # defaultValue: NotRequired[Any] # TODO + # isRequired: NotRequired[Any] # TODO + enumerations: NotRequired[List[str]] + isPrimaryKey: NotRequired[bool] + isWritable: NotRequired[bool] + isSortable: NotRequired[bool] + validations: NotRequired[List[ServerValidationType]] + + +class SchemaV2Collection(TypedDict): + name: str + fields: List[SchemaV2Field] # to define + relations: List # to define + segments: NotRequired[List[ForestServerSegment]] + actions: NotRequired[List[ForestServerAction]] + isSearchable: NotRequired[bool] + canList: NotRequired[bool] + canCreate: NotRequired[bool] + canUpdate: NotRequired[bool] + canDelete: NotRequired[bool] + canCount: NotRequired[bool] + canChart: NotRequired[bool] + + +# MASKS +SCHEMA_V2_FIELDS_MASK = { + "enumerations": None, + "isPrimaryKey": False, + "isWritable": True, + "isSortable": True, + "validations": [], +} + + +SCHEMA_V2_COLLECTION_MASK = { + "segments": [], + "actions": [], + "isSearchable": True, + "canList": True, + "canCreate": True, + "canUpdate": True, + "canDelete": True, + "canCount": True, + "canChart": True, +} + + +class SchemaV1toV2: + def __init__(self, schema_collections: List[ForestServerCollection]) -> None: + self.initial_schema: List[ForestServerCollection] = schema_collections + + def translate(self) -> List[SchemaV2Collection]: + return self._convert_collection_schema_v1_to_v2(self.initial_schema) + + def _convert_collection_schema_v1_to_v2( + self, schema_collections: List[ForestServerCollection] + ) -> List[SchemaV2Collection]: + schema_collections_v2: List[SchemaV2Collection] = [] + for collection_v1 in schema_collections: + collection_v2: SchemaV2Collection = { + "name": collection_v1["name"], + "fields": self._convert_fields_v1_to_v2(collection_v1["fields"]), + "relations": self._convert_fields_to_relation(collection_v1["fields"]), + "actions": collection_v1["actions"], # type:ignore + "isSearchable": collection_v1["isSearchable"], + "canList": True, + "canCreate": not collection_v1["isReadOnly"], + "canUpdate": not collection_v1["isReadOnly"], + "canDelete": not collection_v1["isReadOnly"], + "canCount": True, + "canChart": True, + } + schema_collections_v2.append(self._template_reduce_collection(collection_v2)) + + return schema_collections_v2 + + def _convert_fields_v1_to_v2(self, fields: List[ForestServerField]) -> List[SchemaV2Field]: + fields_v2: List[SchemaV2Field] = [] + for field_v1 in fields: + if field_v1["relationship"] is not None: # type:ignore + continue + + fields_v2.append( + self._template_reduce_field( + { + "name": field_v1["field"], # type:ignore + "type": field_v1["type"], # type:ignore + "filterOperators": [ + op.value + for op in FrontendFilterableUtils.OPERATOR_BY_TYPES[ + PrimitiveType(field_v1["field"]) # type:ignore + ] # type:ignore + ], # type:ignore + "enumerations": field_v1["enums"] if field_v1["type"] == "Enum" else None, # type:ignore + "isPrimaryKey": field_v1["isPrimaryKey"], # type:ignore + "isSortable": field_v1["isSortable"], # type:ignore + "isWritable": not field_v1["isReadOnly"], # type:ignore + "validations": field_v1["validations"], # type:ignore + } + ) + ) + + return fields_v2 + + def _convert_fields_to_relation(self, fields: List[ForestServerField]) -> List[SchemaV2Relation]: + relation_v2: List[SchemaV2Relation] = [] + for field_v1 in fields: + if field_v1["relationship"] is None: # type:ignore + continue + + relation: SchemaV2Relation = { + "name": field_v1["field"], # type:ignore + "foreignCollection": field_v1["reference"].split(".")[0], # type:ignore + } # type:ignore + if field_v1["relationship"] == "BelongsTo": # type:ignore + relation = { + **relation, + "type": "ManyToOne", + "foreignKeyTarget": field_v1["reference"].split(".")[1], # type:ignore + # TODO: may be impossible because v1 schema doesn't include foreign_keys + "foreignKey": "TODO: N/A", # type:ignore + # doable in the case where it's the reverse of a 1to1 + } + + elif field_v1["relationship"] == "HasMany": # type:ignore + relation = { + **relation, + "type": "OneToMany", + "originKey": "", + # TODO: may be impossible because v1 schema doesn't include foreign_keys + "originKeyTarget": field_v1["reference"].split(".")[1], # type:ignore # TODO: not sure about this + } + + elif field_v1["relationship"] == "HasOne": # type:ignore + relation = { + **relation, + "type": "OneToOne", + "originKey": "", + "originKeyTarget": field_v1["reference"].split(".")[1], # type:ignore + } + + elif field_v1["relationship"] == "BelongsToMany": # type:ignore + reverse_relation = filter( # type:ignore + lambda x: x["fields"] == field_v1["inverseOf"], # type:ignore + self.initial_schema[relation["foreignCollection"]]["fields"], # type:ignore + )[0] + + relation = { + **relation, + "type": "ManyToMany", + "foreignKeyTarget": field_v1["reference"].split(".")[1], # type:ignore + "originKeyTarget": reverse_relation["reference"].split(".")[1], + # TODO: may be impossible because v1 schema doesn't include foreign_keys + "originKey": "TODO: N/A", + "foreignKey": "TODO: N/A", + "throughCollection": "TODO: N/A", + } + relation_v2.append(relation) + + return relation_v2 + + def _template_reduce_field(self, collection: SchemaV2Field) -> SchemaV2Field: + return self._reduce_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore + + def _template_reduce_collection(self, collection: SchemaV2Collection) -> SchemaV2Collection: + return self._reduce_from_template(collection, SCHEMA_V2_COLLECTION_MASK) # type:ignore + + def _reduce_from_template(self, input, mask): + reduced = {} + for key, value in input: + if key not in mask or input[key] != mask[key]: + reduced[key] = value + return reduced + + +class SchemaV2toV1: + def __init__(self, schema_collections: List[SchemaV2Collection]) -> None: + self.initial_schema = schema_collections + + def translate(self) -> List[ForestServerCollection]: + return self._convert_collection_schema_v2_to_v1(self.initial_schema) + + def _get_value_or_default(self, instance, key, mask): + return instance.get(key, mask[key]) + + def collection_get_value_or_default(self, instance, key): + return self._get_value_or_default(instance, key, SCHEMA_V2_COLLECTION_MASK) + + def field_get_value_or_default(self, instance, key): + return self._get_value_or_default(instance, key, SCHEMA_V2_FIELDS_MASK) + + def _convert_collection_schema_v2_to_v1( + self, collections_v2: List[SchemaV2Collection] + ) -> List[ForestServerCollection]: + schema_collections_v1: List[ForestServerCollection] = [] + for collection_v2 in collections_v2: + schema_collections_v1.append( + { + "name": collection_v2["name"], + "icon": None, + "integration": None, + "isVirtual": False, + "onlyForRelationships": False, + "paginationType": "page", + "fields": self.convert_fields_and_relation(collection_v2["fields"], collection_v2["relations"]), + "isReadOnly": not collection_v2.get("canCreate", SCHEMA_V2_COLLECTION_MASK["canCreate"]), + "isSearchable": collection_v2.get("isSearchable", SCHEMA_V2_COLLECTION_MASK["isSearchable"]), + "actions": collection_v2.get("actions", SCHEMA_V2_COLLECTION_MASK["actions"]), + "segments": collection_v2.get("segments", SCHEMA_V2_COLLECTION_MASK["segments"]), + } + ) + + return schema_collections_v1 + + def convert_fields_and_relation( + self, fields: List[SchemaV2Field], relations: List[SchemaV2Relation] + ) -> List[ForestServerField]: + fields_v1: Dict[str, ForestServerField] = {} + + for field_v2 in fields: + fields_v1[field_v2["name"]] = { + "field": field_v2["name"], + "type": field_v2["type"], + "isPrimaryKey": field_v2.get("isPrimaryKey", SCHEMA_V2_FIELDS_MASK["isPrimaryKey"]), + "isReadOnly": not field_v2.get("isWritable", SCHEMA_V2_FIELDS_MASK["isWritable"]), + "isSortable": field_v2.get("isSortable", SCHEMA_V2_FIELDS_MASK["isSortable"]), + # "defaultValue": None # TODO: need to handle this + "isRequired": len( + [ + *filter( + lambda x: x["type"] == "is present", + field_v2.get("validations", SCHEMA_V2_FIELDS_MASK["validations"]), + ) + ] + ) + > 0, # type:ignore + "enums": ( + field_v2.get("enumerations", SCHEMA_V2_FIELDS_MASK["enumerations"]) + if field_v2["type"] == "Enum" + else None + ), + "isFilterable": FrontendFilterableUtils.is_filterable( + field_v2["type"], + set([Operator(op) for op in field_v2["filterOperators"]]), + ), + "validations": field_v2.get("validations", SCHEMA_V2_FIELDS_MASK["validations"]), + "integration": None, + "isVirtual": False, + "inverseOf": None, + # "relationship": None, not set when it's a field + } + + for rel_v2 in relations: + name = rel_v2["name"] + + if rel_v2["type"] == "OneToMany": + related_field_v2: SchemaV2Field = [ + *filter(lambda x: x["name"] == rel_v2["originKey"], fields) # type:ignore + ][0] + fields_v1[name] = { + **fields_v1[name], + "relationship": "HasMany", + } + + elif rel_v2["type"] == "ManyToOne": + foreign_collection_fields = [ + *filter(lambda x: x["name"] == rel_v2["foreignCollection"], self.initial_schema) # type:ignore + ][0]["fields"] + related_field_v2: SchemaV2Field = [ + *filter(lambda x: x["name"] == rel_v2["foreignKey"], foreign_collection_fields) # type:ignore + ][0] + + fields_v1[name] = { + **fields_v1[name], + "relationship": "BelongsTo", + } + + elif rel_v2["type"] == "OneToOne": + related_field_v2: SchemaV2Field = [ + *filter(lambda x: x["name"] == rel_v2["originKey"], fields) # type:ignore + ][0] + fields_v1[name] = { + **fields_v1[name], + "relationship": "HasOne", + } + + elif rel_v2["type"] == "ManyToMany": + foreign_collection_fields = [ + *filter(lambda x: x["name"] == rel_v2["foreignCollection"], self.initial_schema) # type:ignore + ][0]["fields"] + related_field_v2: SchemaV2Field = [ + *filter(lambda x: x["name"] == rel_v2["foreignKey"], foreign_collection_fields) # type:ignore + ][0] + + fields_v1[name] = { + **fields_v1[name], + "relationship": "BelongsToMany", + } + + # fields_v1[rel_v2["name"]] = { + # "field": rel_v2["name"], + # "type": rel_v2["type"], + # "isPrimaryKey": rel_v2.get("isPrimaryKey", SCHEMA_V2_FIELDS_MASK["isPrimaryKey"]), + # "isReadOnly": not rel_v2.get("isWritable", SCHEMA_V2_FIELDS_MASK["isWritable"]), + # "isSortable": rel_v2.get("isSortable", SCHEMA_V2_FIELDS_MASK["isSortable"]), + # # "defaultValue": None # TODO: need to handle this + # "isRequired": len( + # [ + # *filter( + # lambda x: x["type"] == "is present", + # rel_v2.get("validations", SCHEMA_V2_FIELDS_MASK["validations"]), + # ) + # ] + # ) + # > 0, # type:ignore + # "enums": ( + # rel_v2.get("enumerations", SCHEMA_V2_FIELDS_MASK["enumerations"]) + # if rel_v2["type"] == "Enum" + # else None + # ), + # "isFilterable": FrontendFilterableUtils.is_filterable( + # rel_v2["type"], + # set([Operator(op) for op in rel_v2["filterOperators"]]), + # ), + # "validations": rel_v2.get("validations", SCHEMA_V2_FIELDS_MASK["validations"]), + # "integration": None, + # "isVirtual": False, + # "inverseOf": None, + # # "relationship": None, not set when it's a field + # } + + return [*fields_v1.values()] From 000912ab0422fd96bbc390afe2aa8834733aabaf Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Tue, 2 Apr 2024 16:36:57 +0200 Subject: [PATCH 02/16] feat(schema): generate new style schema --- .../django_demo/.forestadmin-schema.json | 66 +- .../.forestadmin-schema_full_v2.json | 4236 +++++++++++++++++ .../django_demo/.forestadmin-schema_v2.json | 3461 ++++++++++++++ .../django/django_demo/app/forest_admin.py | 4 +- .../forestadmin/agent_toolkit/agent.py | 8 + .../utils/forest_schema/emitter_v2.py | 76 + .../forest_schema/generator_collection_v2.py | 54 + .../utils/forest_schema/generator_field_v2.py | 121 + .../utils/forest_schema/type_v2.py | 114 + .../datasource_django/collection.py | 9 + .../datasource_django/interface.py | 3 +- .../datasource_toolkit/collections.py | 20 +- .../interfaces/models/collections.py | 14 +- 13 files changed, 8151 insertions(+), 35 deletions(-) create mode 100644 src/_example/django/django_demo/.forestadmin-schema_full_v2.json create mode 100644 src/_example/django/django_demo/.forestadmin-schema_v2.json create mode 100644 src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py create mode 100644 src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py create mode 100644 src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py create mode 100644 src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py diff --git a/src/_example/django/django_demo/.forestadmin-schema.json b/src/_example/django/django_demo/.forestadmin-schema.json index 483a9a409..31ae27876 100644 --- a/src/_example/django/django_demo/.forestadmin-schema.json +++ b/src/_example/django/django_demo/.forestadmin-schema.json @@ -9,7 +9,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -242,7 +241,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [ { @@ -501,7 +499,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [ { @@ -532,7 +529,7 @@ "field": "customer_id", "integration": null, "inverseOf": null, - "isFilterable": false, + "isFilterable": true, "isPrimaryKey": false, "isReadOnly": true, "isRequired": false, @@ -628,7 +625,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [ { "id": "app_customer-0-export json", @@ -1079,7 +1075,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -1154,7 +1149,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -1229,7 +1223,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -1298,7 +1291,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -1400,7 +1392,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [ { "id": "app_order-0-export json", @@ -1428,8 +1419,8 @@ }, { "field": "customer", - "value": "", - "defaultValue": "", + "value": null, + "defaultValue": null, "description": "", "enums": null, "hook": null, @@ -1451,7 +1442,7 @@ { "id": "app_order-1-refund order(s)", "name": "Refund order(s)", - "type": "bulk", + "type": "single", "baseUrl": null, "endpoint": "/forest/_actions/app_order/1/refund order(s)", "httpMethod": "POST", @@ -1696,6 +1687,22 @@ "type": "Date", "validations": [] }, + { + "defaultValue": null, + "enums": null, + "field": "ordered_date", + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "type": "Date", + "validations": [] + }, { "defaultValue": null, "enums": [ @@ -1728,6 +1735,22 @@ } ] }, + { + "defaultValue": null, + "enums": null, + "field": "test_skillcorner", + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "type": "String", + "validations": [] + }, { "defaultValue": null, "enums": null, @@ -1755,7 +1778,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -1885,7 +1907,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -1960,7 +1981,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2139,7 +2159,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2460,7 +2479,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2535,7 +2553,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2610,7 +2627,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2700,7 +2716,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2903,7 +2918,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -2972,7 +2986,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -3142,7 +3155,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -3263,7 +3275,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -3349,7 +3360,6 @@ "isSearchable": true, "onlyForRelationships": false, "paginationType": "page", - "searchField": null, "actions": [], "segments": [], "fields": [ @@ -3510,10 +3520,10 @@ ], "meta": { "liana": "agent-python", - "liana_version": "1.4.0", + "liana_version": "1.5.6", "stack": { "engine": "python", - "engine_version": "3.10.11" + "engine_version": "3.11.3" } } } \ No newline at end of file diff --git a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json new file mode 100644 index 000000000..3c1b47525 --- /dev/null +++ b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json @@ -0,0 +1,4236 @@ +{ + "collections": [ + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "city", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "country", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "street", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is shorter than", + "value": 2, + "message": null + } + ], + "name": "street_number", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 5, + "message": null + } + ], + "name": "zip_code", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "customers", + "type": "ManyToMany", + "foreignCollection": "customer", + "throughCollection": "customers_addresses", + "originKey": "address_id", + "originKeyTarget": "id", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "flaskcustomersaddresses_address", + "type": "OneToMany", + "foreignCollection": "customers_addresses", + "originKey": "address_id", + "originKeyTarget": "id" + }, + { + "name": "flaskorder_billing_address", + "type": "OneToMany", + "foreignCollection": "order", + "originKey": "billing_address_id", + "originKeyTarget": "id" + }, + { + "name": "order_delivering_address_set_delivering_address", + "type": "OneToMany", + "foreignCollection": "order", + "originKey": "delivering_address_id", + "originKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "address" + }, + { + "segments": [ + { + "id": "app_address.highOrderDelivery", + "name": "highOrderDelivery" + } + ], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "city", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "complete_address", + "type": "String", + "filterOperators": [] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": "France", + "isSortable": true, + "validations": [ + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "pays", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "postal_code", + "type": [ + { + "codePostal": "String", + "codeCommune": "String", + "nomCommune": "String", + "libelleAcheminement": "String" + } + ], + "filterOperators": [] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "street", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": "75009", + "isSortable": true, + "validations": [ + { + "type": "is shorter than", + "value": 5, + "message": null + } + ], + "name": "zip_code", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "billing_orders_billing_address", + "type": "OneToMany", + "foreignCollection": "app_order", + "originKey": "billing_address_id", + "originKeyTarget": "id" + }, + { + "name": "customeraddress_address", + "type": "OneToMany", + "foreignCollection": "app_customeraddress", + "originKey": "address_id", + "originKeyTarget": "id" + }, + { + "name": "customers", + "type": "ManyToMany", + "foreignCollection": "app_customer", + "throughCollection": "app_customeraddress", + "originKey": "address_id", + "originKeyTarget": "id", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "delivering_orders_delivering_address", + "type": "OneToMany", + "foreignCollection": "app_order", + "originKey": "delivering_address_id", + "originKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": false, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_address" + }, + { + "segments": [ + { + "id": "app_cart.No order", + "name": "No order" + } + ], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "IncludesAll", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "order_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "extendedcart", + "type": "OneToOne", + "foreignCollection": "app_extendedcart", + "originKey": "cart_id", + "originKeyTarget": "id" + }, + { + "name": "order", + "type": "ManyToOne", + "foreignCollection": "app_order", + "foreignKey": "order_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_cart" + }, + { + "segments": [ + { + "id": "app_customer.VIP customers", + "name": "VIP customers" + }, + { + "id": "app_customer.with french address", + "name": "with french address" + } + ], + "actions": [ + { + "id": "app_customer-0-export json", + "name": "Export json", + "type": "bulk", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + }, + { + "id": "app_customer-1-age operation", + "name": "Age operation", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/1/age operation", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "Loading...", + "type": "String", + "isReadOnly": true, + "defaultValue": "Form is loading", + "value": null, + "description": "", + "enums": null, + "hook": null, + "isRequired": false, + "reference": null, + "widget": null + } + ], + "hooks": { + "load": true, + "change": [ + "changeHook" + ] + } + } + ], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "TotalSpending", + "type": "Number", + "filterOperators": [] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "age", + "type": "Number", + "filterOperators": [] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "avatar", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "birthday_date", + "type": "Dateonly", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "full_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "GreaterThan", + "In", + "IncludesAll", + "LessThan", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": false, + "isSortable": true, + "validations": [], + "name": "is_vip", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "last_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "updated_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + } + ], + "relations": [ + { + "name": "Customer_blocked_customer+_from_customer", + "type": "OneToMany", + "foreignCollection": "app_customer_blocked_customer", + "originKey": "from_customer_id", + "originKeyTarget": "id" + }, + { + "name": "Customer_blocked_customer+_to_customer", + "type": "OneToMany", + "foreignCollection": "app_customer_blocked_customer", + "originKey": "to_customer_id", + "originKeyTarget": "id" + }, + { + "name": "addresses", + "type": "ManyToMany", + "foreignCollection": "app_address", + "throughCollection": "app_customeraddress", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "block_by_users", + "type": "ManyToMany", + "foreignCollection": "app_customer", + "throughCollection": "app_customer_blocked_customer", + "originKey": "to_customer_id", + "originKeyTarget": "id", + "foreignKey": "from_customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "blocked_customer", + "type": "ManyToMany", + "foreignCollection": "app_customer", + "throughCollection": "app_customer_blocked_customer", + "originKey": "from_customer_id", + "originKeyTarget": "id", + "foreignKey": "to_customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "customeraddress_customer", + "type": "OneToMany", + "foreignCollection": "app_customeraddress", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "orders_customer", + "type": "OneToMany", + "foreignCollection": "app_order", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "smart_billing_addresses", + "type": "ManyToMany", + "foreignCollection": "app_address", + "throughCollection": "app_order", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "billing_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "smart_carts", + "type": "OneToMany", + "foreignCollection": "app_cart", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "smart_delivering_addresses", + "type": "ManyToMany", + "foreignCollection": "app_address", + "throughCollection": "app_order", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "delivering_address_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_customer" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "from_customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "to_customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "from_customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "from_customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "to_customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "to_customer_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_customer_blocked_customer" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "address", + "type": "ManyToOne", + "foreignCollection": "app_address", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_customeraddress" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "discount", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "extendedcart", + "type": "OneToOne", + "foreignCollection": "app_extendedcart", + "originKey": "discount_id", + "originKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_discountcart" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "cart_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 20, + "message": null + } + ], + "name": "color", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "discount_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "cart", + "type": "ManyToOne", + "foreignCollection": "app_cart", + "foreignKey": "cart_id", + "foreignKeyTarget": "id" + }, + { + "name": "discount", + "type": "ManyToOne", + "foreignCollection": "app_discountcart", + "foreignKey": "discount_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_extendedcart" + }, + { + "segments": [ + { + "id": "app_order.Delivered order", + "name": "Delivered order" + }, + { + "id": "app_order.Dispatched order", + "name": "Dispatched order" + }, + { + "id": "app_order.Pending order", + "name": "Pending order" + }, + { + "id": "app_order.Rejected order", + "name": "Rejected order" + }, + { + "id": "app_order.Suspicious order", + "name": "Suspicious order" + }, + { + "id": "app_order.newly_created", + "name": "newly_created" + } + ], + "actions": [ + { + "id": "app_order-0-export json", + "name": "Export json", + "type": "global", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [ + { + "field": "dummy field", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + }, + { + "field": "customer", + "value": null, + "defaultValue": null, + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": true, + "reference": "app_customer.id", + "type": "Number", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + }, + { + "id": "app_order-1-refund order(s)", + "name": "Refund order(s)", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/1/refund order(s)", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "reason", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + } + ], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "billing_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is greater than", + "value": 0, + "message": null + }, + { + "type": "is greater than", + "value": 0, + "message": null + } + ], + "name": "cost", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "customer_first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "customer_full_name", + "type": "String", + "filterOperators": [] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "delivering_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "ordered_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "ordered_date", + "type": "Date", + "filterOperators": [] + }, + { + "enumerations": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ], + "name": "status", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": false, + "validations": [], + "name": "test_skillcorner", + "type": "String", + "filterOperators": [] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "updated_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + } + ], + "relations": [ + { + "name": "billing_address", + "type": "ManyToOne", + "foreignCollection": "app_address", + "foreignKey": "billing_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "cart", + "type": "OneToOne", + "foreignCollection": "app_cart", + "originKey": "order_id", + "originKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "delivering_address", + "type": "ManyToOne", + "foreignCollection": "app_address", + "foreignKey": "delivering_address_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "app_order" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } + ], + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "Group_permissions+_group", + "type": "OneToMany", + "foreignCollection": "auth_group_permissions", + "originKey": "group_id", + "originKeyTarget": "id" + }, + { + "name": "User_groups+_group", + "type": "OneToMany", + "foreignCollection": "auth_user_groups", + "originKey": "group_id", + "originKeyTarget": "id" + }, + { + "name": "permissions", + "type": "ManyToMany", + "foreignCollection": "auth_permission", + "throughCollection": "auth_group_permissions", + "originKey": "group_id", + "originKeyTarget": "id", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToMany", + "foreignCollection": "auth_user", + "throughCollection": "auth_user_groups", + "originKey": "group_id", + "originKeyTarget": "id", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "auth_group" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "group_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "permission_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "group", + "type": "ManyToOne", + "foreignCollection": "auth_group", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "permission", + "type": "ManyToOne", + "foreignCollection": "auth_permission", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "auth_group_permissions" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ], + "name": "codename", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "content_type_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "Group_permissions+_permission", + "type": "OneToMany", + "foreignCollection": "auth_group_permissions", + "originKey": "permission_id", + "originKeyTarget": "id" + }, + { + "name": "User_user_permissions+_permission", + "type": "OneToMany", + "foreignCollection": "auth_user_user_permissions", + "originKey": "permission_id", + "originKeyTarget": "id" + }, + { + "name": "content_type", + "type": "ManyToOne", + "foreignCollection": "django_content_type", + "foreignKey": "content_type_id", + "foreignKeyTarget": "id" + }, + { + "name": "group", + "type": "ManyToMany", + "foreignCollection": "auth_group", + "throughCollection": "auth_group_permissions", + "originKey": "permission_id", + "originKeyTarget": "id", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToMany", + "foreignCollection": "auth_user", + "throughCollection": "auth_user_user_permissions", + "originKey": "permission_id", + "originKeyTarget": "id", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "auth_permission" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "date_joined", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is shorter than", + "value": 254, + "message": null + } + ], + "name": "email", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } + ], + "name": "first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": true, + "isSortable": true, + "validations": [], + "name": "is_active", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": false, + "isSortable": true, + "validations": [], + "name": "is_staff", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": false, + "isSortable": true, + "validations": [], + "name": "is_superuser", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "last_login", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } + ], + "name": "last_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 128, + "message": null + } + ], + "name": "password", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } + ], + "name": "username", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "User_groups+_user", + "type": "OneToMany", + "foreignCollection": "auth_user_groups", + "originKey": "user_id", + "originKeyTarget": "id" + }, + { + "name": "User_user_permissions+_user", + "type": "OneToMany", + "foreignCollection": "auth_user_user_permissions", + "originKey": "user_id", + "originKeyTarget": "id" + }, + { + "name": "groups", + "type": "ManyToMany", + "foreignCollection": "auth_group", + "throughCollection": "auth_user_groups", + "originKey": "user_id", + "originKeyTarget": "id", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "logentry_user", + "type": "OneToMany", + "foreignCollection": "django_admin_log", + "originKey": "user_id", + "originKeyTarget": "id" + }, + { + "name": "user_permissions", + "type": "ManyToMany", + "foreignCollection": "auth_permission", + "throughCollection": "auth_user_user_permissions", + "originKey": "user_id", + "originKeyTarget": "id", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "auth_user" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "group_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "user_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "group", + "type": "ManyToOne", + "foreignCollection": "auth_group", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToOne", + "foreignCollection": "auth_user", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "auth_user_groups" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "permission_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "user_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "permission", + "type": "ManyToOne", + "foreignCollection": "auth_permission", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToOne", + "foreignCollection": "auth_user", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "auth_user_user_permissions" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "order_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "order", + "type": "ManyToOne", + "foreignCollection": "order", + "foreignKey": "order_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "cart" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "age", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "avatar", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "birthday_date", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "is_vip", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ], + "name": "last_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "addresses", + "type": "ManyToMany", + "foreignCollection": "address", + "throughCollection": "customers_addresses", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "flaskcustomersaddresses_customer", + "type": "OneToMany", + "foreignCollection": "customers_addresses", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "flaskorder_customer", + "type": "OneToMany", + "foreignCollection": "order", + "originKey": "customer_id", + "originKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "customer" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "customer_id", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "address", + "type": "ManyToOne", + "foreignCollection": "address", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "customers_addresses" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": [ + "1", + "2", + "3" + ], + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "action_flag", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "action_time", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "change_message", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "content_type_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "object_id", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 200, + "message": null + } + ], + "name": "object_repr", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "user_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "content_type", + "type": "ManyToOne", + "foreignCollection": "django_content_type", + "foreignKey": "content_type_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToOne", + "foreignCollection": "auth_user", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "django_admin_log" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ], + "name": "app_label", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ], + "name": "model", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [ + { + "name": "logentry_content_type", + "type": "OneToMany", + "foreignCollection": "django_admin_log", + "originKey": "content_type_id", + "originKeyTarget": "id" + }, + { + "name": "permission_content_type", + "type": "OneToMany", + "foreignCollection": "auth_permission", + "originKey": "content_type_id", + "originKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "django_content_type" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "expire_date", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "session_data", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 40, + "message": null + } + ], + "name": "session_key", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + } + ], + "relations": [], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "django_session" + }, + { + "segments": [], + "actions": [], + "fields": [ + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ], + "name": "amount", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "billing_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "customer_id", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "delivering_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": null, + "isPrimaryKey": true, + "isWritable": false, + "prefillFormValue": null, + "isSortable": true, + "validations": [], + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "enumerations": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "isPrimaryKey": false, + "isWritable": true, + "prefillFormValue": null, + "isSortable": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ], + "name": "status", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "billing_address", + "type": "ManyToOne", + "foreignCollection": "address", + "foreignKey": "billing_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "delivering_address", + "type": "ManyToOne", + "foreignCollection": "address", + "foreignKey": "delivering_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "flaskcart_order", + "type": "OneToMany", + "foreignCollection": "cart", + "originKey": "order_id", + "originKeyTarget": "id" + } + ], + "canList": true, + "canCreate": true, + "canUpdate": true, + "canDelete": true, + "canCount": true, + "canSearch": true, + "canChart": true, + "canNativeQuery": true, + "name": "order" + } + ], + "meta": { + "liana": "agent-python", + "liana_version": "1.5.6", + "stack": { + "engine": "python", + "engine_version": "3.11.3" + } + } +} \ No newline at end of file diff --git a/src/_example/django/django_demo/.forestadmin-schema_v2.json b/src/_example/django/django_demo/.forestadmin-schema_v2.json new file mode 100644 index 000000000..e8824d5e7 --- /dev/null +++ b/src/_example/django/django_demo/.forestadmin-schema_v2.json @@ -0,0 +1,3461 @@ +{ + "collections": [ + { + "name": "address", + "fields": [ + { + "name": "city", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "name": "country", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "street", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "name": "street_number", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is shorter than", + "value": 2, + "message": null + } + ] + }, + { + "name": "zip_code", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 5, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "customers", + "type": "ManyToMany", + "foreignCollection": "customer", + "throughCollection": "customers_addresses", + "originKey": "address_id", + "originKeyTarget": "id", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "flaskcustomersaddresses_address", + "type": "OneToMany", + "foreignCollection": "customers_addresses", + "originKey": "address_id", + "originKeyTarget": "id" + }, + { + "name": "flaskorder_billing_address", + "type": "OneToMany", + "foreignCollection": "order", + "originKey": "billing_address_id", + "originKeyTarget": "id" + }, + { + "name": "order_delivering_address_set_delivering_address", + "type": "OneToMany", + "foreignCollection": "order", + "originKey": "delivering_address_id", + "originKeyTarget": "id" + } + ] + }, + { + "name": "app_address", + "fields": [ + { + "name": "city", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "complete_address", + "type": "String", + "filterOperators": [], + "isWritable": false + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "pays", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "prefillFormValue": "France", + "validations": [ + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "postal_code", + "type": [ + { + "codePostal": "String", + "codeCommune": "String", + "nomCommune": "String", + "libelleAcheminement": "String" + } + ], + "filterOperators": [], + "isSortable": false, + "isWritable": false + }, + { + "name": "street", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "zip_code", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "prefillFormValue": "75009", + "validations": [ + { + "type": "is shorter than", + "value": 5, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "billing_orders_billing_address", + "type": "OneToMany", + "foreignCollection": "app_order", + "originKey": "billing_address_id", + "originKeyTarget": "id" + }, + { + "name": "customeraddress_address", + "type": "OneToMany", + "foreignCollection": "app_customeraddress", + "originKey": "address_id", + "originKeyTarget": "id" + }, + { + "name": "customers", + "type": "ManyToMany", + "foreignCollection": "app_customer", + "throughCollection": "app_customeraddress", + "originKey": "address_id", + "originKeyTarget": "id", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "delivering_orders_delivering_address", + "type": "OneToMany", + "foreignCollection": "app_order", + "originKey": "delivering_address_id", + "originKeyTarget": "id" + } + ], + "segments": [ + { + "id": "app_address.highOrderDelivery", + "name": "highOrderDelivery" + } + ], + "canCount": false + }, + { + "name": "app_cart", + "fields": [ + { + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ], + "isWritable": false + }, + { + "name": "customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "IncludesAll", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isSortable": false, + "isWritable": false + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "order_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "extendedcart", + "type": "OneToOne", + "foreignCollection": "app_extendedcart", + "originKey": "cart_id", + "originKeyTarget": "id" + }, + { + "name": "order", + "type": "ManyToOne", + "foreignCollection": "app_order", + "foreignKey": "order_id", + "foreignKeyTarget": "id" + } + ], + "segments": [ + { + "id": "app_cart.No order", + "name": "No order" + } + ] + }, + { + "name": "app_customer", + "fields": [ + { + "name": "TotalSpending", + "type": "Number", + "filterOperators": [], + "isSortable": false, + "isWritable": false + }, + { + "name": "age", + "type": "Number", + "filterOperators": [], + "isSortable": false, + "isWritable": false + }, + { + "name": "avatar", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "birthday_date", + "type": "Dateonly", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ], + "isWritable": false + }, + { + "name": "first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "full_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "GreaterThan", + "In", + "IncludesAll", + "LessThan", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "isSortable": false + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "is_vip", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "prefillFormValue": false + }, + { + "name": "last_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "updated_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ], + "isWritable": false + } + ], + "relations": [ + { + "name": "Customer_blocked_customer+_from_customer", + "type": "OneToMany", + "foreignCollection": "app_customer_blocked_customer", + "originKey": "from_customer_id", + "originKeyTarget": "id" + }, + { + "name": "Customer_blocked_customer+_to_customer", + "type": "OneToMany", + "foreignCollection": "app_customer_blocked_customer", + "originKey": "to_customer_id", + "originKeyTarget": "id" + }, + { + "name": "addresses", + "type": "ManyToMany", + "foreignCollection": "app_address", + "throughCollection": "app_customeraddress", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "block_by_users", + "type": "ManyToMany", + "foreignCollection": "app_customer", + "throughCollection": "app_customer_blocked_customer", + "originKey": "to_customer_id", + "originKeyTarget": "id", + "foreignKey": "from_customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "blocked_customer", + "type": "ManyToMany", + "foreignCollection": "app_customer", + "throughCollection": "app_customer_blocked_customer", + "originKey": "from_customer_id", + "originKeyTarget": "id", + "foreignKey": "to_customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "customeraddress_customer", + "type": "OneToMany", + "foreignCollection": "app_customeraddress", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "orders_customer", + "type": "OneToMany", + "foreignCollection": "app_order", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "smart_billing_addresses", + "type": "ManyToMany", + "foreignCollection": "app_address", + "throughCollection": "app_order", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "billing_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "smart_carts", + "type": "OneToMany", + "foreignCollection": "app_cart", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "smart_delivering_addresses", + "type": "ManyToMany", + "foreignCollection": "app_address", + "throughCollection": "app_order", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "delivering_address_id", + "foreignKeyTarget": "id" + } + ], + "actions": [ + { + "id": "app_customer-0-export json", + "name": "Export json", + "type": "bulk", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + }, + { + "id": "app_customer-1-age operation", + "name": "Age operation", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/1/age operation", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "Loading...", + "type": "String", + "isReadOnly": true, + "defaultValue": "Form is loading", + "value": null, + "description": "", + "enums": null, + "hook": null, + "isRequired": false, + "reference": null, + "widget": null + } + ], + "hooks": { + "load": true, + "change": [ + "changeHook" + ] + } + } + ], + "segments": [ + { + "id": "app_customer.VIP customers", + "name": "VIP customers" + }, + { + "id": "app_customer.with french address", + "name": "with french address" + } + ] + }, + { + "name": "app_customer_blocked_customer", + "fields": [ + { + "name": "from_customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "to_customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "from_customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "from_customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "to_customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "to_customer_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "app_customeraddress", + "fields": [ + { + "name": "address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + } + ], + "relations": [ + { + "name": "address", + "type": "ManyToOne", + "foreignCollection": "app_address", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "app_discountcart", + "fields": [ + { + "name": "discount", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + } + ], + "relations": [ + { + "name": "extendedcart", + "type": "OneToOne", + "foreignCollection": "app_extendedcart", + "originKey": "discount_id", + "originKeyTarget": "id" + } + ] + }, + { + "name": "app_extendedcart", + "fields": [ + { + "name": "cart_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "color", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 20, + "message": null + } + ] + }, + { + "name": "discount_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "cart", + "type": "ManyToOne", + "foreignCollection": "app_cart", + "foreignKey": "cart_id", + "foreignKeyTarget": "id" + }, + { + "name": "discount", + "type": "ManyToOne", + "foreignCollection": "app_discountcart", + "foreignKey": "discount_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "app_order", + "fields": [ + { + "name": "billing_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "cost", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is greater than", + "value": 0, + "message": null + }, + { + "type": "is greater than", + "value": 0, + "message": null + } + ] + }, + { + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ], + "isWritable": false + }, + { + "name": "customer_first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "name": "customer_full_name", + "type": "String", + "filterOperators": [], + "isSortable": false, + "isWritable": false + }, + { + "name": "customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "delivering_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "ordered_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "ordered_date", + "type": "Date", + "filterOperators": [], + "isSortable": false, + "isWritable": false + }, + { + "name": "status", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "enumerations": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ] + }, + { + "name": "test_skillcorner", + "type": "String", + "filterOperators": [], + "isSortable": false, + "isWritable": false + }, + { + "name": "updated_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ], + "isWritable": false + } + ], + "relations": [ + { + "name": "billing_address", + "type": "ManyToOne", + "foreignCollection": "app_address", + "foreignKey": "billing_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "cart", + "type": "OneToOne", + "foreignCollection": "app_cart", + "originKey": "order_id", + "originKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "app_customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "delivering_address", + "type": "ManyToOne", + "foreignCollection": "app_address", + "foreignKey": "delivering_address_id", + "foreignKeyTarget": "id" + } + ], + "actions": [ + { + "id": "app_order-0-export json", + "name": "Export json", + "type": "global", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [ + { + "field": "dummy field", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + }, + { + "field": "customer", + "value": null, + "defaultValue": null, + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": true, + "reference": "app_customer.id", + "type": "Number", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + }, + { + "id": "app_order-1-refund order(s)", + "name": "Refund order(s)", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/1/refund order(s)", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "reason", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + } + ], + "segments": [ + { + "id": "app_order.Delivered order", + "name": "Delivered order" + }, + { + "id": "app_order.Dispatched order", + "name": "Dispatched order" + }, + { + "id": "app_order.Pending order", + "name": "Pending order" + }, + { + "id": "app_order.Rejected order", + "name": "Rejected order" + }, + { + "id": "app_order.Suspicious order", + "name": "Suspicious order" + }, + { + "id": "app_order.newly_created", + "name": "newly_created" + } + ] + }, + { + "name": "auth_group", + "fields": [ + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "Group_permissions+_group", + "type": "OneToMany", + "foreignCollection": "auth_group_permissions", + "originKey": "group_id", + "originKeyTarget": "id" + }, + { + "name": "User_groups+_group", + "type": "OneToMany", + "foreignCollection": "auth_user_groups", + "originKey": "group_id", + "originKeyTarget": "id" + }, + { + "name": "permissions", + "type": "ManyToMany", + "foreignCollection": "auth_permission", + "throughCollection": "auth_group_permissions", + "originKey": "group_id", + "originKeyTarget": "id", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToMany", + "foreignCollection": "auth_user", + "throughCollection": "auth_user_groups", + "originKey": "group_id", + "originKeyTarget": "id", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "auth_group_permissions", + "fields": [ + { + "name": "group_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "permission_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "group", + "type": "ManyToOne", + "foreignCollection": "auth_group", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "permission", + "type": "ManyToOne", + "foreignCollection": "auth_permission", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "auth_permission", + "fields": [ + { + "name": "codename", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ] + }, + { + "name": "content_type_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "Group_permissions+_permission", + "type": "OneToMany", + "foreignCollection": "auth_group_permissions", + "originKey": "permission_id", + "originKeyTarget": "id" + }, + { + "name": "User_user_permissions+_permission", + "type": "OneToMany", + "foreignCollection": "auth_user_user_permissions", + "originKey": "permission_id", + "originKeyTarget": "id" + }, + { + "name": "content_type", + "type": "ManyToOne", + "foreignCollection": "django_content_type", + "foreignKey": "content_type_id", + "foreignKeyTarget": "id" + }, + { + "name": "group", + "type": "ManyToMany", + "foreignCollection": "auth_group", + "throughCollection": "auth_group_permissions", + "originKey": "permission_id", + "originKeyTarget": "id", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToMany", + "foreignCollection": "auth_user", + "throughCollection": "auth_user_user_permissions", + "originKey": "permission_id", + "originKeyTarget": "id", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "auth_user", + "fields": [ + { + "name": "date_joined", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "email", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "name": "first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "is_active", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "prefillFormValue": true + }, + { + "name": "is_staff", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "prefillFormValue": false + }, + { + "name": "is_superuser", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "prefillFormValue": false + }, + { + "name": "last_login", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "last_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + }, + { + "name": "password", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 128, + "message": null + } + ] + }, + { + "name": "username", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "User_groups+_user", + "type": "OneToMany", + "foreignCollection": "auth_user_groups", + "originKey": "user_id", + "originKeyTarget": "id" + }, + { + "name": "User_user_permissions+_user", + "type": "OneToMany", + "foreignCollection": "auth_user_user_permissions", + "originKey": "user_id", + "originKeyTarget": "id" + }, + { + "name": "groups", + "type": "ManyToMany", + "foreignCollection": "auth_group", + "throughCollection": "auth_user_groups", + "originKey": "user_id", + "originKeyTarget": "id", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "logentry_user", + "type": "OneToMany", + "foreignCollection": "django_admin_log", + "originKey": "user_id", + "originKeyTarget": "id" + }, + { + "name": "user_permissions", + "type": "ManyToMany", + "foreignCollection": "auth_permission", + "throughCollection": "auth_user_user_permissions", + "originKey": "user_id", + "originKeyTarget": "id", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "auth_user_groups", + "fields": [ + { + "name": "group_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "user_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "group", + "type": "ManyToOne", + "foreignCollection": "auth_group", + "foreignKey": "group_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToOne", + "foreignCollection": "auth_user", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "auth_user_user_permissions", + "fields": [ + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "permission_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "user_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "permission", + "type": "ManyToOne", + "foreignCollection": "auth_permission", + "foreignKey": "permission_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToOne", + "foreignCollection": "auth_user", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "cart", + "fields": [ + { + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "name": "order_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + } + ], + "relations": [ + { + "name": "order", + "type": "ManyToOne", + "foreignCollection": "order", + "foreignKey": "order_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "customer", + "fields": [ + { + "name": "age", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "avatar", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "birthday_date", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "first_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "name": "id", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true + }, + { + "name": "is_vip", + "type": "Boolean", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "last_name", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "addresses", + "type": "ManyToMany", + "foreignCollection": "address", + "throughCollection": "customers_addresses", + "originKey": "customer_id", + "originKeyTarget": "id", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "flaskcustomersaddresses_customer", + "type": "OneToMany", + "foreignCollection": "customers_addresses", + "originKey": "customer_id", + "originKeyTarget": "id" + }, + { + "name": "flaskorder_customer", + "type": "OneToMany", + "foreignCollection": "order", + "originKey": "customer_id", + "originKeyTarget": "id" + } + ] + }, + { + "name": "customers_addresses", + "fields": [ + { + "name": "address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "customer_id", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + } + ], + "relations": [ + { + "name": "address", + "type": "ManyToOne", + "foreignCollection": "address", + "foreignKey": "address_id", + "foreignKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "django_admin_log", + "fields": [ + { + "name": "action_flag", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "enumerations": [ + "1", + "2", + "3" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "action_time", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "change_message", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "name": "content_type_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "object_id", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ] + }, + { + "name": "object_repr", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 200, + "message": null + } + ] + }, + { + "name": "user_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "content_type", + "type": "ManyToOne", + "foreignCollection": "django_content_type", + "foreignKey": "content_type_id", + "foreignKeyTarget": "id" + }, + { + "name": "user", + "type": "ManyToOne", + "foreignCollection": "auth_user", + "foreignKey": "user_id", + "foreignKeyTarget": "id" + } + ] + }, + { + "name": "django_content_type", + "fields": [ + { + "name": "app_label", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "model", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "logentry_content_type", + "type": "OneToMany", + "foreignCollection": "django_admin_log", + "originKey": "content_type_id", + "originKeyTarget": "id" + }, + { + "name": "permission_content_type", + "type": "OneToMany", + "foreignCollection": "auth_permission", + "originKey": "content_type_id", + "originKeyTarget": "id" + } + ] + }, + { + "name": "django_session", + "fields": [ + { + "name": "expire_date", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "session_data", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "session_key", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], + "isPrimaryKey": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 40, + "message": null + } + ] + } + ] + }, + { + "name": "order", + "fields": [ + { + "name": "amount", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "name": "billing_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "created_at", + "type": "Date", + "filterOperators": [ + "After", + "AfterXHoursAgo", + "Before", + "BeforeXHoursAgo", + "Blank", + "Equal", + "Future", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Past", + "Present", + "PreviousMonth", + "PreviousMonthToDate", + "PreviousQuarter", + "PreviousQuarterToDate", + "PreviousWeek", + "PreviousWeekToDate", + "PreviousXDays", + "PreviousXDaysToDate", + "PreviousYear", + "PreviousYearToDate", + "Today", + "Yesterday" + ] + }, + { + "name": "customer_id", + "type": "String", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "delivering_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ] + }, + { + "name": "id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "isPrimaryKey": true, + "isWritable": false + }, + { + "name": "status", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], + "enumerations": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ] + } + ], + "relations": [ + { + "name": "billing_address", + "type": "ManyToOne", + "foreignCollection": "address", + "foreignKey": "billing_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "customer", + "type": "ManyToOne", + "foreignCollection": "customer", + "foreignKey": "customer_id", + "foreignKeyTarget": "id" + }, + { + "name": "delivering_address", + "type": "ManyToOne", + "foreignCollection": "address", + "foreignKey": "delivering_address_id", + "foreignKeyTarget": "id" + }, + { + "name": "flaskcart_order", + "type": "OneToMany", + "foreignCollection": "cart", + "originKey": "order_id", + "originKeyTarget": "id" + } + ] + } + ], + "meta": { + "liana": "agent-python", + "liana_version": "1.5.6", + "stack": { + "engine": "python", + "engine_version": "3.11.3" + } + } +} \ No newline at end of file diff --git a/src/_example/django/django_demo/app/forest_admin.py b/src/_example/django/django_demo/app/forest_admin.py index c9444d925..04c566f13 100644 --- a/src/_example/django/django_demo/app/forest_admin.py +++ b/src/_example/django/django_demo/app/forest_admin.py @@ -33,8 +33,8 @@ refund_order_action, rejected_order_segment, suspicious_order_segment, + total_order_chart, ) -from demo.forest_admin.smart.order import total_order_chart from forestadmin.datasource_toolkit.interfaces.query.condition_tree.nodes.leaf import ConditionTreeLeaf from forestadmin.django_agent.agent import DjangoAgent @@ -102,7 +102,7 @@ def customize_forest(agent: DjangoAgent): }, ).add_segment("with french address", french_address_segment).add_segment( "VIP customers", - lambda context: ConditionTreeLeaf("is_vip", "equal", True) + lambda context: ConditionTreeLeaf("is_vip", "equal", True), # add actions ).add_action( "Export json", export_json_action_dict diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py index 0c65e8859..161432bbd 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py @@ -16,6 +16,7 @@ from forestadmin.agent_toolkit.services.serializers.json_api import create_json_api_schema from forestadmin.agent_toolkit.utils.context import HttpResponseBuilder from forestadmin.agent_toolkit.utils.forest_schema.emitter import SchemaEmitter +from forestadmin.agent_toolkit.utils.forest_schema.emitter_v2 import SchemaEmitterV2 from forestadmin.agent_toolkit.utils.forest_schema.type import AgentMeta from forestadmin.agent_toolkit.utils.http import ForestHttpApi from forestadmin.datasource_toolkit.datasource_customizer.collection_customizer import CollectionCustomizer @@ -198,6 +199,13 @@ async def _start(self): return ForestLogger.log("debug", "Starting agent") + try: + api_map = await SchemaEmitterV2.get_serialized_schema( + self.options, await self.customizer.get_datasource(), self.meta + ) + except Exception: + ForestLogger.log("exception", "Error generating forest schema V2") + if self.options["skip_schema_update"] is False: try: api_map = await SchemaEmitter.get_serialized_schema( diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py new file mode 100644 index 000000000..b75fdc6ef --- /dev/null +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py @@ -0,0 +1,76 @@ +import json +from hashlib import sha1 +from typing import Any, Dict, List, Union + +from forestadmin.agent_toolkit.forest_logger import ForestLogger +from forestadmin.agent_toolkit.options import Options +from forestadmin.agent_toolkit.utils.forest_schema.generator_collection_v2 import SchemaCollectionGeneratorV2 +from forestadmin.agent_toolkit.utils.forest_schema.type import AgentMeta +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ( + SchemaV2Collection, + template_apply_collection, + template_apply_field, +) +from forestadmin.datasource_toolkit.collections import Collection +from forestadmin.datasource_toolkit.datasource_customizer.datasource_customizer import DatasourceCustomizer +from forestadmin.datasource_toolkit.datasources import Datasource + + +class SchemaEmitterV2: + @classmethod + def serialize(cls, collections: List[SchemaV2Collection], meta: AgentMeta) -> Dict[str, Any]: + """return schema ready to send as api_map format""" + schema_file_hash = sha1(json.dumps({"collections": collections, "meta": meta}).encode("utf-8")).hexdigest() + return { + "collections": collections, + "meta": { + **meta, + "schemaFileHash": schema_file_hash, + }, + } + + @classmethod + async def get_serialized_schema( + cls, options: Options, datasource: Union[Datasource[Collection], DatasourceCustomizer], meta: AgentMeta + ): + schema_path = f'{options["schema_path"].split(".json")[0]}_v2.json' + full_schema_path = f'{options["schema_path"].split(".json")[0]}_full_v2.json' + if not options["is_production"]: + collections_schema = await SchemaEmitterV2.generate(options["prefix"], datasource) + + with open(schema_path, "w", encoding="utf-8") as schema_file: + json.dump({"collections": collections_schema, "meta": meta}, schema_file, indent=4) + + with open(full_schema_path, "w", encoding="utf-8") as schema_file: + full_collections = [] + for collection in collections_schema: + full_collections.append( + { + **template_apply_collection(collection), + "fields": [{**template_apply_field(f)} for f in collection["fields"]], + } + ) + json.dump({"collections": full_collections, "meta": meta}, schema_file, indent=4) + else: + try: + with open(schema_path, "r", encoding="utf-8") as schema_file: + collections_schema = json.load(schema_file)["collections"] + + except Exception: + ForestLogger.log( + "error", + f"Can't read {options['schema_path']}. Providing a schema is mandatory in production. Skipping...", + ) + raise + + return cls.serialize(collections_schema, meta) + + @staticmethod + async def generate( + prefix: str, datasource: Union[Datasource[Collection], DatasourceCustomizer] + ) -> List[SchemaV2Collection]: + """generate schema from datasource""" + collection_schema: List[SchemaV2Collection] = [] + for collection in datasource.collections: + collection_schema.append(await SchemaCollectionGeneratorV2.build(prefix, collection)) # type:ignore + return sorted(collection_schema, key=lambda collection: collection["name"]) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py new file mode 100644 index 000000000..4989afec2 --- /dev/null +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py @@ -0,0 +1,54 @@ +from typing import List + +from forestadmin.agent_toolkit.utils.forest_schema.generator_action import SchemaActionGenerator +from forestadmin.agent_toolkit.utils.forest_schema.generator_field_v2 import SchemaFieldGeneratorV2 +from forestadmin.agent_toolkit.utils.forest_schema.generator_segment import SchemaSegmentGenerator +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ( + SchemaV2Collection, + SchemaV2Field, + SchemaV2Relation, + template_reduce_collection, +) +from forestadmin.datasource_toolkit.collections import Collection +from forestadmin.datasource_toolkit.interfaces.fields import is_column + + +class SchemaCollectionGeneratorV2: + @staticmethod + async def build(prefix: str, collection: Collection) -> SchemaV2Collection: + fields: List[SchemaV2Field] = [] + relations: List[SchemaV2Relation] = [] + for field_name in collection.schema["fields"].keys(): + field_schema = collection.get_field(field_name) + if is_column(field_schema): + fields.append(SchemaFieldGeneratorV2.build_field(collection, field_name)) + else: + relations.append(SchemaFieldGeneratorV2.build_relation(collection, field_name)) + + return template_reduce_collection( + { + "name": collection.name, + "fields": sorted(fields, key=lambda field: field["name"]), + "relations": sorted(relations, key=lambda field: field["name"]), + "actions": sorted( + [ + await SchemaActionGenerator.build(prefix, collection, name) + for name in collection.schema["actions"].keys() + ], + key=lambda action: action["id"], + ), + "segments": sorted( + [await SchemaSegmentGenerator.build(collection, name) for name in collection.schema["segments"]], + key=lambda segment: segment["id"], + ), + # capabilities + "canSearch": collection.schema["searchable"], + "canList": collection.schema["listable"], + "canCreate": collection.schema["creatable"], + "canUpdate": collection.schema["updatable"], + "canDelete": collection.schema["deletable"], + "canCount": collection.schema["countable"], + "canChart": collection.schema["chartable"], + "canNativeQuery": collection.schema["support_native_query"], + } + ) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py new file mode 100644 index 000000000..050c2b53e --- /dev/null +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py @@ -0,0 +1,121 @@ +from typing import cast + +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Field, SchemaV2Relation, template_reduce_field +from forestadmin.agent_toolkit.utils.forest_schema.validation import FrontendValidationUtils +from forestadmin.datasource_toolkit.collections import Collection +from forestadmin.datasource_toolkit.interfaces.fields import ( + Column, + ColumnAlias, + ManyToMany, + ManyToOne, + OneToMany, + OneToOne, + Operator, + PrimitiveType, + RelationAlias, + is_many_to_many, + is_many_to_one, + is_one_to_many, + is_one_to_one, +) + + +class SchemaFieldGeneratorV2: + @classmethod + def _convert_operator(cls, operator: Operator) -> str: + return operator.value.replace("_", " ").title().replace(" ", "") + + @classmethod + def build_field(cls, collection: Collection, field_name: str) -> SchemaV2Field: + field_schema: Column = cast(Column, collection.get_field(field_name)) + return template_reduce_field( + { + "name": field_name, + "type": cls.build_column_type(field_schema["column_type"]), + "filterOperators": sorted( + [ + SchemaFieldGeneratorV2._convert_operator(operator) + for operator in field_schema["filter_operators"] or {} + ] + ), + "enumerations": field_schema["enum_values"], # type:ignore + "isPrimaryKey": field_schema["is_primary_key"], + "isSortable": field_schema["is_sortable"], + "isWritable": not field_schema["is_read_only"], + "prefillFormValue": field_schema["default_value"], + "validations": FrontendValidationUtils.convert_validation_list(field_schema["validations"]), + } + ) + + @staticmethod + def build_column_type(_column_type: ColumnAlias) -> ColumnAlias: + column_type: ColumnAlias + if isinstance(_column_type, PrimitiveType): + column_type = _column_type.value + elif isinstance(_column_type, str): + column_type = _column_type + elif isinstance(_column_type, list): + column_type = [SchemaFieldGeneratorV2.build_column_type(_column_type[0])] + elif isinstance(_column_type, dict): + column_type = { + k: SchemaFieldGeneratorV2.build_column_type(t) for k, t in _column_type.items() + } # type:ignore + + return column_type + + @classmethod + def build_relation(cls, collection: Collection, relation_name: str) -> SchemaV2Relation: + field_schema: RelationAlias = cast(RelationAlias, collection.get_field(relation_name)) + if is_many_to_many(field_schema): + return SchemaFieldGeneratorV2.build_many_to_many_schema(field_schema, relation_name) + elif is_many_to_one(field_schema): + return SchemaFieldGeneratorV2.build_many_to_one_schema(field_schema, relation_name) + elif is_one_to_many(field_schema): + return SchemaFieldGeneratorV2.build_one_to_many_schema(field_schema, relation_name) + elif is_one_to_one(field_schema): + return SchemaFieldGeneratorV2.build_one_to_one_schema(field_schema, relation_name) + else: + raise + + @classmethod + def build_one_to_one_schema(cls, relation: OneToOne, relation_name: str) -> SchemaV2Relation: + return { + "name": relation_name, + "type": "OneToOne", + "foreignCollection": relation["foreign_collection"], + "originKey": relation["origin_key"], + "originKeyTarget": relation["origin_key_target"], + } + + @classmethod + def build_many_to_one_schema(cls, relation: ManyToOne, relation_name: str) -> SchemaV2Relation: + return { + "name": relation_name, + "type": "ManyToOne", + "foreignCollection": relation["foreign_collection"], + "foreignKey": relation["foreign_key"], + "foreignKeyTarget": relation["foreign_key_target"], + } + + @classmethod + def build_one_to_many_schema(cls, relation: OneToMany, relation_name: str) -> SchemaV2Relation: + return { + "name": relation_name, + "type": "OneToMany", + "foreignCollection": relation["foreign_collection"], + "originKey": relation["origin_key"], + "originKeyTarget": relation["origin_key_target"], + } + + @classmethod + def build_many_to_many_schema(cls, relation: ManyToMany, relation_name: str) -> SchemaV2Relation: + return { + "name": relation_name, + "type": "ManyToMany", + "foreignCollection": relation["foreign_collection"], + "throughCollection": relation["through_collection"], + "originKey": relation["origin_key"], + "originKeyTarget": relation["origin_key_target"], + "foreignKey": relation["foreign_key"], + "foreignKeyTarget": relation["foreign_key_target"], + } diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py new file mode 100644 index 000000000..d8d4aedc2 --- /dev/null +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -0,0 +1,114 @@ +from typing import Any, List, Literal + +from forestadmin.agent_toolkit.utils.forest_schema.type import ( + ForestServerAction, + ForestServerSegment, + ServerValidationType, +) +from forestadmin.datasource_toolkit.interfaces.fields import ColumnAlias +from typing_extensions import NotRequired, TypedDict + + +class SchemaV2Relation(TypedDict): + name: str + type: Literal["ManyToMany", "ManyToOne", "OneToOne", "OneToMany"] + foreignCollection: str + throughCollection: NotRequired[str] + foreignKey: NotRequired[str] + foreignKeyTarget: NotRequired[str] + originKey: NotRequired[str] + originKeyTarget: NotRequired[str] + + +class SchemaV2Field(TypedDict): + name: str + type: ColumnAlias + isPrimaryKey: NotRequired[bool] + filterOperators: List[str] + enumerations: NotRequired[List[str]] + + isWritable: NotRequired[bool] + isSortable: NotRequired[bool] + + prefillFormValue: Any + validations: NotRequired[List[ServerValidationType]] + + +class SchemaV2Collection(TypedDict): + name: str + fields: List[SchemaV2Field] # to define + relations: List # to define + + segments: NotRequired[List[ForestServerSegment]] + actions: NotRequired[List[ForestServerAction]] + + canList: NotRequired[bool] + canCreate: NotRequired[bool] + canUpdate: NotRequired[bool] + canDelete: NotRequired[bool] + + canCount: NotRequired[bool] + canChart: NotRequired[bool] + canSearch: NotRequired[bool] + canNativeQuery: NotRequired[bool] + + +# MASKS +SCHEMA_V2_FIELDS_MASK = { + "enumerations": None, + "isPrimaryKey": False, + "isWritable": True, + "prefillFormValue": None, + "isSortable": True, + "isWritable": True, + "validations": [], +} + + +SCHEMA_V2_COLLECTION_MASK = { + "segments": [], + "actions": [], + "fields": [], # I don't kow if we can have a collection without fields + "relations": [], # I don't kow if we can have a collection without relations + "canList": True, + "canCreate": True, + "canUpdate": True, + "canDelete": True, + "canCount": True, + "canSearch": True, + "canChart": True, + "canNativeQuery": True, +} + + +def template_reduce_field(collection: SchemaV2Field) -> SchemaV2Field: + return _reduce_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore + + +def template_reduce_collection(collection: SchemaV2Collection) -> SchemaV2Collection: + return _reduce_from_template(collection, SCHEMA_V2_COLLECTION_MASK) # type:ignore + + +def template_apply_field(collection: SchemaV2Field) -> SchemaV2Field: + return _apply_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore + + +def template_apply_collection(collection: SchemaV2Collection) -> SchemaV2Collection: + return _apply_from_template(collection, SCHEMA_V2_COLLECTION_MASK) # type:ignore + + +def _reduce_from_template(input, mask): + reduced = {} + for key, value in input.items(): + if key not in mask or input[key] != mask[key]: + reduced[key] = value + return reduced + + +def _apply_from_template(input, mask): + full = {} + for key, value in mask.items(): + full[key] = value + for key, value in input.items(): + full[key] = value + return full diff --git a/src/datasource_django/forestadmin/datasource_django/collection.py b/src/datasource_django/forestadmin/datasource_django/collection.py index 8139b8cbc..60cf5eb07 100644 --- a/src/datasource_django/forestadmin/datasource_django/collection.py +++ b/src/datasource_django/forestadmin/datasource_django/collection.py @@ -19,6 +19,15 @@ class DjangoCollection(BaseDjangoCollection): + canList: bool = True + canCreate: bool = True + canUpdate: bool = True + canDelete: bool = True + canChart: bool = True + canCount: bool = True + canNativeQuery: bool = True + canSearch: bool = True + def __init__(self, datasource: Datasource, model: Model): super().__init__(model._meta.db_table, datasource) self._model = model diff --git a/src/datasource_django/forestadmin/datasource_django/interface.py b/src/datasource_django/forestadmin/datasource_django/interface.py index 40684ddb0..7668463c8 100644 --- a/src/datasource_django/forestadmin/datasource_django/interface.py +++ b/src/datasource_django/forestadmin/datasource_django/interface.py @@ -6,7 +6,8 @@ class BaseDjangoCollection(Collection, abc.ABC): - def model(self) -> Model: + @property + def model(self) -> Model: # type: ignore """return model of the collection""" diff --git a/src/datasource_toolkit/forestadmin/datasource_toolkit/collections.py b/src/datasource_toolkit/forestadmin/datasource_toolkit/collections.py index 5457bca17..1d81be8c4 100644 --- a/src/datasource_toolkit/forestadmin/datasource_toolkit/collections.py +++ b/src/datasource_toolkit/forestadmin/datasource_toolkit/collections.py @@ -20,6 +20,16 @@ class CollectionException(DatasourceException): class Collection(CollectionInterface): + # collection capabilities + canList: bool + canCreate: bool + canUpdate: bool + canDelete: bool + canChart: bool + canCount: bool + canNativeQuery: bool + canSearch: bool + def __init__(self, name: str, datasource: Datasource[Self]): super().__init__() self._datasource = datasource @@ -29,8 +39,14 @@ def __init__(self, name: str, datasource: Datasource[Self]): "fields": {}, "searchable": False, "segments": [], - "countable": True, "charts": {}, + "chartable": self.canChart, + "listable": self.canList, + "creatable": self.canCreate, + "updatable": self.canUpdate, + "deletable": self.canDelete, + "countable": self.canCount, + "support_native_query": self.canCount, } def __repr__(self) -> str: @@ -72,7 +88,7 @@ def add_segments(self, segments: List[str]): self.schema["segments"].extend(segments) def enable_search(self): - self.schema["searchable"] = False + self.schema["searchable"] = True async def execute( self, diff --git a/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py b/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py index 5ce7550a5..4f3a61973 100644 --- a/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py +++ b/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py @@ -9,11 +9,21 @@ class CollectionSchema(TypedDict): actions: Dict[str, Action] fields: Dict[str, FieldAlias] - searchable: bool segments: List[str] - countable: bool charts: Dict[str, Callable] + # collection capabilities + # it should be in the form of 'canSomething' but countable & searchable already exists + # because I'm lazy, I'm so sorry for 'chartable' 😅 + listable: bool + creatable: bool + updatable: bool + deletable: bool + chartable: bool + countable: bool + searchable: bool + support_native_query: bool + class Collection(abc.ABC): @abc.abstractproperty From 5670598ed25883a9cd71cdc3ab4204fde3500164 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Tue, 2 Apr 2024 16:45:18 +0200 Subject: [PATCH 03/16] chore: plug new schema v2 to new route --- .../forestadmin/agent_toolkit/agent.py | 13 +++---------- .../agent_toolkit/utils/forest_schema/type_v2.py | 6 ++++++ .../forestadmin/agent_toolkit/utils/http.py | 11 +++++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py index 161432bbd..69af73163 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py @@ -199,23 +199,16 @@ async def _start(self): return ForestLogger.log("debug", "Starting agent") - try: - api_map = await SchemaEmitterV2.get_serialized_schema( - self.options, await self.customizer.get_datasource(), self.meta - ) - except Exception: - ForestLogger.log("exception", "Error generating forest schema V2") - if self.options["skip_schema_update"] is False: try: - api_map = await SchemaEmitter.get_serialized_schema( + api_map = await SchemaEmitterV2.get_serialized_schema( self.options, await self.customizer.get_datasource(), self.meta ) except Exception: - ForestLogger.log("exception", "Error generating forest schema") + ForestLogger.log("exception", "Error generating forest schema V2") try: - await ForestHttpApi.send_schema(self.options, api_map) + await ForestHttpApi.send_schema_v2(self.options, api_map) except Exception: ForestLogger.log("warning", "Cannot send the apimap to Forest. Are you online?") else: diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index d8d4aedc2..a8afdf371 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -1,6 +1,7 @@ from typing import Any, List, Literal from forestadmin.agent_toolkit.utils.forest_schema.type import ( + AgentMeta, ForestServerAction, ForestServerSegment, ServerValidationType, @@ -53,6 +54,11 @@ class SchemaV2Collection(TypedDict): canNativeQuery: NotRequired[bool] +class ForestSchemaV2(TypedDict): + data: List[SchemaV2Collection] + meta: AgentMeta + + # MASKS SCHEMA_V2_FIELDS_MASK = { "enumerations": None, diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py index 51a454f80..0bf98bb68 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py @@ -6,6 +6,7 @@ from forestadmin.agent_toolkit.exceptions import AgentToolkitException from forestadmin.agent_toolkit.forest_logger import ForestLogger from forestadmin.agent_toolkit.utils.forest_schema.type import ForestSchema +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ForestSchemaV2 class ForestHttpApiException(AgentToolkitException): @@ -159,3 +160,13 @@ async def send_schema(cls, options: HttpOptions, schema: ForestSchema) -> bool: ) else: ForestLogger.log("info", "Schema was not updated since last run.") + + @classmethod + async def send_schema_v2(cls, options: HttpOptions, schema: ForestSchemaV2) -> bool: + ForestLogger.log("info", "Schema was updated, sending new version.") + await cls.post( + cls.build_endpoint(options["server_url"], "/forest/v2/apimaps"), + schema, + {"forest-secret-key": options["env_secret"], "content-type": "application/json"}, + options["verify_ssl"], + ) From 38218b862c15932dec369b3d2fb7580f77ada26e Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Tue, 2 Apr 2024 16:47:56 +0200 Subject: [PATCH 04/16] chore: add missing typing --- .../forestadmin/agent_toolkit/utils/forest_schema/type_v2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index a8afdf371..c99d14e8a 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -37,8 +37,8 @@ class SchemaV2Field(TypedDict): class SchemaV2Collection(TypedDict): name: str - fields: List[SchemaV2Field] # to define - relations: List # to define + fields: List[SchemaV2Field] + relations: List[SchemaV2Relation] segments: NotRequired[List[ForestServerSegment]] actions: NotRequired[List[ForestServerAction]] From 97d324194e817c20940aeef360b1d88d58a88eac Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 4 Apr 2024 10:30:43 +0200 Subject: [PATCH 05/16] chore: sort schema keys to compare with backend genereated schema --- .../django_demo/.forestadmin-schema.json | 1062 ++++++++--------- .../forestadmin/agent_toolkit/agent.py | 7 + .../forest_schema/generator_collection.py | 6 +- .../utils/forest_schema/generator_field.py | 14 +- 4 files changed, 540 insertions(+), 549 deletions(-) diff --git a/src/_example/django/django_demo/.forestadmin-schema.json b/src/_example/django/django_demo/.forestadmin-schema.json index 31ae27876..f1136f804 100644 --- a/src/_example/django/django_demo/.forestadmin-schema.json +++ b/src/_example/django/django_demo/.forestadmin-schema.json @@ -2,20 +2,21 @@ "collections": [ { "name": "address", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "city", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -25,7 +26,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -40,9 +40,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "country", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -52,7 +53,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -67,9 +67,12 @@ ] }, { + "field": "customers", + "type": [ + "String" + ], "defaultValue": null, "enums": null, - "field": "customers", "inverseOf": "addresses", "isFilterable": false, "isPrimaryKey": false, @@ -79,15 +82,15 @@ "isVirtual": false, "reference": "customer.id", "relationship": "BelongsToMany", - "type": [ - "String" - ], "validations": [] }, { + "field": "flaskcustomersaddresses_address", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "flaskcustomersaddresses_address", "inverseOf": "address", "isFilterable": false, "isPrimaryKey": false, @@ -97,15 +100,15 @@ "isVirtual": false, "reference": "customers_addresses.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "flaskorder_billing_address", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "flaskorder_billing_address", "inverseOf": "billing_address", "isFilterable": false, "isPrimaryKey": false, @@ -115,15 +118,13 @@ "isVirtual": false, "reference": "order.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -133,13 +134,15 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "order_delivering_address_set_delivering_address", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "order_delivering_address_set_delivering_address", "inverseOf": "delivering_address", "isFilterable": false, "isPrimaryKey": false, @@ -149,15 +152,13 @@ "isVirtual": false, "reference": "order.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "street", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -167,7 +168,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -182,9 +182,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "street_number", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -194,7 +195,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is shorter than", @@ -204,9 +204,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "zip_code", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -216,7 +217,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -234,11 +234,11 @@ }, { "name": "app_address", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], @@ -250,9 +250,12 @@ ], "fields": [ { + "field": "billing_orders_billing_address", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "billing_orders_billing_address", "inverseOf": "billing_address", "isFilterable": false, "isPrimaryKey": false, @@ -262,15 +265,13 @@ "isVirtual": false, "reference": "app_order.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "city", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -280,7 +281,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -295,9 +295,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "complete_address", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -307,13 +308,15 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { + "field": "customeraddress_address", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "customeraddress_address", "inverseOf": "address", "isFilterable": false, "isPrimaryKey": false, @@ -323,15 +326,15 @@ "isVirtual": false, "reference": "app_customeraddress.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "customers", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "customers", "inverseOf": "addresses", "isFilterable": false, "isPrimaryKey": false, @@ -341,15 +344,15 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "delivering_orders_delivering_address", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "delivering_orders_delivering_address", "inverseOf": "delivering_address", "isFilterable": false, "isPrimaryKey": false, @@ -359,15 +362,13 @@ "isVirtual": false, "reference": "app_order.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -377,13 +378,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": "France", - "enums": null, "field": "pays", + "type": "String", + "enums": null, + "defaultValue": "France", "integration": null, "inverseOf": null, "isFilterable": true, @@ -393,7 +394,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is shorter than", @@ -403,18 +403,7 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "postal_code", - "integration": null, - "inverseOf": null, - "isFilterable": false, - "isPrimaryKey": false, - "isReadOnly": true, - "isRequired": false, - "isSortable": false, - "isVirtual": false, - "reference": null, "type": [ { "fields": [ @@ -437,12 +426,24 @@ ] } ], + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "street", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -452,7 +453,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -467,9 +467,10 @@ ] }, { - "defaultValue": "75009", - "enums": null, "field": "zip_code", + "type": "String", + "enums": null, + "defaultValue": "75009", "integration": null, "inverseOf": null, "isFilterable": true, @@ -479,7 +480,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is shorter than", @@ -492,11 +492,11 @@ }, { "name": "app_cart", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], @@ -508,9 +508,10 @@ ], "fields": [ { - "defaultValue": null, - "enums": null, "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -520,13 +521,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "customer_id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -536,13 +537,13 @@ "isSortable": false, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "extendedcart", + "type": "Number", "defaultValue": null, "enums": null, - "field": "extendedcart", "inverseOf": "cart", "isFilterable": true, "isPrimaryKey": false, @@ -552,13 +553,13 @@ "isVirtual": false, "reference": "app_extendedcart.cart_id", "relationship": "HasOne", - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -568,13 +569,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -584,7 +585,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -599,9 +599,10 @@ ] }, { + "field": "order", + "type": "Number", "defaultValue": null, "enums": null, - "field": "order", "inverseOf": "cart", "isFilterable": true, "isPrimaryKey": false, @@ -611,18 +612,17 @@ "isVirtual": false, "reference": "app_order.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] } ] }, { "name": "app_customer", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [ @@ -687,9 +687,12 @@ ], "fields": [ { + "field": "Customer_blocked_customer+_from_customer", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "Customer_blocked_customer+_from_customer", "inverseOf": "from_customer", "isFilterable": false, "isPrimaryKey": false, @@ -699,15 +702,15 @@ "isVirtual": false, "reference": "app_customer_blocked_customer.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "Customer_blocked_customer+_to_customer", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "Customer_blocked_customer+_to_customer", "inverseOf": "to_customer", "isFilterable": false, "isPrimaryKey": false, @@ -717,15 +720,13 @@ "isVirtual": false, "reference": "app_customer_blocked_customer.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "TotalSpending", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -735,13 +736,15 @@ "isSortable": false, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "addresses", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "addresses", "inverseOf": "customers", "isFilterable": false, "isPrimaryKey": false, @@ -751,15 +754,13 @@ "isVirtual": false, "reference": "app_address.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "age", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -769,13 +770,13 @@ "isSortable": false, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "avatar", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -785,13 +786,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "birthday_date", + "type": "Dateonly", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -801,13 +802,15 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Dateonly", "validations": [] }, { + "field": "block_by_users", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "block_by_users", "inverseOf": "blocked_customer", "isFilterable": false, "isPrimaryKey": false, @@ -817,15 +820,15 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "blocked_customer", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "blocked_customer", "inverseOf": "block_by_users", "isFilterable": false, "isPrimaryKey": false, @@ -835,15 +838,13 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -853,13 +854,15 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { + "field": "customeraddress_customer", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "customeraddress_customer", "inverseOf": "customer", "isFilterable": false, "isPrimaryKey": false, @@ -869,15 +872,13 @@ "isVirtual": false, "reference": "app_customeraddress.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "first_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -887,7 +888,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -901,10 +901,11 @@ } ] }, - { - "defaultValue": null, - "enums": null, + { "field": "full_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -914,13 +915,13 @@ "isSortable": false, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -930,13 +931,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": false, - "enums": null, "field": "is_vip", + "type": "Boolean", + "enums": null, + "defaultValue": false, "integration": null, "inverseOf": null, "isFilterable": true, @@ -946,13 +947,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Boolean", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "last_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -962,7 +963,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -977,9 +977,12 @@ ] }, { + "field": "orders_customer", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "orders_customer", "inverseOf": "customer", "isFilterable": false, "isPrimaryKey": false, @@ -989,15 +992,15 @@ "isVirtual": false, "reference": "app_order.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "smart_billing_addresses", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "smart_billing_addresses", "inverseOf": null, "isFilterable": false, "isPrimaryKey": false, @@ -1007,15 +1010,15 @@ "isVirtual": false, "reference": "app_address.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "smart_carts", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "smart_carts", "inverseOf": null, "isFilterable": false, "isPrimaryKey": false, @@ -1025,15 +1028,15 @@ "isVirtual": false, "reference": "app_cart.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "smart_delivering_addresses", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "smart_delivering_addresses", "inverseOf": null, "isFilterable": false, "isPrimaryKey": false, @@ -1043,15 +1046,13 @@ "isVirtual": false, "reference": "app_address.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "updated_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1061,27 +1062,27 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] } ] }, { "name": "app_customer_blocked_customer", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "from_customer", + "type": "Number", "defaultValue": null, "enums": null, - "field": "from_customer", "inverseOf": "Customer_blocked_customer+_from_customer", "isFilterable": true, "isPrimaryKey": false, @@ -1091,7 +1092,6 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1101,9 +1101,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1113,13 +1114,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "to_customer", + "type": "Number", "defaultValue": null, "enums": null, - "field": "to_customer", "inverseOf": "Customer_blocked_customer+_to_customer", "isFilterable": true, "isPrimaryKey": false, @@ -1129,7 +1130,6 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1142,20 +1142,21 @@ }, { "name": "app_customeraddress", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "address", + "type": "Number", "defaultValue": null, "enums": null, - "field": "address", "inverseOf": "customeraddress_address", "isFilterable": true, "isPrimaryKey": false, @@ -1165,7 +1166,6 @@ "isVirtual": false, "reference": "app_address.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1175,9 +1175,10 @@ ] }, { + "field": "customer", + "type": "Number", "defaultValue": null, "enums": null, - "field": "customer", "inverseOf": "customeraddress_customer", "isFilterable": true, "isPrimaryKey": false, @@ -1187,7 +1188,6 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1197,9 +1197,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1209,27 +1210,27 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] } ] }, { "name": "app_discountcart", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "discount", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1239,7 +1240,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [ { "type": "is present", @@ -1249,9 +1249,10 @@ ] }, { + "field": "extendedcart", + "type": "Number", "defaultValue": null, "enums": null, - "field": "extendedcart", "inverseOf": "discount", "isFilterable": true, "isPrimaryKey": false, @@ -1261,13 +1262,13 @@ "isVirtual": false, "reference": "app_extendedcart.discount_id", "relationship": "HasOne", - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1277,27 +1278,27 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] } ] }, { "name": "app_extendedcart", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "cart", + "type": "Number", "defaultValue": null, "enums": null, - "field": "cart", "inverseOf": "extendedcart", "isFilterable": true, "isPrimaryKey": false, @@ -1307,7 +1308,6 @@ "isVirtual": false, "reference": "app_cart.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1317,9 +1317,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "cart_id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1329,7 +1330,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [ { "type": "is present", @@ -1339,9 +1339,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "color", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1351,7 +1352,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -1366,9 +1366,10 @@ ] }, { + "field": "discount", + "type": "Number", "defaultValue": null, "enums": null, - "field": "discount", "inverseOf": "extendedcart", "isFilterable": true, "isPrimaryKey": false, @@ -1378,18 +1379,17 @@ "isVirtual": false, "reference": "app_discountcart.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] } ] }, { "name": "app_order", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [ @@ -1500,9 +1500,10 @@ ], "fields": [ { + "field": "billing_address", + "type": "Number", "defaultValue": null, "enums": null, - "field": "billing_address", "inverseOf": "billing_orders_billing_address", "isFilterable": true, "isPrimaryKey": false, @@ -1512,7 +1513,6 @@ "isVirtual": false, "reference": "app_address.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1522,9 +1522,10 @@ ] }, { + "field": "cart", + "type": "Number", "defaultValue": null, "enums": null, - "field": "cart", "inverseOf": "order", "isFilterable": true, "isPrimaryKey": false, @@ -1534,13 +1535,13 @@ "isVirtual": false, "reference": "app_cart.order_id", "relationship": "HasOne", - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "cost", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1550,7 +1551,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [ { "type": "is present", @@ -1570,9 +1570,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1582,13 +1583,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { + "field": "customer", + "type": "Number", "defaultValue": null, "enums": null, - "field": "customer", "inverseOf": "orders_customer", "isFilterable": true, "isPrimaryKey": false, @@ -1598,13 +1599,13 @@ "isVirtual": false, "reference": "app_customer.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "customer_first_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1614,13 +1615,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "customer_full_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -1630,13 +1631,13 @@ "isSortable": false, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { + "field": "delivering_address", + "type": "Number", "defaultValue": null, "enums": null, - "field": "delivering_address", "inverseOf": "delivering_orders_delivering_address", "isFilterable": true, "isPrimaryKey": false, @@ -1646,7 +1647,6 @@ "isVirtual": false, "reference": "app_address.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1656,9 +1656,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1668,13 +1669,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "ordered_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1684,13 +1685,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "ordered_date", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -1700,18 +1701,18 @@ "isSortable": false, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, + "field": "status", + "type": "Enum", "enums": [ "PENDING", "DISPATCHED", "DELIVERED", "REJECTED" ], - "field": "status", + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1721,7 +1722,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Enum", "validations": [ { "type": "is present", @@ -1736,25 +1736,10 @@ ] }, { - "defaultValue": null, + "field": "updated_at", + "type": "Date", "enums": null, - "field": "test_skillcorner", - "integration": null, - "inverseOf": null, - "isFilterable": false, - "isPrimaryKey": false, - "isReadOnly": true, - "isRequired": false, - "isSortable": false, - "isVirtual": false, - "reference": null, - "type": "String", - "validations": [] - }, - { "defaultValue": null, - "enums": null, - "field": "updated_at", "integration": null, "inverseOf": null, "isFilterable": true, @@ -1764,27 +1749,29 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] } ] }, { "name": "auth_group", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "Group_permissions+_group", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "Group_permissions+_group", "inverseOf": "group", "isFilterable": false, "isPrimaryKey": false, @@ -1794,15 +1781,15 @@ "isVirtual": false, "reference": "auth_group_permissions.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "User_groups+_group", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "User_groups+_group", "inverseOf": "group", "isFilterable": false, "isPrimaryKey": false, @@ -1812,15 +1799,13 @@ "isVirtual": false, "reference": "auth_user_groups.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1830,13 +1815,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1846,7 +1831,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -1861,9 +1845,12 @@ ] }, { + "field": "permissions", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "permissions", "inverseOf": "group", "isFilterable": false, "isPrimaryKey": false, @@ -1873,15 +1860,15 @@ "isVirtual": false, "reference": "auth_permission.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "user", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "user", "inverseOf": "groups", "isFilterable": false, "isPrimaryKey": false, @@ -1891,29 +1878,27 @@ "isVirtual": false, "reference": "auth_user.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] } ] }, { "name": "auth_group_permissions", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "group", + "type": "Number", "defaultValue": null, "enums": null, - "field": "group", "inverseOf": "Group_permissions+_group", "isFilterable": true, "isPrimaryKey": false, @@ -1923,7 +1908,6 @@ "isVirtual": false, "reference": "auth_group.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1933,9 +1917,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -1945,13 +1930,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "permission", + "type": "Number", "defaultValue": null, "enums": null, - "field": "permission", "inverseOf": "Group_permissions+_permission", "isFilterable": true, "isPrimaryKey": false, @@ -1961,7 +1946,6 @@ "isVirtual": false, "reference": "auth_permission.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -1974,20 +1958,23 @@ }, { "name": "auth_permission", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "Group_permissions+_permission", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "Group_permissions+_permission", "inverseOf": "permission", "isFilterable": false, "isPrimaryKey": false, @@ -1997,15 +1984,15 @@ "isVirtual": false, "reference": "auth_group_permissions.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "User_user_permissions+_permission", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "User_user_permissions+_permission", "inverseOf": "permission", "isFilterable": false, "isPrimaryKey": false, @@ -2015,15 +2002,13 @@ "isVirtual": false, "reference": "auth_user_user_permissions.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "codename", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2033,7 +2018,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2048,9 +2032,10 @@ ] }, { + "field": "content_type", + "type": "Number", "defaultValue": null, "enums": null, - "field": "content_type", "inverseOf": "permission_content_type", "isFilterable": true, "isPrimaryKey": false, @@ -2060,7 +2045,6 @@ "isVirtual": false, "reference": "django_content_type.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -2070,9 +2054,12 @@ ] }, { + "field": "group", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "group", "inverseOf": "permissions", "isFilterable": false, "isPrimaryKey": false, @@ -2082,15 +2069,13 @@ "isVirtual": false, "reference": "auth_group.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2100,13 +2085,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2116,7 +2101,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2131,9 +2115,12 @@ ] }, { + "field": "user", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "user", "inverseOf": "user_permissions", "isFilterable": false, "isPrimaryKey": false, @@ -2143,29 +2130,29 @@ "isVirtual": false, "reference": "auth_user.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] } ] }, { "name": "auth_user", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "User_groups+_user", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "User_groups+_user", "inverseOf": "user", "isFilterable": false, "isPrimaryKey": false, @@ -2175,15 +2162,15 @@ "isVirtual": false, "reference": "auth_user_groups.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { + "field": "User_user_permissions+_user", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "User_user_permissions+_user", "inverseOf": "user", "isFilterable": false, "isPrimaryKey": false, @@ -2193,15 +2180,13 @@ "isVirtual": false, "reference": "auth_user_user_permissions.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "date_joined", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2211,13 +2196,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "email", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2227,7 +2212,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is shorter than", @@ -2237,9 +2221,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "first_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2249,7 +2234,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is shorter than", @@ -2259,9 +2243,12 @@ ] }, { + "field": "groups", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "groups", "inverseOf": "user", "isFilterable": false, "isPrimaryKey": false, @@ -2271,15 +2258,13 @@ "isVirtual": false, "reference": "auth_group.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2289,13 +2274,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": true, - "enums": null, "field": "is_active", + "type": "Boolean", + "enums": null, + "defaultValue": true, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2305,13 +2290,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Boolean", "validations": [] }, { - "defaultValue": false, - "enums": null, "field": "is_staff", + "type": "Boolean", + "enums": null, + "defaultValue": false, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2321,13 +2306,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Boolean", "validations": [] }, { - "defaultValue": false, - "enums": null, "field": "is_superuser", + "type": "Boolean", + "enums": null, + "defaultValue": false, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2337,13 +2322,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Boolean", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "last_login", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2353,13 +2338,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "last_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2369,7 +2354,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is shorter than", @@ -2379,9 +2363,12 @@ ] }, { + "field": "logentry_user", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "logentry_user", "inverseOf": "user", "isFilterable": false, "isPrimaryKey": false, @@ -2391,15 +2378,13 @@ "isVirtual": false, "reference": "django_admin_log.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "password", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2409,7 +2394,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2424,9 +2408,12 @@ ] }, { + "field": "user_permissions", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "user_permissions", "inverseOf": "user", "isFilterable": false, "isPrimaryKey": false, @@ -2436,15 +2423,13 @@ "isVirtual": false, "reference": "auth_permission.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "username", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2454,7 +2439,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2472,20 +2456,21 @@ }, { "name": "auth_user_groups", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "group", + "type": "Number", "defaultValue": null, "enums": null, - "field": "group", "inverseOf": "User_groups+_group", "isFilterable": true, "isPrimaryKey": false, @@ -2495,7 +2480,6 @@ "isVirtual": false, "reference": "auth_group.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -2505,9 +2489,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2517,13 +2502,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "user", + "type": "Number", "defaultValue": null, "enums": null, - "field": "user", "inverseOf": "User_groups+_user", "isFilterable": true, "isPrimaryKey": false, @@ -2533,7 +2518,6 @@ "isVirtual": false, "reference": "auth_user.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -2546,20 +2530,21 @@ }, { "name": "auth_user_user_permissions", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2569,13 +2554,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "permission", + "type": "Number", "defaultValue": null, "enums": null, - "field": "permission", "inverseOf": "User_user_permissions+_permission", "isFilterable": true, "isPrimaryKey": false, @@ -2585,7 +2570,6 @@ "isVirtual": false, "reference": "auth_permission.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -2595,9 +2579,10 @@ ] }, { + "field": "user", + "type": "Number", "defaultValue": null, "enums": null, - "field": "user", "inverseOf": "User_user_permissions+_user", "isFilterable": true, "isPrimaryKey": false, @@ -2607,7 +2592,6 @@ "isVirtual": false, "reference": "auth_user.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -2620,20 +2604,21 @@ }, { "name": "cart", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2643,13 +2628,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2659,13 +2644,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2675,7 +2660,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2690,9 +2674,10 @@ ] }, { + "field": "order", + "type": "Number", "defaultValue": null, "enums": null, - "field": "order", "inverseOf": "flaskcart_order", "isFilterable": true, "isPrimaryKey": false, @@ -2702,27 +2687,29 @@ "isVirtual": false, "reference": "order.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] } ] }, { "name": "customer", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "addresses", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "addresses", "inverseOf": "customers", "isFilterable": false, "isPrimaryKey": false, @@ -2732,15 +2719,13 @@ "isVirtual": false, "reference": "address.id", "relationship": "BelongsToMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "age", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2750,13 +2735,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "avatar", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -2766,13 +2751,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "birthday_date", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2782,13 +2767,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "first_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2798,7 +2783,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2813,9 +2797,12 @@ ] }, { + "field": "flaskcustomersaddresses_customer", + "type": [ + "String" + ], "defaultValue": null, "enums": null, - "field": "flaskcustomersaddresses_customer", "inverseOf": "customer", "isFilterable": false, "isPrimaryKey": false, @@ -2825,15 +2812,15 @@ "isVirtual": false, "reference": "customers_addresses.id", "relationship": "HasMany", - "type": [ - "String" - ], "validations": [] }, { + "field": "flaskorder_customer", + "type": [ + "String" + ], "defaultValue": null, "enums": null, - "field": "flaskorder_customer", "inverseOf": "customer", "isFilterable": false, "isPrimaryKey": false, @@ -2843,15 +2830,13 @@ "isVirtual": false, "reference": "order.id", "relationship": "HasMany", - "type": [ - "String" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": false, @@ -2861,13 +2846,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "is_vip", + "type": "Boolean", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2877,13 +2862,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Boolean", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "last_name", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2893,7 +2878,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -2911,20 +2895,21 @@ }, { "name": "customers_addresses", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { + "field": "address", + "type": "Number", "defaultValue": null, "enums": null, - "field": "address", "inverseOf": "flaskcustomersaddresses_address", "isFilterable": true, "isPrimaryKey": false, @@ -2934,7 +2919,6 @@ "isVirtual": false, "reference": "address.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -2944,9 +2928,10 @@ ] }, { + "field": "customer", + "type": "String", "defaultValue": null, "enums": null, - "field": "customer", "inverseOf": "flaskcustomersaddresses_customer", "isFilterable": true, "isPrimaryKey": false, @@ -2956,13 +2941,13 @@ "isVirtual": false, "reference": "customer.id", "relationship": "BelongsTo", - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -2972,31 +2957,31 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] } ] }, { "name": "django_admin_log", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, + "field": "action_flag", + "type": "Enum", "enums": [ "1", "2", "3" ], - "field": "action_flag", + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3006,7 +2991,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Enum", "validations": [ { "type": "is present", @@ -3016,9 +3000,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "action_time", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3028,13 +3013,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "change_message", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3044,13 +3029,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { + "field": "content_type", + "type": "Number", "defaultValue": null, "enums": null, - "field": "content_type", "inverseOf": "logentry_content_type", "isFilterable": true, "isPrimaryKey": false, @@ -3060,13 +3045,13 @@ "isVirtual": false, "reference": "django_content_type.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3076,13 +3061,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "object_id", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3092,13 +3077,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "object_repr", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3108,7 +3093,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -3123,9 +3107,10 @@ ] }, { + "field": "user", + "type": "Number", "defaultValue": null, "enums": null, - "field": "user", "inverseOf": "logentry_user", "isFilterable": true, "isPrimaryKey": false, @@ -3135,7 +3120,6 @@ "isVirtual": false, "reference": "auth_user.id", "relationship": "BelongsTo", - "type": "Number", "validations": [ { "type": "is present", @@ -3148,20 +3132,21 @@ }, { "name": "django_content_type", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "app_label", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3171,7 +3156,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -3186,9 +3170,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3198,13 +3183,15 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { + "field": "logentry_content_type", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "logentry_content_type", "inverseOf": "content_type", "isFilterable": false, "isPrimaryKey": false, @@ -3214,15 +3201,13 @@ "isVirtual": false, "reference": "django_admin_log.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "model", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3232,7 +3217,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -3247,9 +3231,12 @@ ] }, { + "field": "permission_content_type", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "permission_content_type", "inverseOf": "content_type", "isFilterable": false, "isPrimaryKey": false, @@ -3259,29 +3246,27 @@ "isVirtual": false, "reference": "auth_permission.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] } ] }, { "name": "django_session", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "expire_date", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3291,7 +3276,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [ { "type": "is present", @@ -3301,9 +3285,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "session_data", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3313,7 +3298,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -3323,9 +3307,10 @@ ] }, { - "defaultValue": null, - "enums": null, "field": "session_key", + "type": "String", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3335,7 +3320,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "String", "validations": [ { "type": "is present", @@ -3353,20 +3337,21 @@ }, { "name": "order", - "isVirtual": false, "icon": null, - "isReadOnly": false, "integration": null, + "isReadOnly": false, "isSearchable": true, + "isVirtual": false, "onlyForRelationships": false, "paginationType": "page", "actions": [], "segments": [], "fields": [ { - "defaultValue": null, - "enums": null, "field": "amount", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3376,7 +3361,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [ { "type": "is present", @@ -3386,9 +3370,10 @@ ] }, { + "field": "billing_address", + "type": "Number", "defaultValue": null, "enums": null, - "field": "billing_address", "inverseOf": "flaskorder_billing_address", "isFilterable": true, "isPrimaryKey": false, @@ -3398,13 +3383,13 @@ "isVirtual": false, "reference": "address.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3414,13 +3399,13 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Date", "validations": [] }, { + "field": "customer", + "type": "String", "defaultValue": null, "enums": null, - "field": "customer", "inverseOf": "flaskorder_customer", "isFilterable": true, "isPrimaryKey": false, @@ -3430,13 +3415,13 @@ "isVirtual": false, "reference": "customer.id", "relationship": "BelongsTo", - "type": "String", "validations": [] }, { + "field": "delivering_address", + "type": "Number", "defaultValue": null, "enums": null, - "field": "delivering_address", "inverseOf": "order_delivering_address_set_delivering_address", "isFilterable": true, "isPrimaryKey": false, @@ -3446,13 +3431,15 @@ "isVirtual": false, "reference": "address.id", "relationship": "BelongsTo", - "type": "Number", "validations": [] }, { + "field": "flaskcart_order", + "type": [ + "Number" + ], "defaultValue": null, "enums": null, - "field": "flaskcart_order", "inverseOf": "order", "isFilterable": false, "isPrimaryKey": false, @@ -3462,15 +3449,13 @@ "isVirtual": false, "reference": "cart.id", "relationship": "HasMany", - "type": [ - "Number" - ], "validations": [] }, { - "defaultValue": null, - "enums": null, "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3480,18 +3465,18 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Number", "validations": [] }, { - "defaultValue": null, + "field": "status", + "type": "Enum", "enums": [ "PENDING", "DISPATCHED", "DELIVERED", "REJECTED" ], - "field": "status", + "defaultValue": null, "integration": null, "inverseOf": null, "isFilterable": true, @@ -3501,7 +3486,6 @@ "isSortable": true, "isVirtual": false, "reference": null, - "type": "Enum", "validations": [ { "type": "is present", diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py index 69af73163..13b706402 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py @@ -200,6 +200,13 @@ async def _start(self): ForestLogger.log("debug", "Starting agent") if self.options["skip_schema_update"] is False: + try: + api_map = await SchemaEmitter.get_serialized_schema( + self.options, await self.customizer.get_datasource(), self.meta + ) + except Exception: + ForestLogger.log("exception", "Error generating forest schema V1") + try: api_map = await SchemaEmitterV2.get_serialized_schema( self.options, await self.customizer.get_datasource(), self.meta diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection.py index 9083fff4f..24d920c49 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection.py @@ -25,16 +25,16 @@ async def build(prefix: str, collection: Union[Collection, CollectionCustomizer] return { "name": collection.name, - "isVirtual": False, "icon": None, + "integration": None, "isReadOnly": all( [f["type"] == FieldType.COLUMN and f["is_read_only"] for f in collection.schema["fields"].values()] ), - "integration": None, "isSearchable": collection.schema["searchable"], + "isVirtual": False, "onlyForRelationships": False, "paginationType": "page", - "searchField": None, + # "searchField": None, "actions": sorted( [ await SchemaActionGenerator.build(prefix, collection, name) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field.py index 12f6228f4..b826b8981 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field.py @@ -50,7 +50,7 @@ def build(cls, collection: Union[Collection, CollectionCustomizer], field_name: schema = cls.build_relation_schema(collection, field_name, field_schema) else: raise - return cast(ForestServerField, dict(sorted(schema.items()))) + return schema @staticmethod def build_column_type(_column_type: ColumnAlias) -> ColumnAlias: @@ -81,21 +81,21 @@ def build_column_schema(cls, name: str, collection: Collection) -> ForestServerF res = { "field": name, "type": cls.build_column_type(column["column_type"]), - "validations": FrontendValidationUtils.convert_validation_list(column["validations"]), - "defaultValue": column["default_value"], "enums": column["enum_values"], + "defaultValue": column["default_value"], "integration": None, "inverseOf": None, "isFilterable": FrontendFilterableUtils.is_filterable(column["column_type"], column["filter_operators"]), "isPrimaryKey": bool(column["is_primary_key"]), - "isSortable": bool(column["is_sortable"]), # When a column is a foreign key, it is readonly. # This may sound counter-intuitive: it is so that the user don't have two fields which # allow updating the same foreign key in the detail-view form (fk + many to one) "isReadOnly": is_foreign_key or bool(column["is_read_only"]), "isRequired": any([v["operator"] == Operator.PRESENT for v in validations]), + "isSortable": bool(column["is_sortable"]), "isVirtual": False, "reference": None, + "validations": FrontendValidationUtils.convert_validation_list(column["validations"]), } return ForestServerField(**res) @@ -123,11 +123,11 @@ def build_one_to_one_schema( "defaultValue": None, "isFilterable": cls.is_foreign_collection_filterable(foreign_collection), "isPrimaryKey": False, - "isRequired": False, "isReadOnly": bool(key_field["is_read_only"]), + "isRequired": False, "isSortable": bool(target_field["is_sortable"]), - "validations": [], "reference": f"{foreign_collection.name}.{relation['origin_key']}", + "validations": [], } @classmethod @@ -202,4 +202,4 @@ def build_relation_schema( res = cls.build_many_to_one_schema( cast(ManyToOne, field_schema), collection, foreign_collection, relation_schema ) - return res + return {"field": res["field"], "type": res["type"], **dict(sorted(res.items()))} From 0ce3b1d4018426e48b8574b7198864f6018cef2a Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 4 Apr 2024 16:30:35 +0200 Subject: [PATCH 06/16] chore: improve the way to reduce schema --- .../forestadmin/agent_toolkit/agent.py | 10 ++-- .../utils/forest_schema/emitter_v2.py | 20 ++++--- .../forest_schema/generator_collection_v2.py | 59 ++++++++----------- .../utils/forest_schema/generator_field_v2.py | 42 ++++++------- .../utils/forest_schema/type_v2.py | 13 ++-- 5 files changed, 70 insertions(+), 74 deletions(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py index 13b706402..f9c025952 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py @@ -204,20 +204,18 @@ async def _start(self): api_map = await SchemaEmitter.get_serialized_schema( self.options, await self.customizer.get_datasource(), self.meta ) + await ForestHttpApi.send_schema(self.options, api_map) except Exception: - ForestLogger.log("exception", "Error generating forest schema V1") + ForestLogger.log("exception", "Error generating/sending forest schema V1") try: api_map = await SchemaEmitterV2.get_serialized_schema( self.options, await self.customizer.get_datasource(), self.meta ) - except Exception: - ForestLogger.log("exception", "Error generating forest schema V2") - - try: await ForestHttpApi.send_schema_v2(self.options, api_map) except Exception: - ForestLogger.log("warning", "Cannot send the apimap to Forest. Are you online?") + ForestLogger.log("exception", "Error generating/sending forest schema V2") + else: ForestLogger.log("warning", 'Schema update was skipped (caused by options["skip_schema_update"]=True)') diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py index b75fdc6ef..152cc7ca1 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py @@ -8,8 +8,8 @@ from forestadmin.agent_toolkit.utils.forest_schema.type import AgentMeta from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ( SchemaV2Collection, - template_apply_collection, - template_apply_field, + template_reduce_collection, + template_reduce_field, ) from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.datasource_customizer.datasource_customizer import DatasourceCustomizer @@ -38,20 +38,21 @@ async def get_serialized_schema( if not options["is_production"]: collections_schema = await SchemaEmitterV2.generate(options["prefix"], datasource) - with open(schema_path, "w", encoding="utf-8") as schema_file: + with open(full_schema_path, "w", encoding="utf-8") as schema_file: json.dump({"collections": collections_schema, "meta": meta}, schema_file, indent=4) - with open(full_schema_path, "w", encoding="utf-8") as schema_file: - full_collections = [] + with open(schema_path, "w", encoding="utf-8") as schema_file: + reduced_collections = [] for collection in collections_schema: - full_collections.append( + reduced_collections.append( { - **template_apply_collection(collection), - "fields": [{**template_apply_field(f)} for f in collection["fields"]], + **template_reduce_collection(collection), + "fields": [{**template_reduce_field(f)} for f in collection["fields"]], } ) - json.dump({"collections": full_collections, "meta": meta}, schema_file, indent=4) + json.dump({"collections": reduced_collections, "meta": meta}, schema_file, indent=4) else: + 1 / 0 try: with open(schema_path, "r", encoding="utf-8") as schema_file: collections_schema = json.load(schema_file)["collections"] @@ -63,6 +64,7 @@ async def get_serialized_schema( ) raise + # return cls.serialize(reduced_collections, meta) return cls.serialize(collections_schema, meta) @staticmethod diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py index 4989afec2..378786f16 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py @@ -3,12 +3,7 @@ from forestadmin.agent_toolkit.utils.forest_schema.generator_action import SchemaActionGenerator from forestadmin.agent_toolkit.utils.forest_schema.generator_field_v2 import SchemaFieldGeneratorV2 from forestadmin.agent_toolkit.utils.forest_schema.generator_segment import SchemaSegmentGenerator -from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ( - SchemaV2Collection, - SchemaV2Field, - SchemaV2Relation, - template_reduce_collection, -) +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Collection, SchemaV2Field, SchemaV2Relation from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.interfaces.fields import is_column @@ -25,30 +20,28 @@ async def build(prefix: str, collection: Collection) -> SchemaV2Collection: else: relations.append(SchemaFieldGeneratorV2.build_relation(collection, field_name)) - return template_reduce_collection( - { - "name": collection.name, - "fields": sorted(fields, key=lambda field: field["name"]), - "relations": sorted(relations, key=lambda field: field["name"]), - "actions": sorted( - [ - await SchemaActionGenerator.build(prefix, collection, name) - for name in collection.schema["actions"].keys() - ], - key=lambda action: action["id"], - ), - "segments": sorted( - [await SchemaSegmentGenerator.build(collection, name) for name in collection.schema["segments"]], - key=lambda segment: segment["id"], - ), - # capabilities - "canSearch": collection.schema["searchable"], - "canList": collection.schema["listable"], - "canCreate": collection.schema["creatable"], - "canUpdate": collection.schema["updatable"], - "canDelete": collection.schema["deletable"], - "canCount": collection.schema["countable"], - "canChart": collection.schema["chartable"], - "canNativeQuery": collection.schema["support_native_query"], - } - ) + return { + "name": collection.name, + "fields": sorted(fields, key=lambda field: field["name"]), + "relations": sorted(relations, key=lambda field: field["name"]), + "actions": sorted( + [ + await SchemaActionGenerator.build(prefix, collection, name) + for name in collection.schema["actions"].keys() + ], + key=lambda action: action["id"], + ), + "segments": sorted( + [await SchemaSegmentGenerator.build(collection, name) for name in collection.schema["segments"]], + key=lambda segment: segment["id"], + ), + # capabilities + "canSearch": collection.schema["searchable"], + "canList": collection.schema["listable"], + "canCreate": collection.schema["creatable"], + "canUpdate": collection.schema["updatable"], + "canDelete": collection.schema["deletable"], + "canCount": collection.schema["countable"], + "canChart": collection.schema["chartable"], + "canNativeQuery": collection.schema["support_native_query"], + } diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py index 050c2b53e..1a6c9f3ec 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py @@ -1,6 +1,6 @@ from typing import cast -from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Field, SchemaV2Relation, template_reduce_field +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Field, SchemaV2Relation from forestadmin.agent_toolkit.utils.forest_schema.validation import FrontendValidationUtils from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.interfaces.fields import ( @@ -28,24 +28,22 @@ def _convert_operator(cls, operator: Operator) -> str: @classmethod def build_field(cls, collection: Collection, field_name: str) -> SchemaV2Field: field_schema: Column = cast(Column, collection.get_field(field_name)) - return template_reduce_field( - { - "name": field_name, - "type": cls.build_column_type(field_schema["column_type"]), - "filterOperators": sorted( - [ - SchemaFieldGeneratorV2._convert_operator(operator) - for operator in field_schema["filter_operators"] or {} - ] - ), - "enumerations": field_schema["enum_values"], # type:ignore - "isPrimaryKey": field_schema["is_primary_key"], - "isSortable": field_schema["is_sortable"], - "isWritable": not field_schema["is_read_only"], - "prefillFormValue": field_schema["default_value"], - "validations": FrontendValidationUtils.convert_validation_list(field_schema["validations"]), - } - ) + return { + "name": field_name, + "type": cls.build_column_type(field_schema["column_type"]), + "filterOperators": sorted( + [ + SchemaFieldGeneratorV2._convert_operator(operator) + for operator in field_schema["filter_operators"] or {} + ] + ), + "enumerations": field_schema["enum_values"], # type:ignore + "isPrimaryKey": field_schema["is_primary_key"], + "isSortable": field_schema["is_sortable"], + "isWritable": not field_schema["is_read_only"], + "prefillFormValue": field_schema["default_value"], + "validations": FrontendValidationUtils.convert_validation_list(field_schema["validations"]), + } @staticmethod def build_column_type(_column_type: ColumnAlias) -> ColumnAlias: @@ -56,9 +54,11 @@ def build_column_type(_column_type: ColumnAlias) -> ColumnAlias: column_type = _column_type elif isinstance(_column_type, list): column_type = [SchemaFieldGeneratorV2.build_column_type(_column_type[0])] - elif isinstance(_column_type, dict): + else: column_type = { - k: SchemaFieldGeneratorV2.build_column_type(t) for k, t in _column_type.items() + "fields": [ + {"field": k, "type": SchemaFieldGeneratorV2.build_column_type(t)} for k, t in _column_type.items() + ] } # type:ignore return column_type diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index c99d14e8a..2919bb64d 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -63,7 +63,6 @@ class ForestSchemaV2(TypedDict): SCHEMA_V2_FIELDS_MASK = { "enumerations": None, "isPrimaryKey": False, - "isWritable": True, "prefillFormValue": None, "isSortable": True, "isWritable": True, @@ -104,10 +103,14 @@ def template_apply_collection(collection: SchemaV2Collection) -> SchemaV2Collect def _reduce_from_template(input, mask): - reduced = {} - for key, value in input.items(): - if key not in mask or input[key] != mask[key]: - reduced[key] = value + # reduced = {} + # for key, value in input.items(): + # if key not in mask or input[key] != mask[key]: + # reduced[key] = value + reduced = {**input} + for key, value in mask.items(): + if input[key] == value: + del reduced[key] return reduced From fc32afdea6272b1ba81221553a237831b531afd8 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 4 Apr 2024 16:36:05 +0200 Subject: [PATCH 07/16] chore: fix flask models --- .../django_demo/.forestadmin-schema.json | 39 +++++++++++++------ .../django/django_demo/app/flask_models.py | 2 +- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/_example/django/django_demo/.forestadmin-schema.json b/src/_example/django/django_demo/.forestadmin-schema.json index f1136f804..958e49593 100644 --- a/src/_example/django/django_demo/.forestadmin-schema.json +++ b/src/_example/django/django_demo/.forestadmin-schema.json @@ -69,7 +69,7 @@ { "field": "customers", "type": [ - "String" + "Uuid" ], "defaultValue": null, "enums": null, @@ -2799,7 +2799,7 @@ { "field": "flaskcustomersaddresses_customer", "type": [ - "String" + "Uuid" ], "defaultValue": null, "enums": null, @@ -2817,7 +2817,7 @@ { "field": "flaskorder_customer", "type": [ - "String" + "Uuid" ], "defaultValue": null, "enums": null, @@ -2834,19 +2834,30 @@ }, { "field": "id", - "type": "String", + "type": "Uuid", "enums": null, "defaultValue": null, "integration": null, "inverseOf": null, - "isFilterable": false, + "isFilterable": true, "isPrimaryKey": true, "isReadOnly": false, - "isRequired": false, + "isRequired": true, "isSortable": true, "isVirtual": false, "reference": null, - "validations": [] + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 32, + "message": null + } + ] }, { "field": "is_vip", @@ -2929,19 +2940,25 @@ }, { "field": "customer", - "type": "String", + "type": "Uuid", "defaultValue": null, "enums": null, "inverseOf": "flaskcustomersaddresses_customer", "isFilterable": true, "isPrimaryKey": false, "isReadOnly": false, - "isRequired": false, + "isRequired": true, "isSortable": true, "isVirtual": false, "reference": "customer.id", "relationship": "BelongsTo", - "validations": [] + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] }, { "field": "id", @@ -3403,7 +3420,7 @@ }, { "field": "customer", - "type": "String", + "type": "Uuid", "defaultValue": null, "enums": null, "inverseOf": "flaskorder_customer", diff --git a/src/_example/django/django_demo/app/flask_models.py b/src/_example/django/django_demo/app/flask_models.py index 367c56a13..01dc28a17 100644 --- a/src/_example/django/django_demo/app/flask_models.py +++ b/src/_example/django/django_demo/app/flask_models.py @@ -23,7 +23,7 @@ class Meta: class FlaskCustomer(models.Model): - id = models.BinaryField(primary_key=True, db_column="pk") + id = models.UUIDField(primary_key=True, db_column="pk") first_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255) birthday_date = models.DateTimeField(blank=True, null=True) From 06db665f199bad6928ccaafda315e01bbc4f9d98 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Fri, 5 Apr 2024 10:48:57 +0200 Subject: [PATCH 08/16] chore: dump receive schema to compare --- src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py index 0bf98bb68..6626974aa 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py @@ -164,9 +164,12 @@ async def send_schema(cls, options: HttpOptions, schema: ForestSchema) -> bool: @classmethod async def send_schema_v2(cls, options: HttpOptions, schema: ForestSchemaV2) -> bool: ForestLogger.log("info", "Schema was updated, sending new version.") - await cls.post( + ret = await cls.post( cls.build_endpoint(options["server_url"], "/forest/v2/apimaps"), schema, {"forest-secret-key": options["env_secret"], "content-type": "application/json"}, options["verify_ssl"], ) + # TODO: this dump is only to compare schema during poc + with open(options["schema_path"], "w") as fout: + json.dump(ret, fout, indent=4) From e04d308dc2e63fe86779ac39d6d77139cc62bf15 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Tue, 9 Apr 2024 14:57:09 +0200 Subject: [PATCH 09/16] chore: work with schemav2 but still write files for debugging --- .../forestadmin/agent_toolkit/agent.py | 14 +++++++------- .../agent_toolkit/utils/forest_schema/emitter.py | 5 ++++- .../forestadmin/agent_toolkit/utils/http.py | 11 ++++++++--- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py index f9c025952..224508306 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/agent.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/agent.py @@ -200,13 +200,13 @@ async def _start(self): ForestLogger.log("debug", "Starting agent") if self.options["skip_schema_update"] is False: - try: - api_map = await SchemaEmitter.get_serialized_schema( - self.options, await self.customizer.get_datasource(), self.meta - ) - await ForestHttpApi.send_schema(self.options, api_map) - except Exception: - ForestLogger.log("exception", "Error generating/sending forest schema V1") + # try: + # api_map = await SchemaEmitter.get_serialized_schema( + # self.options, await self.customizer.get_datasource(), self.meta + # ) + # await ForestHttpApi.send_schema(self.options, api_map) + # except Exception: + # ForestLogger.log("exception", "Error generating/sending forest schema V1") try: api_map = await SchemaEmitterV2.get_serialized_schema( diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter.py index a7cf77ef8..bf813aaea 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter.py @@ -78,7 +78,10 @@ async def get_serialized_schema( ) raise - return cls.serialize(collections_schema, meta) + serialized = cls.serialize(collections_schema, meta) + with open(f'{options["schema_path"].split(".json")[0]}_json_api.json', "w") as fout: + json.dump(serialized, fout, indent=4) + return serialized @staticmethod async def generate( diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py index 6626974aa..3b793ff2f 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/http.py @@ -170,6 +170,11 @@ async def send_schema_v2(cls, options: HttpOptions, schema: ForestSchemaV2) -> b {"forest-secret-key": options["env_secret"], "content-type": "application/json"}, options["verify_ssl"], ) - # TODO: this dump is only to compare schema during poc - with open(options["schema_path"], "w") as fout: - json.dump(ret, fout, indent=4) + pass + # TODO: remove file writing just after this dump is only to compare schema during poc + if ret: + with open(options["schema_path"], "w") as fout: + json.dump(ret["schemaV1"], fout, indent=4) + + with open(f'{options["schema_path"].split(".json")[0]}_json_api.json', "w") as fout: + json.dump(ret["schemaV1JSONAPI"], fout, indent=4) From 4a5928433ac1183fcbbb2f081792f33bd161eb53 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Tue, 9 Apr 2024 14:57:29 +0200 Subject: [PATCH 10/16] chore: update json example fields --- .../django_demo/.forestadmin-schema.json | 3 +- .../.forestadmin-schema_full_v2.json | 2624 +++++------ .../.forestadmin-schema_json_api.json | 3865 +++++++++++++++++ .../django_demo/.forestadmin-schema_v2.json | 74 +- 4 files changed, 5251 insertions(+), 1315 deletions(-) create mode 100644 src/_example/django/django_demo/.forestadmin-schema_json_api.json diff --git a/src/_example/django/django_demo/.forestadmin-schema.json b/src/_example/django/django_demo/.forestadmin-schema.json index 958e49593..5122a1818 100644 --- a/src/_example/django/django_demo/.forestadmin-schema.json +++ b/src/_example/django/django_demo/.forestadmin-schema.json @@ -3525,6 +3525,7 @@ "stack": { "engine": "python", "engine_version": "3.11.3" - } + }, + "schemaFileHash": "94008f891a07a15668f272455796cec49a8c8526" } } \ No newline at end of file diff --git a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json index 3c1b47525..c33617d46 100644 --- a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json +++ b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json @@ -1,27 +1,9 @@ { "collections": [ { - "segments": [], - "actions": [], + "name": "address", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 255, - "message": null - } - ], "name": "city", "type": "String", "filterOperators": [ @@ -39,14 +21,12 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -58,7 +38,9 @@ "value": 255, "message": null } - ], + ] + }, + { "name": "country", "type": "String", "filterOperators": [ @@ -76,15 +58,26 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -97,14 +90,38 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": true, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] }, { + "name": "street", + "type": "String", + "filterOperators": [ + "Blank", + "Contains", + "EndsWith", + "Equal", + "In", + "Like", + "LongerThan", + "Missing", + "NotContains", + "NotEqual", + "NotIn", + "Present", + "ShorterThan", + "StartsWith" + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -116,8 +133,10 @@ "value": 255, "message": null } - ], - "name": "street", + ] + }, + { + "name": "street_number", "type": "String", "filterOperators": [ "Blank", @@ -134,22 +153,22 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is shorter than", "value": 2, "message": null } - ], - "name": "street_number", + ] + }, + { + "name": "zip_code", "type": "String", "filterOperators": [ "Blank", @@ -166,14 +185,12 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -185,24 +202,6 @@ "value": 5, "message": null } - ], - "name": "zip_code", - "type": "String", - "filterOperators": [ - "Blank", - "Contains", - "EndsWith", - "Equal", - "In", - "Like", - "LongerThan", - "Missing", - "NotContains", - "NotEqual", - "NotIn", - "Present", - "ShorterThan", - "StartsWith" ] } ], @@ -239,43 +238,21 @@ "originKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "address" + "canNativeQuery": true }, { - "segments": [ - { - "id": "app_address.highOrderDelivery", - "name": "highOrderDelivery" - } - ], - "actions": [], + "name": "app_address", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 254, - "message": null - } - ], "name": "city", "type": "String", "filterOperators": [ @@ -293,26 +270,37 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } ] }, { + "name": "complete_address", + "type": "String", + "filterOperators": [], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": false, "prefillFormValue": null, - "isSortable": true, - "validations": [], - "name": "complete_address", - "type": "String", - "filterOperators": [] + "validations": [] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -325,21 +313,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": "France", + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is shorter than", - "value": 254, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "pays", "type": "String", "filterOperators": [ @@ -357,44 +339,53 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, - "isSortable": false, - "validations": [], - "name": "postal_code", - "type": [ - { - "codePostal": "String", - "codeCommune": "String", - "nomCommune": "String", - "libelleAcheminement": "String" - } ], - "filterOperators": [] - }, - { "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, "isSortable": true, + "isWritable": true, + "prefillFormValue": "France", "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, { "type": "is shorter than", "value": 254, "message": null } + ] + }, + { + "name": "postal_code", + "type": [ + { + "fields": [ + { + "field": "codePostal", + "type": "String" + }, + { + "field": "codeCommune", + "type": "String" + }, + { + "field": "nomCommune", + "type": "String" + }, + { + "field": "libelleAcheminement", + "type": "String" + } + ] + } ], + "filterOperators": [], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": false, + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "street", "type": "String", "filterOperators": [ @@ -412,21 +403,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": "75009", "isSortable": true, + "isWritable": true, + "prefillFormValue": null, "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, { "type": "is shorter than", - "value": 5, + "value": 254, "message": null } - ], + ] + }, + { "name": "zip_code", "type": "String", "filterOperators": [ @@ -444,11 +440,23 @@ "Present", "ShorterThan", "StartsWith" - ] - } - ], - "relations": [ - { + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": "75009", + "validations": [ + { + "type": "is shorter than", + "value": 5, + "message": null + } + ] + } + ], + "relations": [ + { "name": "billing_orders_billing_address", "type": "OneToMany", "foreignCollection": "app_order", @@ -480,32 +488,26 @@ "originKeyTarget": "id" } ], + "actions": [], + "segments": [ + { + "id": "app_address.highOrderDelivery", + "name": "highOrderDelivery" + } + ], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": false, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_address" + "canNativeQuery": true }, { - "segments": [ - { - "id": "app_cart.No order", - "name": "No order" - } - ], - "actions": [], + "name": "app_cart", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "created_at", "type": "Date", "filterOperators": [ @@ -536,15 +538,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": false, "prefillFormValue": null, - "isSortable": false, - "validations": [], + "validations": [] + }, + { "name": "customer_id", "type": "Number", "filterOperators": [ @@ -558,15 +560,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, + "isPrimaryKey": false, + "isSortable": false, "isWritable": false, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "id", "type": "Number", "filterOperators": [ @@ -579,26 +581,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 254, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "name", "type": "String", "filterOperators": [ @@ -616,15 +607,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { "name": "order_id", "type": "Number", "filterOperators": [ @@ -637,7 +639,13 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -656,107 +664,48 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [ + { + "id": "app_cart.No order", + "name": "No order" + } + ], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_cart" + "canNativeQuery": true }, { - "segments": [ - { - "id": "app_customer.VIP customers", - "name": "VIP customers" - }, - { - "id": "app_customer.with french address", - "name": "with french address" - } - ], - "actions": [ - { - "id": "app_customer-0-export json", - "name": "Export json", - "type": "bulk", - "baseUrl": null, - "endpoint": "/forest/_actions/app_customer/0/export json", - "httpMethod": "POST", - "redirect": null, - "download": true, - "fields": [], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } - }, - { - "id": "app_customer-1-age operation", - "name": "Age operation", - "type": "single", - "baseUrl": null, - "endpoint": "/forest/_actions/app_customer/1/age operation", - "httpMethod": "POST", - "redirect": null, - "download": false, - "fields": [ - { - "field": "Loading...", - "type": "String", - "isReadOnly": true, - "defaultValue": "Form is loading", - "value": null, - "description": "", - "enums": null, - "hook": null, - "isRequired": false, - "reference": null, - "widget": null - } - ], - "hooks": { - "load": true, - "change": [ - "changeHook" - ] - } - } - ], + "name": "app_customer", "fields": [ { + "name": "TotalSpending", + "type": "Number", + "filterOperators": [], "enumerations": null, "isPrimaryKey": false, + "isSortable": false, "isWritable": false, "prefillFormValue": null, - "isSortable": false, - "validations": [], - "name": "TotalSpending", - "type": "Number", - "filterOperators": [] + "validations": [] }, { + "name": "age", + "type": "Number", + "filterOperators": [], "enumerations": null, "isPrimaryKey": false, + "isSortable": false, "isWritable": false, "prefillFormValue": null, - "isSortable": false, - "validations": [], - "name": "age", - "type": "Number", - "filterOperators": [] + "validations": [] }, { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "avatar", "type": "String", "filterOperators": [ @@ -767,15 +716,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "birthday_date", "type": "Dateonly", "filterOperators": [ @@ -806,15 +755,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": null, + "validations": [] + }, + { "name": "created_at", "type": "Date", "filterOperators": [ @@ -845,26 +794,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 254, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "first_name", "type": "String", "filterOperators": [ @@ -882,15 +820,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": false, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { "name": "full_name", "type": "String", "filterOperators": [ @@ -911,15 +860,15 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, + "isPrimaryKey": false, + "isSortable": false, + "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "id", "type": "Number", "filterOperators": [ @@ -932,15 +881,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": false, + "isPrimaryKey": true, "isSortable": true, - "validations": [], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "is_vip", "type": "Boolean", "filterOperators": [ @@ -951,26 +900,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 254, - "message": null - } - ], + "isWritable": true, + "prefillFormValue": false, + "validations": [] + }, + { "name": "last_name", "type": "String", "filterOperators": [ @@ -988,15 +926,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { "name": "updated_at", "type": "Date", "filterOperators": [ @@ -1027,7 +976,13 @@ "PreviousYearToDate", "Today", "Yesterday" - ] + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -1117,34 +1072,107 @@ "foreignKeyTarget": "id" } ], + "actions": [ + { + "id": "app_customer-0-export json", + "name": "Export json", + "type": "bulk", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + }, + { + "id": "app_customer-1-age operation", + "name": "Age operation", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/1/age operation", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "Loading...", + "type": "String", + "isReadOnly": true, + "defaultValue": "Form is loading", + "value": null, + "description": "", + "enums": null, + "hook": null, + "isRequired": false, + "reference": null, + "widget": null + } + ], + "hooks": { + "load": true, + "change": [ + "changeHook" + ] + } + } + ], + "segments": [ + { + "id": "app_customer.VIP customers", + "name": "VIP customers" + }, + { + "id": "app_customer.with french address", + "name": "with french address" + } + ], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_customer" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "app_customer_blocked_customer", "fields": [ { + "name": "from_customer_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], - "name": "from_customer_id", + ] + }, + { + "name": "id", "type": "Number", "filterOperators": [ "Blank", @@ -1156,16 +1184,16 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": true, + "isSortable": true, "isWritable": false, "prefillFormValue": null, - "isSortable": true, - "validations": [], - "name": "id", + "validations": [] + }, + { + "name": "to_customer_id", "type": "Number", "filterOperators": [ "Blank", @@ -1177,33 +1205,18 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], - "name": "to_customer_id", - "type": "Number", - "filterOperators": [ - "Blank", - "Equal", - "GreaterThan", - "In", - "LessThan", - "Missing", - "NotEqual", - "NotIn", - "Present" ] } ], @@ -1223,33 +1236,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_customer_blocked_customer" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "app_customeraddress", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "address_id", "type": "Number", "filterOperators": [ @@ -1262,21 +1263,21 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], + ] + }, + { "name": "customer_id", "type": "Number", "filterOperators": [ @@ -1289,15 +1290,21 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -1310,7 +1317,13 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": true, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -1329,33 +1342,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_customeraddress" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "app_discountcart", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "discount", "type": "Number", "filterOperators": [ @@ -1368,15 +1369,21 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -1389,7 +1396,13 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": true, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -1401,33 +1414,21 @@ "originKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_discountcart" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "app_extendedcart", "fields": [ { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "cart_id", "type": "Number", "filterOperators": [ @@ -1440,26 +1441,21 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, + "isPrimaryKey": true, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null - }, - { - "type": "is shorter than", - "value": 20, - "message": null } - ], + ] + }, + { "name": "color", "type": "String", "filterOperators": [ @@ -1477,15 +1473,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 20, + "message": null + } + ] + }, + { "name": "discount_id", "type": "Number", "filterOperators": [ @@ -1498,7 +1505,13 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -1517,138 +1530,49 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_extendedcart" + "canNativeQuery": true }, { - "segments": [ - { - "id": "app_order.Delivered order", - "name": "Delivered order" - }, - { - "id": "app_order.Dispatched order", - "name": "Dispatched order" - }, - { - "id": "app_order.Pending order", - "name": "Pending order" - }, - { - "id": "app_order.Rejected order", - "name": "Rejected order" - }, - { - "id": "app_order.Suspicious order", - "name": "Suspicious order" - }, - { - "id": "app_order.newly_created", - "name": "newly_created" - } - ], - "actions": [ - { - "id": "app_order-0-export json", - "name": "Export json", - "type": "global", - "baseUrl": null, - "endpoint": "/forest/_actions/app_order/0/export json", - "httpMethod": "POST", - "redirect": null, - "download": true, - "fields": [ - { - "field": "dummy field", - "value": "", - "defaultValue": "", - "description": "", - "enums": null, - "hook": null, - "isReadOnly": false, - "isRequired": false, - "reference": null, - "type": "String", - "widget": null, - "widgetEdit": null - }, - { - "field": "customer", - "value": null, - "defaultValue": null, - "description": "", - "enums": null, - "hook": null, - "isReadOnly": false, - "isRequired": true, - "reference": "app_customer.id", - "type": "Number", - "widget": null, - "widgetEdit": null - } - ], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } - }, - { - "id": "app_order-1-refund order(s)", - "name": "Refund order(s)", - "type": "single", - "baseUrl": null, - "endpoint": "/forest/_actions/app_order/1/refund order(s)", - "httpMethod": "POST", - "redirect": null, - "download": false, - "fields": [ - { - "field": "reason", - "value": "", - "defaultValue": "", - "description": "", - "enums": null, - "hook": null, - "isReadOnly": false, - "isRequired": false, - "reference": null, - "type": "String", - "widget": null, - "widgetEdit": null - } - ], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } - } - ], + "name": "app_order", "fields": [ { + "name": "billing_address_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], - "name": "billing_address_id", + ] + }, + { + "name": "cost", "type": "Number", "filterOperators": [ "Blank", @@ -1660,14 +1584,12 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -1684,28 +1606,9 @@ "value": 0, "message": null } - ], - "name": "cost", - "type": "Number", - "filterOperators": [ - "Blank", - "Equal", - "GreaterThan", - "In", - "LessThan", - "Missing", - "NotEqual", - "NotIn", - "Present" ] }, { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "created_at", "type": "Date", "filterOperators": [ @@ -1736,15 +1639,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, "isSortable": true, - "validations": [], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "customer_first_name", "type": "String", "filterOperators": [ @@ -1762,26 +1665,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": false, + "isSortable": true, + "isWritable": true, "prefillFormValue": null, - "isSortable": false, - "validations": [], - "name": "customer_full_name", - "type": "String", - "filterOperators": [] + "validations": [] }, { + "name": "customer_full_name", + "type": "String", + "filterOperators": [], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, + "isSortable": false, + "isWritable": false, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "customer_id", "type": "Number", "filterOperators": [ @@ -1794,21 +1697,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], + "validations": [] + }, + { "name": "delivering_address_id", "type": "Number", "filterOperators": [ @@ -1821,15 +1718,21 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -1842,15 +1745,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "ordered_at", "type": "Date", "filterOperators": [ @@ -1881,20 +1784,37 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": false, + "isSortable": true, + "isWritable": true, "prefillFormValue": null, - "isSortable": false, - "validations": [], + "validations": [] + }, + { "name": "ordered_date", "type": "Date", - "filterOperators": [] + "filterOperators": [], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": false, + "isWritable": false, + "prefillFormValue": null, + "validations": [] }, { + "name": "status", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], "enumerations": [ "PENDING", "DISPATCHED", @@ -1902,9 +1822,9 @@ "REJECTED" ], "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -1916,37 +1836,9 @@ "value": 10, "message": null } - ], - "name": "status", - "type": "Enum", - "filterOperators": [ - "Blank", - "Equal", - "In", - "Missing", - "NotEqual", - "NotIn", - "Present" ] }, { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, - "isSortable": false, - "validations": [], - "name": "test_skillcorner", - "type": "String", - "filterOperators": [] - }, - { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "updated_at", "type": "Date", "filterOperators": [ @@ -1977,7 +1869,13 @@ "PreviousYearToDate", "Today", "Yesterday" - ] + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -2010,27 +1908,125 @@ "foreignKeyTarget": "id" } ], + "actions": [ + { + "id": "app_order-0-export json", + "name": "Export json", + "type": "global", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [ + { + "field": "dummy field", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + }, + { + "field": "customer", + "value": null, + "defaultValue": null, + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": true, + "reference": "app_customer.id", + "type": "Number", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + }, + { + "id": "app_order-1-refund order(s)", + "name": "Refund order(s)", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/1/refund order(s)", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "reason", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + } + ], + "segments": [ + { + "id": "app_order.Delivered order", + "name": "Delivered order" + }, + { + "id": "app_order.Dispatched order", + "name": "Dispatched order" + }, + { + "id": "app_order.Pending order", + "name": "Pending order" + }, + { + "id": "app_order.Rejected order", + "name": "Rejected order" + }, + { + "id": "app_order.Suspicious order", + "name": "Suspicious order" + }, + { + "id": "app_order.newly_created", + "name": "newly_created" + } + ], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "app_order" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "auth_group", "fields": [ { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -2043,26 +2039,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 150, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "name", "type": "String", "filterOperators": [ @@ -2080,6 +2065,23 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } ] } ], @@ -2119,33 +2121,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "auth_group" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "auth_group_permissions", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "group_id", "type": "Number", "filterOperators": [ @@ -2158,15 +2148,21 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -2179,21 +2175,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "permission_id", "type": "Number", "filterOperators": [ @@ -2206,6 +2196,18 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] } ], @@ -2225,38 +2227,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "auth_group_permissions" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "auth_permission", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 100, - "message": null - } - ], "name": "codename", "type": "String", "filterOperators": [ @@ -2274,21 +2259,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null } - ], + ] + }, + { "name": "content_type_id", "type": "Number", "filterOperators": [ @@ -2301,15 +2291,21 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -2322,26 +2318,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 255, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "name", "type": "String", "filterOperators": [ @@ -2359,6 +2344,23 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } ] } ], @@ -2405,27 +2407,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "auth_permission" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "auth_user", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "date_joined", "type": "Date", "filterOperators": [ @@ -2456,21 +2452,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is shorter than", - "value": 254, - "message": null - } - ], + "validations": [] + }, + { "name": "email", "type": "String", "filterOperators": [ @@ -2488,21 +2478,21 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is shorter than", - "value": 150, + "value": 254, "message": null } - ], + ] + }, + { "name": "first_name", "type": "String", "filterOperators": [ @@ -2520,15 +2510,21 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -2541,15 +2537,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": true, + "isPrimaryKey": true, "isSortable": true, - "validations": [], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "is_active", "type": "Boolean", "filterOperators": [ @@ -2560,15 +2556,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": false, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": true, + "validations": [] + }, + { "name": "is_staff", "type": "Boolean", "filterOperators": [ @@ -2579,15 +2575,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": false, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "is_superuser", "type": "Boolean", "filterOperators": [ @@ -2598,15 +2594,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": false, + "validations": [] + }, + { "name": "last_login", "type": "Date", "filterOperators": [ @@ -2637,21 +2633,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is shorter than", - "value": 150, - "message": null - } - ], + "validations": [] + }, + { "name": "last_name", "type": "String", "filterOperators": [ @@ -2669,26 +2659,21 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, { "type": "is shorter than", - "value": 128, + "value": 150, "message": null } - ], + ] + }, + { "name": "password", "type": "String", "filterOperators": [ @@ -2706,14 +2691,12 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -2722,10 +2705,12 @@ }, { "type": "is shorter than", - "value": 150, + "value": 128, "message": null } - ], + ] + }, + { "name": "username", "type": "String", "filterOperators": [ @@ -2743,6 +2728,23 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } ] } ], @@ -2789,34 +2791,49 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "auth_user" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "auth_user_groups", "fields": [ { + "name": "group_id", + "type": "Number", + "filterOperators": [ + "Blank", + "Equal", + "GreaterThan", + "In", + "LessThan", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], - "name": "group_id", + ] + }, + { + "name": "id", "type": "Number", "filterOperators": [ "Blank", @@ -2828,16 +2845,16 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": true, + "isSortable": true, "isWritable": false, "prefillFormValue": null, - "isSortable": true, - "validations": [], - "name": "id", + "validations": [] + }, + { + "name": "user_id", "type": "Number", "filterOperators": [ "Blank", @@ -2849,33 +2866,18 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], - "name": "user_id", - "type": "Number", - "filterOperators": [ - "Blank", - "Equal", - "GreaterThan", - "In", - "LessThan", - "Missing", - "NotEqual", - "NotIn", - "Present" ] } ], @@ -2895,27 +2897,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "auth_user_groups" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "auth_user_user_permissions", "fields": [ { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -2928,21 +2924,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "permission_id", "type": "Number", "filterOperators": [ @@ -2955,21 +2945,21 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], + ] + }, + { "name": "user_id", "type": "Number", "filterOperators": [ @@ -2982,6 +2972,18 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] } ], @@ -3001,27 +3003,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "auth_user_user_permissions" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "cart", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "created_at", "type": "Date", "filterOperators": [ @@ -3052,15 +3048,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, + "isPrimaryKey": false, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": null, + "validations": [] + }, + { "name": "id", "type": "Number", "filterOperators": [ @@ -3073,26 +3069,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 255, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "name", "type": "String", "filterOperators": [ @@ -3110,15 +3095,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { "name": "order_id", "type": "Number", "filterOperators": [ @@ -3131,7 +3127,13 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -3143,27 +3145,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "cart" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "customer", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "age", "type": "Number", "filterOperators": [ @@ -3176,15 +3172,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "avatar", "type": "String", "filterOperators": [ @@ -3195,15 +3191,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "birthday_date", "type": "Date", "filterOperators": [ @@ -3234,26 +3230,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 255, - "message": null - } - ], + "validations": [] + }, + { "name": "first_name", "type": "String", "filterOperators": [ @@ -3271,34 +3256,60 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, + "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { "name": "id", - "type": "String", + "type": "Uuid", "filterOperators": [ "Blank", + "Contains", + "EndsWith", "Equal", "In", + "Like", "Missing", "NotEqual", "NotIn", - "Present" - ] - }, - { + "Present", + "StartsWith" + ], "enumerations": null, - "isPrimaryKey": false, + "isPrimaryKey": true, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 32, + "message": null + } + ] + }, + { "name": "is_vip", "type": "Boolean", "filterOperators": [ @@ -3309,26 +3320,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 255, - "message": null - } - ], + "validations": [] + }, + { "name": "last_name", "type": "String", "filterOperators": [ @@ -3346,6 +3346,23 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } ] } ], @@ -3375,33 +3392,21 @@ "originKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "customer" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "customers_addresses", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "address_id", "type": "Number", "filterOperators": [ @@ -3414,34 +3419,50 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { "name": "customer_id", - "type": "String", + "type": "Uuid", "filterOperators": [ "Blank", + "Contains", + "EndsWith", "Equal", "In", + "Like", "Missing", "NotEqual", "NotIn", - "Present" + "Present", + "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -3454,7 +3475,13 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": true, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] } ], "relations": [ @@ -3473,56 +3500,50 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "customers_addresses" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "django_admin_log", "fields": [ { + "name": "action_flag", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], "enumerations": [ "1", "2", "3" ], "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], - "name": "action_flag", - "type": "Enum", - "filterOperators": [ - "Blank", - "Equal", - "In", - "Missing", - "NotEqual", - "NotIn", - "Present" ] }, { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "action_time", "type": "Date", "filterOperators": [ @@ -3553,15 +3574,15 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "change_message", "type": "String", "filterOperators": [ @@ -3579,15 +3600,15 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "content_type_id", "type": "Number", "filterOperators": [ @@ -3600,15 +3621,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, + "isPrimaryKey": false, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": null, + "validations": [] + }, + { "name": "id", "type": "Number", "filterOperators": [ @@ -3621,15 +3642,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "object_id", "type": "String", "filterOperators": [ @@ -3647,26 +3668,15 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 200, - "message": null - } - ], + "validations": [] + }, + { "name": "object_repr", "type": "String", "filterOperators": [ @@ -3684,21 +3694,26 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null + }, + { + "type": "is shorter than", + "value": 200, + "message": null } - ], + ] + }, + { "name": "user_id", "type": "Number", "filterOperators": [ @@ -3711,6 +3726,18 @@ "NotEqual", "NotIn", "Present" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] } ], @@ -3730,38 +3757,21 @@ "foreignKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "django_admin_log" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "django_content_type", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 100, - "message": null - } - ], "name": "app_label", "type": "String", "filterOperators": [ @@ -3779,15 +3789,26 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } ] }, { - "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, - "isSortable": true, - "validations": [], "name": "id", "type": "Number", "filterOperators": [ @@ -3800,26 +3821,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, + "isPrimaryKey": true, "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - }, - { - "type": "is shorter than", - "value": 100, - "message": null - } - ], + "isWritable": false, + "prefillFormValue": null, + "validations": [] + }, + { "name": "model", "type": "String", "filterOperators": [ @@ -3837,6 +3847,23 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": false, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } ] } ], @@ -3856,33 +3883,21 @@ "originKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "django_content_type" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "django_session", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "expire_date", "type": "Date", "filterOperators": [ @@ -3913,21 +3928,21 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null } - ], + ] + }, + { "name": "session_data", "type": "String", "filterOperators": [ @@ -3945,26 +3960,21 @@ "Present", "ShorterThan", "StartsWith" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, + "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", "value": null, "message": null - }, - { - "type": "is shorter than", - "value": 40, - "message": null } - ], + ] + }, + { "name": "session_key", "type": "String", "filterOperators": [ @@ -3982,37 +3992,42 @@ "Present", "ShorterThan", "StartsWith" + ], + "enumerations": null, + "isPrimaryKey": true, + "isSortable": true, + "isWritable": true, + "prefillFormValue": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 40, + "message": null + } ] } ], "relations": [], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "django_session" + "canNativeQuery": true }, { - "segments": [], - "actions": [], + "name": "order", "fields": [ { - "enumerations": null, - "isPrimaryKey": false, - "isWritable": true, - "prefillFormValue": null, - "isSortable": true, - "validations": [ - { - "type": "is present", - "value": null, - "message": null - } - ], "name": "amount", "type": "Number", "filterOperators": [ @@ -4025,15 +4040,21 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { "name": "billing_address_id", "type": "Number", "filterOperators": [ @@ -4046,15 +4067,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "created_at", "type": "Date", "filterOperators": [ @@ -4085,34 +4106,38 @@ "PreviousYearToDate", "Today", "Yesterday" - ] - }, - { + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "customer_id", - "type": "String", + "type": "Uuid", "filterOperators": [ "Blank", + "Contains", + "EndsWith", "Equal", "In", + "Like", "Missing", "NotEqual", "NotIn", - "Present" - ] - }, - { + "Present", + "StartsWith" + ], "enumerations": null, "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, - "validations": [], + "validations": [] + }, + { "name": "delivering_address_id", "type": "Number", "filterOperators": [ @@ -4125,15 +4150,15 @@ "NotEqual", "NotIn", "Present" - ] - }, - { + ], "enumerations": null, - "isPrimaryKey": true, - "isWritable": false, - "prefillFormValue": null, + "isPrimaryKey": false, "isSortable": true, - "validations": [], + "isWritable": true, + "prefillFormValue": null, + "validations": [] + }, + { "name": "id", "type": "Number", "filterOperators": [ @@ -4146,9 +4171,26 @@ "NotEqual", "NotIn", "Present" - ] + ], + "enumerations": null, + "isPrimaryKey": true, + "isSortable": true, + "isWritable": false, + "prefillFormValue": null, + "validations": [] }, { + "name": "status", + "type": "Enum", + "filterOperators": [ + "Blank", + "Equal", + "In", + "Missing", + "NotEqual", + "NotIn", + "Present" + ], "enumerations": [ "PENDING", "DISPATCHED", @@ -4156,9 +4198,9 @@ "REJECTED" ], "isPrimaryKey": false, + "isSortable": true, "isWritable": true, "prefillFormValue": null, - "isSortable": true, "validations": [ { "type": "is present", @@ -4170,17 +4212,6 @@ "value": 10, "message": null } - ], - "name": "status", - "type": "Enum", - "filterOperators": [ - "Blank", - "Equal", - "In", - "Missing", - "NotEqual", - "NotIn", - "Present" ] } ], @@ -4214,15 +4245,16 @@ "originKeyTarget": "id" } ], + "actions": [], + "segments": [], + "canSearch": true, "canList": true, "canCreate": true, "canUpdate": true, "canDelete": true, "canCount": true, - "canSearch": true, "canChart": true, - "canNativeQuery": true, - "name": "order" + "canNativeQuery": true } ], "meta": { diff --git a/src/_example/django/django_demo/.forestadmin-schema_json_api.json b/src/_example/django/django_demo/.forestadmin-schema_json_api.json new file mode 100644 index 000000000..972ed6b5a --- /dev/null +++ b/src/_example/django/django_demo/.forestadmin-schema_json_api.json @@ -0,0 +1,3865 @@ +{ + "data": [ + { + "id": "address", + "type": "collections", + "attributes": { + "name": "address", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "city", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "field": "country", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "field": "customers", + "type": [ + "Uuid" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "addresses", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "customer.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "flaskcustomersaddresses_address", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "customers_addresses.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "flaskorder_billing_address", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "billing_address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "order.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "order_delivering_address_set_delivering_address", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "delivering_address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "order.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "street", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "field": "street_number", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is shorter than", + "value": 2, + "message": null + } + ] + }, + { + "field": "zip_code", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 5, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "app_address", + "type": "collections", + "attributes": { + "name": "app_address", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "billing_orders_billing_address", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "billing_address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_order.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "city", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "complete_address", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "customeraddress_address", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customeraddress.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "customers", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "addresses", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "delivering_orders_delivering_address", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "delivering_address", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_order.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "pays", + "type": "String", + "enums": null, + "defaultValue": "France", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "postal_code", + "type": [ + { + "fields": [ + { + "field": "codePostal", + "type": "String" + }, + { + "field": "codeCommune", + "type": "String" + }, + { + "field": "nomCommune", + "type": "String" + }, + { + "field": "libelleAcheminement", + "type": "String" + } + ] + } + ], + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "street", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "zip_code", + "type": "String", + "enums": null, + "defaultValue": "75009", + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is shorter than", + "value": 5, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [ + { + "id": "app_address.highOrderDelivery", + "type": "segments" + } + ] + } + } + }, + { + "id": "app_cart", + "type": "collections", + "attributes": { + "name": "app_cart", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "customer_id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "extendedcart", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "cart", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_extendedcart.cart_id", + "relationship": "HasOne", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "order", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "cart", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_order.id", + "relationship": "BelongsTo", + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [ + { + "id": "app_cart.No order", + "type": "segments" + } + ] + } + } + }, + { + "id": "app_customer", + "type": "collections", + "attributes": { + "name": "app_customer", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "Customer_blocked_customer+_from_customer", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "from_customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer_blocked_customer.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "Customer_blocked_customer+_to_customer", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "to_customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer_blocked_customer.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "TotalSpending", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "addresses", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "customers", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_address.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "age", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "avatar", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "birthday_date", + "type": "Dateonly", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "block_by_users", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "blocked_customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "blocked_customer", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "block_by_users", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "customeraddress_customer", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customeraddress.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "first_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "full_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "is_vip", + "type": "Boolean", + "enums": null, + "defaultValue": false, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "last_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "orders_customer", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_order.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "smart_billing_addresses", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_address.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "smart_carts", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_cart.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "smart_delivering_addresses", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_address.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "updated_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [ + { + "id": "app_customer.Export json", + "type": "actions" + }, + { + "id": "app_customer.Age operation", + "type": "actions" + } + ] + }, + "segments": { + "data": [ + { + "id": "app_customer.VIP customers", + "type": "segments" + }, + { + "id": "app_customer.with french address", + "type": "segments" + } + ] + } + } + }, + { + "id": "app_customer_blocked_customer", + "type": "collections", + "attributes": { + "name": "app_customer_blocked_customer", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "from_customer", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "Customer_blocked_customer+_from_customer", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "to_customer", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "Customer_blocked_customer+_to_customer", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "app_customeraddress", + "type": "collections", + "attributes": { + "name": "app_customeraddress", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "address", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "customeraddress_address", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_address.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "customer", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "customeraddress_customer", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "app_discountcart", + "type": "collections", + "attributes": { + "name": "app_discountcart", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "discount", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "extendedcart", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "discount", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_extendedcart.discount_id", + "relationship": "HasOne", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "app_extendedcart", + "type": "collections", + "attributes": { + "name": "app_extendedcart", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "cart", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "extendedcart", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_cart.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "cart_id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "color", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 20, + "message": null + } + ] + }, + { + "field": "discount", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "extendedcart", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_discountcart.id", + "relationship": "BelongsTo", + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "app_order", + "type": "collections", + "attributes": { + "name": "app_order", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "billing_address", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "billing_orders_billing_address", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_address.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "cart", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "order", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_cart.order_id", + "relationship": "HasOne", + "validations": [] + }, + { + "field": "cost", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is greater than", + "value": 0, + "message": null + }, + { + "type": "is greater than", + "value": 0, + "message": null + } + ] + }, + { + "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "customer", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "orders_customer", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "app_customer.id", + "relationship": "BelongsTo", + "validations": [] + }, + { + "field": "customer_first_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "customer_full_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "delivering_address", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "delivering_orders_delivering_address", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "app_address.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "ordered_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "ordered_date", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": false, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "status", + "type": "Enum", + "enums": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ] + }, + { + "field": "updated_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [ + { + "id": "app_order.Export json", + "type": "actions" + }, + { + "id": "app_order.Refund order(s)", + "type": "actions" + } + ] + }, + "segments": { + "data": [ + { + "id": "app_order.Delivered order", + "type": "segments" + }, + { + "id": "app_order.Dispatched order", + "type": "segments" + }, + { + "id": "app_order.Pending order", + "type": "segments" + }, + { + "id": "app_order.Rejected order", + "type": "segments" + }, + { + "id": "app_order.Suspicious order", + "type": "segments" + }, + { + "id": "app_order.newly_created", + "type": "segments" + } + ] + } + } + }, + { + "id": "auth_group", + "type": "collections", + "attributes": { + "name": "auth_group", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "Group_permissions+_group", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "group", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_group_permissions.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "User_groups+_group", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "group", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user_groups.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + }, + { + "field": "permissions", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "group", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_permission.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "user", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "groups", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user.id", + "relationship": "BelongsToMany", + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "auth_group_permissions", + "type": "collections", + "attributes": { + "name": "auth_group_permissions", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "group", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "Group_permissions+_group", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_group.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "permission", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "Group_permissions+_permission", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_permission.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "auth_permission", + "type": "collections", + "attributes": { + "name": "auth_permission", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "Group_permissions+_permission", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "permission", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_group_permissions.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "User_user_permissions+_permission", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "permission", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user_user_permissions.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "codename", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ] + }, + { + "field": "content_type", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "permission_content_type", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "django_content_type.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "group", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "permissions", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_group.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "field": "user", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "user_permissions", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user.id", + "relationship": "BelongsToMany", + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "auth_user", + "type": "collections", + "attributes": { + "name": "auth_user", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "User_groups+_user", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "user", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user_groups.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "User_user_permissions+_user", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "user", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user_user_permissions.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "date_joined", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "email", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is shorter than", + "value": 254, + "message": null + } + ] + }, + { + "field": "first_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + }, + { + "field": "groups", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "user", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_group.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "is_active", + "type": "Boolean", + "enums": null, + "defaultValue": true, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "is_staff", + "type": "Boolean", + "enums": null, + "defaultValue": false, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "is_superuser", + "type": "Boolean", + "enums": null, + "defaultValue": false, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "last_login", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "last_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + }, + { + "field": "logentry_user", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "user", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "django_admin_log.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "password", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 128, + "message": null + } + ] + }, + { + "field": "user_permissions", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "user", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_permission.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "username", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 150, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "auth_user_groups", + "type": "collections", + "attributes": { + "name": "auth_user_groups", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "group", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "User_groups+_group", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_group.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "user", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "User_groups+_user", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "auth_user_user_permissions", + "type": "collections", + "attributes": { + "name": "auth_user_user_permissions", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "permission", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "User_user_permissions+_permission", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_permission.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "user", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "User_user_permissions+_user", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "cart", + "type": "collections", + "attributes": { + "name": "cart", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "field": "order", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "flaskcart_order", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "order.id", + "relationship": "BelongsTo", + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "customer", + "type": "collections", + "attributes": { + "name": "customer", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "addresses", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "customers", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "address.id", + "relationship": "BelongsToMany", + "validations": [] + }, + { + "field": "age", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "avatar", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "birthday_date", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "first_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + }, + { + "field": "flaskcustomersaddresses_customer", + "type": [ + "Uuid" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "customers_addresses.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "flaskorder_customer", + "type": [ + "Uuid" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "customer", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "order.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "id", + "type": "Uuid", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 32, + "message": null + } + ] + }, + { + "field": "is_vip", + "type": "Boolean", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "last_name", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 255, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "customers_addresses", + "type": "collections", + "attributes": { + "name": "customers_addresses", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "address", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "flaskcustomersaddresses_address", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "address.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "customer", + "type": "Uuid", + "defaultValue": null, + "enums": null, + "inverseOf": "flaskcustomersaddresses_customer", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "customer.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "django_admin_log", + "type": "collections", + "attributes": { + "name": "django_admin_log", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "action_flag", + "type": "Enum", + "enums": [ + "1", + "2", + "3" + ], + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "action_time", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "change_message", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "content_type", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "logentry_content_type", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "django_content_type.id", + "relationship": "BelongsTo", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "object_id", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "object_repr", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 200, + "message": null + } + ] + }, + { + "field": "user", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "logentry_user", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": "auth_user.id", + "relationship": "BelongsTo", + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "django_content_type", + "type": "collections", + "attributes": { + "name": "django_content_type", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "app_label", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "logentry_content_type", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "content_type", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "django_admin_log.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "model", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 100, + "message": null + } + ] + }, + { + "field": "permission_content_type", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "content_type", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "auth_permission.id", + "relationship": "HasMany", + "validations": [] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "django_session", + "type": "collections", + "attributes": { + "name": "django_session", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "expire_date", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "session_data", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "session_key", + "type": "String", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 40, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + }, + { + "id": "order", + "type": "collections", + "attributes": { + "name": "order", + "icon": null, + "integration": null, + "isReadOnly": false, + "isSearchable": true, + "isVirtual": false, + "onlyForRelationships": false, + "paginationType": "page", + "fields": [ + { + "field": "amount", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } + ] + }, + { + "field": "billing_address", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "flaskorder_billing_address", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "address.id", + "relationship": "BelongsTo", + "validations": [] + }, + { + "field": "created_at", + "type": "Date", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "customer", + "type": "Uuid", + "defaultValue": null, + "enums": null, + "inverseOf": "flaskorder_customer", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "customer.id", + "relationship": "BelongsTo", + "validations": [] + }, + { + "field": "delivering_address", + "type": "Number", + "defaultValue": null, + "enums": null, + "inverseOf": "order_delivering_address_set_delivering_address", + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "address.id", + "relationship": "BelongsTo", + "validations": [] + }, + { + "field": "flaskcart_order", + "type": [ + "Number" + ], + "defaultValue": null, + "enums": null, + "inverseOf": "order", + "isFilterable": false, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": "cart.id", + "relationship": "HasMany", + "validations": [] + }, + { + "field": "id", + "type": "Number", + "enums": null, + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": true, + "isReadOnly": true, + "isRequired": false, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [] + }, + { + "field": "status", + "type": "Enum", + "enums": [ + "PENDING", + "DISPATCHED", + "DELIVERED", + "REJECTED" + ], + "defaultValue": null, + "integration": null, + "inverseOf": null, + "isFilterable": true, + "isPrimaryKey": false, + "isReadOnly": false, + "isRequired": true, + "isSortable": true, + "isVirtual": false, + "reference": null, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 10, + "message": null + } + ] + } + ] + }, + "relationships": { + "actions": { + "data": [] + }, + "segments": { + "data": [] + } + } + } + ], + "included": [ + { + "id": "app_address.highOrderDelivery", + "type": "segments", + "attributes": { + "id": "app_address.highOrderDelivery", + "name": "highOrderDelivery" + } + }, + { + "id": "app_cart.No order", + "type": "segments", + "attributes": { + "id": "app_cart.No order", + "name": "No order" + } + }, + { + "id": "app_customer.Export json", + "type": "actions", + "attributes": { + "id": "app_customer-0-export json", + "name": "Export json", + "type": "bulk", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + } + }, + { + "id": "app_customer.Age operation", + "type": "actions", + "attributes": { + "id": "app_customer-1-age operation", + "name": "Age operation", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_customer/1/age operation", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "Loading...", + "type": "String", + "isReadOnly": true, + "defaultValue": "Form is loading", + "value": null, + "description": "", + "enums": null, + "hook": null, + "isRequired": false, + "reference": null, + "widget": null + } + ], + "hooks": { + "load": true, + "change": [ + "changeHook" + ] + } + } + }, + { + "id": "app_customer.VIP customers", + "type": "segments", + "attributes": { + "id": "app_customer.VIP customers", + "name": "VIP customers" + } + }, + { + "id": "app_customer.with french address", + "type": "segments", + "attributes": { + "id": "app_customer.with french address", + "name": "with french address" + } + }, + { + "id": "app_order.Export json", + "type": "actions", + "attributes": { + "id": "app_order-0-export json", + "name": "Export json", + "type": "global", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/0/export json", + "httpMethod": "POST", + "redirect": null, + "download": true, + "fields": [ + { + "field": "dummy field", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + }, + { + "field": "customer", + "value": null, + "defaultValue": null, + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": true, + "reference": "app_customer.id", + "type": "Number", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + } + }, + { + "id": "app_order.Refund order(s)", + "type": "actions", + "attributes": { + "id": "app_order-1-refund order(s)", + "name": "Refund order(s)", + "type": "single", + "baseUrl": null, + "endpoint": "/forest/_actions/app_order/1/refund order(s)", + "httpMethod": "POST", + "redirect": null, + "download": false, + "fields": [ + { + "field": "reason", + "value": "", + "defaultValue": "", + "description": "", + "enums": null, + "hook": null, + "isReadOnly": false, + "isRequired": false, + "reference": null, + "type": "String", + "widget": null, + "widgetEdit": null + } + ], + "hooks": { + "load": false, + "change": [ + "changeHook" + ] + } + } + }, + { + "id": "app_order.Delivered order", + "type": "segments", + "attributes": { + "id": "app_order.Delivered order", + "name": "Delivered order" + } + }, + { + "id": "app_order.Dispatched order", + "type": "segments", + "attributes": { + "id": "app_order.Dispatched order", + "name": "Dispatched order" + } + }, + { + "id": "app_order.Pending order", + "type": "segments", + "attributes": { + "id": "app_order.Pending order", + "name": "Pending order" + } + }, + { + "id": "app_order.Rejected order", + "type": "segments", + "attributes": { + "id": "app_order.Rejected order", + "name": "Rejected order" + } + }, + { + "id": "app_order.Suspicious order", + "type": "segments", + "attributes": { + "id": "app_order.Suspicious order", + "name": "Suspicious order" + } + }, + { + "id": "app_order.newly_created", + "type": "segments", + "attributes": { + "id": "app_order.newly_created", + "name": "newly_created" + } + } + ], + "meta": { + "liana": "agent-python", + "liana_version": "1.5.6", + "stack": { + "engine": "python", + "engine_version": "3.11.3" + }, + "schemaFileHash": "94008f891a07a15668f272455796cec49a8c8526" + } +} \ No newline at end of file diff --git a/src/_example/django/django_demo/.forestadmin-schema_v2.json b/src/_example/django/django_demo/.forestadmin-schema_v2.json index e8824d5e7..74be1de9c 100644 --- a/src/_example/django/django_demo/.forestadmin-schema_v2.json +++ b/src/_example/django/django_demo/.forestadmin-schema_v2.json @@ -300,10 +300,24 @@ "name": "postal_code", "type": [ { - "codePostal": "String", - "codeCommune": "String", - "nomCommune": "String", - "libelleAcheminement": "String" + "fields": [ + { + "field": "codePostal", + "type": "String" + }, + { + "field": "codeCommune", + "type": "String" + }, + { + "field": "nomCommune", + "type": "String" + }, + { + "field": "libelleAcheminement", + "type": "String" + } + ] } ], "filterOperators": [], @@ -1505,13 +1519,6 @@ } ] }, - { - "name": "test_skillcorner", - "type": "String", - "filterOperators": [], - "isSortable": false, - "isWritable": false - }, { "name": "updated_at", "type": "Date", @@ -2698,17 +2705,33 @@ }, { "name": "id", - "type": "String", + "type": "Uuid", "filterOperators": [ "Blank", + "Contains", + "EndsWith", "Equal", "In", + "Like", "Missing", "NotEqual", "NotIn", - "Present" + "Present", + "StartsWith" ], - "isPrimaryKey": true + "isPrimaryKey": true, + "validations": [ + { + "type": "is present", + "value": null, + "message": null + }, + { + "type": "is shorter than", + "value": 32, + "message": null + } + ] }, { "name": "is_vip", @@ -2810,15 +2833,26 @@ }, { "name": "customer_id", - "type": "String", + "type": "Uuid", "filterOperators": [ "Blank", + "Contains", + "EndsWith", "Equal", "In", + "Like", "Missing", "NotEqual", "NotIn", - "Present" + "Present", + "StartsWith" + ], + "validations": [ + { + "type": "is present", + "value": null, + "message": null + } ] }, { @@ -3343,15 +3377,19 @@ }, { "name": "customer_id", - "type": "String", + "type": "Uuid", "filterOperators": [ "Blank", + "Contains", + "EndsWith", "Equal", "In", + "Like", "Missing", "NotEqual", "NotIn", - "Present" + "Present", + "StartsWith" ] }, { From c2cb1616557a93f63f65ce543fea88e4b02b93f4 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 11 Apr 2024 15:44:23 +0200 Subject: [PATCH 11/16] chore: translate action v2 for schema --- .../utils/forest_schema/emitter_v2.py | 13 +-- .../forest_schema/generator_action_v2.py | 79 +++++++++++++ .../forest_schema/generator_collection_v2.py | 4 +- .../utils/forest_schema/type_v2.py | 106 +++++++++++++++--- 4 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py index 152cc7ca1..ff25cb073 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py @@ -6,11 +6,7 @@ from forestadmin.agent_toolkit.options import Options from forestadmin.agent_toolkit.utils.forest_schema.generator_collection_v2 import SchemaCollectionGeneratorV2 from forestadmin.agent_toolkit.utils.forest_schema.type import AgentMeta -from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ( - SchemaV2Collection, - template_reduce_collection, - template_reduce_field, -) +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Collection, template_reduce_collection from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.datasource_customizer.datasource_customizer import DatasourceCustomizer from forestadmin.datasource_toolkit.datasources import Datasource @@ -44,12 +40,7 @@ async def get_serialized_schema( with open(schema_path, "w", encoding="utf-8") as schema_file: reduced_collections = [] for collection in collections_schema: - reduced_collections.append( - { - **template_reduce_collection(collection), - "fields": [{**template_reduce_field(f)} for f in collection["fields"]], - } - ) + reduced_collections.append(template_reduce_collection(collection)) json.dump({"collections": reduced_collections, "meta": meta}, schema_file, indent=4) else: 1 / 0 diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py new file mode 100644 index 000000000..d3a2f2f28 --- /dev/null +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py @@ -0,0 +1,79 @@ +from typing import List, Union, cast + +from forestadmin.agent_toolkit.utils.forest_schema.action_values import ForestValueConverter +from forestadmin.agent_toolkit.utils.forest_schema.generator_action_field_widget import GeneratorActionFieldWidget +from forestadmin.agent_toolkit.utils.forest_schema.generator_field_v2 import SchemaFieldGeneratorV2 +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Action, SchemaV2ActionField +from forestadmin.datasource_toolkit.collections import Collection +from forestadmin.datasource_toolkit.datasource_customizer.collection_customizer import CollectionCustomizer +from forestadmin.datasource_toolkit.datasources import Datasource +from forestadmin.datasource_toolkit.interfaces.actions import Action, ActionField, ActionFieldType +from forestadmin.datasource_toolkit.interfaces.fields import Column, PrimitiveType +from forestadmin.datasource_toolkit.utils.schema import SchemaUtils + + +class SchemaActionGeneratorV2: + + @classmethod + async def build(cls, prefix: str, collection: Union[Collection, CollectionCustomizer], name: str) -> SchemaV2Action: + schema = collection.schema["actions"][name] + idx = list(collection.schema["actions"].keys()).index(name) + slug = name.lower().replace(r"[^a-z0-9-]+", "-") + return SchemaV2Action( + id=f"{collection.name}-{idx}-{slug}", # type:ignore + name=name, + type=schema.scope.value.lower(), # type:ignore + endpoint=f"/forest/_actions/{collection.name}/{idx}/{slug}", # type:ignore + download=bool(schema.generate_file), + fields=await cls.build_fields(collection, schema, name), + isDynamicForm=not schema.static_form, + ) + + @classmethod + async def build_field_schema(cls, datasource: Datasource[Collection], field: ActionField) -> SchemaV2ActionField: + value = ForestValueConverter.value_to_forest(field, field.get("value")) + default_value = ForestValueConverter.value_to_forest(field, field.get("default_value")) + output = { + "name": field["label"], + "type": PrimitiveType.STRING, + "description": field.get("description"), + # When sending to server, we need to rename 'value' into 'defaultValue' + # otherwise, it does not gets applied 🤷‍♂️ + "value": value, + "defaultValue": default_value, + "isReadOnly": field.get("is_read_only", False), + "isRequired": field.get("is_required", True), + "widget": GeneratorActionFieldWidget.build_widget_options(field), + } + if field["type"] == ActionFieldType.COLLECTION: + collection: Collection = datasource.get_collection(field["collection_name"]) # type: ignore + pk = SchemaUtils.get_primary_keys(collection.schema)[0] + pk_schema = cast(Column, collection.get_field(pk)) + output["type"] = SchemaFieldGeneratorV2.build_column_type(pk_schema["column_type"]) # type: ignore + output["reference"] = f"{collection.name}.{pk}" + + elif "File" in field["type"].value: + output["type"] = ["File"] if "List" in field["type"].value else "File" + + elif field["type"].value.endswith("List"): + output["type"] = [PrimitiveType(field["type"].value[:-4])] + else: + output["type"] = field["type"].value + + if field["type"] in [ActionFieldType.ENUM, ActionFieldType.ENUM_LIST]: + output["enums"] = field.get("enum_values") + + if not isinstance(output["type"], str): + output["type"] = SchemaFieldGeneratorV2.build_column_type(output["type"]) # type:ignore + + return SchemaV2ActionField(**output) + + @classmethod + async def build_fields( + cls, collection: Union[Collection, CollectionCustomizer], action: Action, name: str + ) -> List[SchemaV2ActionField]: + fields = await collection.get_form(None, name, None, None) # type:ignore + new_fields: List[SchemaV2ActionField] = [] + for field in fields: + new_fields.append(await cls.build_field_schema(collection.datasource, field)) # type:ignore + return new_fields diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py index 378786f16..4a1f4445b 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_collection_v2.py @@ -1,6 +1,6 @@ from typing import List -from forestadmin.agent_toolkit.utils.forest_schema.generator_action import SchemaActionGenerator +from forestadmin.agent_toolkit.utils.forest_schema.generator_action_v2 import SchemaActionGeneratorV2 from forestadmin.agent_toolkit.utils.forest_schema.generator_field_v2 import SchemaFieldGeneratorV2 from forestadmin.agent_toolkit.utils.forest_schema.generator_segment import SchemaSegmentGenerator from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Collection, SchemaV2Field, SchemaV2Relation @@ -26,7 +26,7 @@ async def build(prefix: str, collection: Collection) -> SchemaV2Collection: "relations": sorted(relations, key=lambda field: field["name"]), "actions": sorted( [ - await SchemaActionGenerator.build(prefix, collection, name) + await SchemaActionGeneratorV2.build(prefix, collection, name) for name in collection.schema["actions"].keys() ], key=lambda action: action["id"], diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index 2919bb64d..4c38da781 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -1,11 +1,12 @@ -from typing import Any, List, Literal +from typing import Any, List, Literal, Optional from forestadmin.agent_toolkit.utils.forest_schema.type import ( AgentMeta, - ForestServerAction, ForestServerSegment, ServerValidationType, + WidgetEditConfiguration, ) +from forestadmin.datasource_toolkit.interfaces.actions import ActionFieldType from forestadmin.datasource_toolkit.interfaces.fields import ColumnAlias from typing_extensions import NotRequired, TypedDict @@ -26,22 +27,46 @@ class SchemaV2Field(TypedDict): type: ColumnAlias isPrimaryKey: NotRequired[bool] filterOperators: List[str] - enumerations: NotRequired[List[str]] + enumerations: NotRequired[Optional[List[str]]] isWritable: NotRequired[bool] isSortable: NotRequired[bool] - prefillFormValue: Any + prefillFormValue: NotRequired[Optional[Any]] validations: NotRequired[List[ServerValidationType]] +class SchemaV2ActionField(TypedDict): + name: str + type: ActionFieldType + description: NotRequired[Optional[str]] + + value: NotRequired[Optional[Any]] + defaultValue: NotRequired[Optional[Any]] + enumeration: NotRequired[Optional[List[str]]] + isReadOnly: NotRequired[bool] + isRequired: NotRequired[bool] + reference: NotRequired[Optional[str]] + widget: NotRequired[Optional[WidgetEditConfiguration]] + + +class SchemaV2Action(TypedDict): + id: str + name: str + type: Literal["single", "bulk", "global"] + endpoint: str # should it include the 'prefix' setting of the agent ?? + fields: NotRequired[List[SchemaV2ActionField]] + download: NotRequired[bool] + isDynamicForm: NotRequired[bool] + + class SchemaV2Collection(TypedDict): name: str fields: List[SchemaV2Field] relations: List[SchemaV2Relation] segments: NotRequired[List[ForestServerSegment]] - actions: NotRequired[List[ForestServerAction]] + actions: NotRequired[List[SchemaV2Action]] canList: NotRequired[bool] canCreate: NotRequired[bool] @@ -69,6 +94,21 @@ class ForestSchemaV2(TypedDict): "validations": [], } +SCHEMA_V2_ACTION_FIELD_MASK = { + "value": None, + "defaultValue": None, + "enumeration": None, + "isReadOnly": False, + "isRequired": False, + "reference": None, + "widget": None, +} + +SCHEMA_V2_ACTION_MASK = { + "download": False, + "isDynamicForm": False, + "fields": [], +} SCHEMA_V2_COLLECTION_MASK = { "segments": [], @@ -86,20 +126,33 @@ class ForestSchemaV2(TypedDict): } -def template_reduce_field(collection: SchemaV2Field) -> SchemaV2Field: - return _reduce_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore +# reduce templates +def template_reduce_collection(collection: SchemaV2Collection) -> SchemaV2Collection: + fields: List[SchemaV2Field] = collection.get("fields", []) + relations: List[SchemaV2Relation] = collection.get("relations", []) + actions: List[SchemaV2Action] = collection.get("actions", []) + reduced: SchemaV2Collection = {**collection} + reduced["fields"] = [template_reduce_field(field) for field in fields] + reduced["relations"] = relations + reduced["actions"] = [template_reduce_action(action) for action in actions] + return _reduce_from_template(reduced, SCHEMA_V2_COLLECTION_MASK) # type:ignore -def template_reduce_collection(collection: SchemaV2Collection) -> SchemaV2Collection: - return _reduce_from_template(collection, SCHEMA_V2_COLLECTION_MASK) # type:ignore +def template_reduce_field(collection: SchemaV2Field) -> SchemaV2Field: + return _reduce_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore -def template_apply_field(collection: SchemaV2Field) -> SchemaV2Field: - return _apply_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore +def template_reduce_action(action: SchemaV2Action) -> SchemaV2Action: + fields: List[SchemaV2ActionField] = action.get("fields", []) + reduced_action: SchemaV2Action = {**action} + reduced_action["fields"] = [template_reduce_action_field(action) for action in fields] + return _reduce_from_template(reduced_action, SCHEMA_V2_ACTION_MASK) # type:ignore -def template_apply_collection(collection: SchemaV2Collection) -> SchemaV2Collection: - return _apply_from_template(collection, SCHEMA_V2_COLLECTION_MASK) # type:ignore + +def template_reduce_action_field(action_field: SchemaV2ActionField) -> SchemaV2ActionField: + reduced: SchemaV2ActionField = _reduce_from_template(action_field, SCHEMA_V2_ACTION_FIELD_MASK) # type:ignore + return reduced def _reduce_from_template(input, mask): @@ -114,6 +167,33 @@ def _reduce_from_template(input, mask): return reduced +# apply templates +def template_apply_collection(collection: SchemaV2Collection) -> SchemaV2Collection: + fields: List[SchemaV2Field] = collection.get("fields", []) + actions: List[SchemaV2Action] = collection.get("actions", []) + + full: SchemaV2Collection = {**collection} + full["fields"] = [template_apply_field(field) for field in fields] + full["actions"] = [template_apply_action(action) for action in actions] + return _apply_from_template(collection, SCHEMA_V2_COLLECTION_MASK) # type:ignore + + +def template_apply_field(collection: SchemaV2Field) -> SchemaV2Field: + return _apply_from_template(collection, SCHEMA_V2_FIELDS_MASK) # type:ignore + + +def template_apply_action(action: SchemaV2Action) -> SchemaV2Action: + fields = action.get("fields", []) + full: SchemaV2Action = {**action} + + full["fields"] = [template_apply_action_field(field) for field in fields] + return _apply_from_template(full, SCHEMA_V2_ACTION_MASK) # type:ignore + + +def template_apply_action_field(action_field: SchemaV2ActionField) -> SchemaV2ActionField: + return _apply_from_template(action_field, SCHEMA_V2_ACTION_FIELD_MASK) # type:ignore + + def _apply_from_template(input, mask): full = {} for key, value in mask.items(): From e7bedb2662053747ca41acb85e16aba7fee0486c Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Fri, 12 Apr 2024 10:16:09 +0200 Subject: [PATCH 12/16] chore: details about action schema --- .../utils/forest_schema/generator_action_v2.py | 16 +++++++++++----- .../agent_toolkit/utils/forest_schema/type_v2.py | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py index d3a2f2f28..dfbf2e318 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py @@ -33,16 +33,22 @@ async def build(cls, prefix: str, collection: Union[Collection, CollectionCustom async def build_field_schema(cls, datasource: Datasource[Collection], field: ActionField) -> SchemaV2ActionField: value = ForestValueConverter.value_to_forest(field, field.get("value")) default_value = ForestValueConverter.value_to_forest(field, field.get("default_value")) - output = { + output: SchemaV2ActionField = { "name": field["label"], - "type": PrimitiveType.STRING, + "type": field["type"], "description": field.get("description"), # When sending to server, we need to rename 'value' into 'defaultValue' # otherwise, it does not gets applied 🤷‍♂️ "value": value, "defaultValue": default_value, - "isReadOnly": field.get("is_read_only", False), - "isRequired": field.get("is_required", True), + # "enumeration": None, # default value + "isReadOnly": ( + field["is_read_only"] if "is_read_only" in field and field["is_read_only"] is not None else False + ), + "isRequired": ( + field["is_required"] if "is_required" in field and field["is_required"] is not None else True + ), + # "reference": None, # default value "widget": GeneratorActionFieldWidget.build_widget_options(field), } if field["type"] == ActionFieldType.COLLECTION: @@ -61,7 +67,7 @@ async def build_field_schema(cls, datasource: Datasource[Collection], field: Act output["type"] = field["type"].value if field["type"] in [ActionFieldType.ENUM, ActionFieldType.ENUM_LIST]: - output["enums"] = field.get("enum_values") + output["enumeration"] = field.get("enum_values") if not isinstance(output["type"], str): output["type"] = SchemaFieldGeneratorV2.build_column_type(output["type"]) # type:ignore diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index 4c38da781..1636f1535 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -98,6 +98,7 @@ class ForestSchemaV2(TypedDict): "value": None, "defaultValue": None, "enumeration": None, + "description": "", "isReadOnly": False, "isRequired": False, "reference": None, @@ -162,7 +163,7 @@ def _reduce_from_template(input, mask): # reduced[key] = value reduced = {**input} for key, value in mask.items(): - if input[key] == value: + if key in input and input[key] == value: del reduced[key] return reduced From 44df97e755d3f3aafa5be7d71f82670f9a377682 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Fri, 12 Apr 2024 14:21:04 +0200 Subject: [PATCH 13/16] chore: fix actions ? wip --- .../forest_schema/generator_action_v2.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py index dfbf2e318..939d6a6a2 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py @@ -2,7 +2,9 @@ from forestadmin.agent_toolkit.utils.forest_schema.action_values import ForestValueConverter from forestadmin.agent_toolkit.utils.forest_schema.generator_action_field_widget import GeneratorActionFieldWidget +from forestadmin.agent_toolkit.utils.forest_schema.generator_field import SchemaFieldGenerator from forestadmin.agent_toolkit.utils.forest_schema.generator_field_v2 import SchemaFieldGeneratorV2 +from forestadmin.agent_toolkit.utils.forest_schema.type import ForestServerActionField from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Action, SchemaV2ActionField from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.datasource_customizer.collection_customizer import CollectionCustomizer @@ -13,6 +15,21 @@ class SchemaActionGeneratorV2: + DUMMY_FIELDS = [ + ForestServerActionField( + field="Loading...", + type=SchemaFieldGenerator.build_column_type(PrimitiveType.STRING), + isReadOnly=True, + defaultValue="Form is loading", + value=None, + description="", + enums=None, + hook=None, + isRequired=False, + reference=None, + widget=None, + ) + ] @classmethod async def build(cls, prefix: str, collection: Union[Collection, CollectionCustomizer], name: str) -> SchemaV2Action: @@ -25,7 +42,11 @@ async def build(cls, prefix: str, collection: Union[Collection, CollectionCustom type=schema.scope.value.lower(), # type:ignore endpoint=f"/forest/_actions/{collection.name}/{idx}/{slug}", # type:ignore download=bool(schema.generate_file), - fields=await cls.build_fields(collection, schema, name), + fields=( + await cls.build_fields(collection, schema, name) + if schema.static_form + else SchemaActionGeneratorV2.DUMMY_FIELDS + ), isDynamicForm=not schema.static_form, ) From d67607f21059fa92bef4785a11b9f2529e8d1cc5 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Tue, 16 Apr 2024 09:36:06 +0200 Subject: [PATCH 14/16] fix: fix action fields --- .../.forestadmin-schema_full_v2.json | 108 ++++------------ .../django_demo/.forestadmin-schema_v2.json | 115 +++--------------- .../forest_schema/generator_action_v2.py | 41 ++----- .../utils/forest_schema/type_v2.py | 4 +- 4 files changed, 57 insertions(+), 211 deletions(-) diff --git a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json index c33617d46..2a6b1355a 100644 --- a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json +++ b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json @@ -1076,50 +1076,19 @@ { "id": "app_customer-0-export json", "name": "Export json", - "type": "bulk", - "baseUrl": null, + "scope": "bulk", "endpoint": "/forest/_actions/app_customer/0/export json", - "httpMethod": "POST", - "redirect": null, "download": true, - "fields": [], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } + "isDynamicForm": false, + "fields": [] }, { "id": "app_customer-1-age operation", "name": "Age operation", - "type": "single", - "baseUrl": null, + "scope": "single", "endpoint": "/forest/_actions/app_customer/1/age operation", - "httpMethod": "POST", - "redirect": null, "download": false, - "fields": [ - { - "field": "Loading...", - "type": "String", - "isReadOnly": true, - "defaultValue": "Form is loading", - "value": null, - "description": "", - "enums": null, - "hook": null, - "isRequired": false, - "reference": null, - "widget": null - } - ], - "hooks": { - "load": true, - "change": [ - "changeHook" - ] - } + "isDynamicForm": true } ], "segments": [ @@ -1912,80 +1881,53 @@ { "id": "app_order-0-export json", "name": "Export json", - "type": "global", - "baseUrl": null, + "scope": "global", "endpoint": "/forest/_actions/app_order/0/export json", - "httpMethod": "POST", - "redirect": null, "download": true, + "isDynamicForm": false, "fields": [ { - "field": "dummy field", + "name": "dummy field", + "type": "String", + "description": "", "value": "", "defaultValue": "", - "description": "", - "enums": null, - "hook": null, "isReadOnly": false, "isRequired": false, - "reference": null, - "type": "String", - "widget": null, - "widgetEdit": null + "widget": null }, { - "field": "customer", + "name": "customer", + "type": "Number", + "description": "", "value": null, "defaultValue": null, - "description": "", - "enums": null, - "hook": null, "isReadOnly": false, "isRequired": true, - "reference": "app_customer.id", - "type": "Number", "widget": null, - "widgetEdit": null + "reference": "app_customer.id" } - ], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } + ] }, { "id": "app_order-1-refund order(s)", "name": "Refund order(s)", - "type": "single", - "baseUrl": null, + "scope": "single", "endpoint": "/forest/_actions/app_order/1/refund order(s)", - "httpMethod": "POST", - "redirect": null, "download": false, + "isDynamicForm": false, "fields": [ { - "field": "reason", + "name": "reason", + "type": "String", + "description": "", "value": "", "defaultValue": "", - "description": "", - "enums": null, - "hook": null, "isReadOnly": false, "isRequired": false, - "reference": null, - "type": "String", - "widget": null, - "widgetEdit": null + "widget": null } - ], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } + ] } ], "segments": [ @@ -4259,10 +4201,10 @@ ], "meta": { "liana": "agent-python", - "liana_version": "1.5.6", + "liana_version": "1.5.5", "stack": { "engine": "python", - "engine_version": "3.11.3" + "engine_version": "3.10.11" } } } \ No newline at end of file diff --git a/src/_example/django/django_demo/.forestadmin-schema_v2.json b/src/_example/django/django_demo/.forestadmin-schema_v2.json index 74be1de9c..f696f9f5e 100644 --- a/src/_example/django/django_demo/.forestadmin-schema_v2.json +++ b/src/_example/django/django_demo/.forestadmin-schema_v2.json @@ -911,50 +911,16 @@ { "id": "app_customer-0-export json", "name": "Export json", - "type": "bulk", - "baseUrl": null, + "scope": "bulk", "endpoint": "/forest/_actions/app_customer/0/export json", - "httpMethod": "POST", - "redirect": null, - "download": true, - "fields": [], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } + "download": true }, { "id": "app_customer-1-age operation", "name": "Age operation", - "type": "single", - "baseUrl": null, + "scope": "single", "endpoint": "/forest/_actions/app_customer/1/age operation", - "httpMethod": "POST", - "redirect": null, - "download": false, - "fields": [ - { - "field": "Loading...", - "type": "String", - "isReadOnly": true, - "defaultValue": "Form is loading", - "value": null, - "description": "", - "enums": null, - "hook": null, - "isRequired": false, - "reference": null, - "widget": null - } - ], - "hooks": { - "load": true, - "change": [ - "changeHook" - ] - } + "isDynamicForm": true } ], "segments": [ @@ -1588,80 +1554,37 @@ { "id": "app_order-0-export json", "name": "Export json", - "type": "global", - "baseUrl": null, + "scope": "global", "endpoint": "/forest/_actions/app_order/0/export json", - "httpMethod": "POST", - "redirect": null, "download": true, "fields": [ { - "field": "dummy field", - "value": "", - "defaultValue": "", - "description": "", - "enums": null, - "hook": null, - "isReadOnly": false, - "isRequired": false, - "reference": null, + "name": "dummy field", "type": "String", - "widget": null, - "widgetEdit": null + "value": "", + "defaultValue": "" }, { - "field": "customer", - "value": null, - "defaultValue": null, - "description": "", - "enums": null, - "hook": null, - "isReadOnly": false, - "isRequired": true, - "reference": "app_customer.id", + "name": "customer", "type": "Number", - "widget": null, - "widgetEdit": null + "isRequired": true, + "reference": "app_customer.id" } - ], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } + ] }, { "id": "app_order-1-refund order(s)", "name": "Refund order(s)", - "type": "single", - "baseUrl": null, + "scope": "single", "endpoint": "/forest/_actions/app_order/1/refund order(s)", - "httpMethod": "POST", - "redirect": null, - "download": false, "fields": [ { - "field": "reason", - "value": "", - "defaultValue": "", - "description": "", - "enums": null, - "hook": null, - "isReadOnly": false, - "isRequired": false, - "reference": null, + "name": "reason", "type": "String", - "widget": null, - "widgetEdit": null + "value": "", + "defaultValue": "" } - ], - "hooks": { - "load": false, - "change": [ - "changeHook" - ] - } + ] } ], "segments": [ @@ -3490,10 +3413,10 @@ ], "meta": { "liana": "agent-python", - "liana_version": "1.5.6", + "liana_version": "1.5.5", "stack": { "engine": "python", - "engine_version": "3.11.3" + "engine_version": "3.10.11" } } } \ No newline at end of file diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py index 939d6a6a2..f30f7421f 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_action_v2.py @@ -2,9 +2,7 @@ from forestadmin.agent_toolkit.utils.forest_schema.action_values import ForestValueConverter from forestadmin.agent_toolkit.utils.forest_schema.generator_action_field_widget import GeneratorActionFieldWidget -from forestadmin.agent_toolkit.utils.forest_schema.generator_field import SchemaFieldGenerator from forestadmin.agent_toolkit.utils.forest_schema.generator_field_v2 import SchemaFieldGeneratorV2 -from forestadmin.agent_toolkit.utils.forest_schema.type import ForestServerActionField from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Action, SchemaV2ActionField from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.datasource_customizer.collection_customizer import CollectionCustomizer @@ -15,40 +13,23 @@ class SchemaActionGeneratorV2: - DUMMY_FIELDS = [ - ForestServerActionField( - field="Loading...", - type=SchemaFieldGenerator.build_column_type(PrimitiveType.STRING), - isReadOnly=True, - defaultValue="Form is loading", - value=None, - description="", - enums=None, - hook=None, - isRequired=False, - reference=None, - widget=None, - ) - ] - @classmethod async def build(cls, prefix: str, collection: Union[Collection, CollectionCustomizer], name: str) -> SchemaV2Action: schema = collection.schema["actions"][name] idx = list(collection.schema["actions"].keys()).index(name) slug = name.lower().replace(r"[^a-z0-9-]+", "-") - return SchemaV2Action( + ret = SchemaV2Action( id=f"{collection.name}-{idx}-{slug}", # type:ignore name=name, - type=schema.scope.value.lower(), # type:ignore + scope=schema.scope.value.lower(), # type:ignore endpoint=f"/forest/_actions/{collection.name}/{idx}/{slug}", # type:ignore download=bool(schema.generate_file), - fields=( - await cls.build_fields(collection, schema, name) - if schema.static_form - else SchemaActionGeneratorV2.DUMMY_FIELDS - ), isDynamicForm=not schema.static_form, ) + if schema.static_form: + ret["fields"] = await cls.build_fields(collection, schema, name) + + return ret @classmethod async def build_field_schema(cls, datasource: Datasource[Collection], field: ActionField) -> SchemaV2ActionField: @@ -61,7 +42,7 @@ async def build_field_schema(cls, datasource: Datasource[Collection], field: Act # When sending to server, we need to rename 'value' into 'defaultValue' # otherwise, it does not gets applied 🤷‍♂️ "value": value, - "defaultValue": default_value, + "prefillValue": default_value, # "enumeration": None, # default value "isReadOnly": ( field["is_read_only"] if "is_read_only" in field and field["is_read_only"] is not None else False @@ -80,18 +61,18 @@ async def build_field_schema(cls, datasource: Datasource[Collection], field: Act output["reference"] = f"{collection.name}.{pk}" elif "File" in field["type"].value: - output["type"] = ["File"] if "List" in field["type"].value else "File" + output["type"] = ["File"] if "List" in field["type"].value else "File" # type: ignore elif field["type"].value.endswith("List"): - output["type"] = [PrimitiveType(field["type"].value[:-4])] + output["type"] = [PrimitiveType(field["type"].value[:-4])] # type: ignore else: - output["type"] = field["type"].value + output["type"] = field["type"].value # type: ignore if field["type"] in [ActionFieldType.ENUM, ActionFieldType.ENUM_LIST]: output["enumeration"] = field.get("enum_values") if not isinstance(output["type"], str): - output["type"] = SchemaFieldGeneratorV2.build_column_type(output["type"]) # type:ignore + output["type"] = SchemaFieldGeneratorV2.build_column_type(output["type"]) # type: ignore return SchemaV2ActionField(**output) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index 1636f1535..fb948632f 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -42,7 +42,7 @@ class SchemaV2ActionField(TypedDict): description: NotRequired[Optional[str]] value: NotRequired[Optional[Any]] - defaultValue: NotRequired[Optional[Any]] + prefillValue: NotRequired[Optional[Any]] enumeration: NotRequired[Optional[List[str]]] isReadOnly: NotRequired[bool] isRequired: NotRequired[bool] @@ -53,7 +53,7 @@ class SchemaV2ActionField(TypedDict): class SchemaV2Action(TypedDict): id: str name: str - type: Literal["single", "bulk", "global"] + scope: Literal["single", "bulk", "global"] endpoint: str # should it include the 'prefix' setting of the agent ?? fields: NotRequired[List[SchemaV2ActionField]] download: NotRequired[bool] From 42bfbcab8b51d87d1187bb4ba353011e75d171ff Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 18 Apr 2024 17:04:29 +0200 Subject: [PATCH 15/16] chore: handle agent meta in schema --- .../.forestadmin-schema_full_v2.json | 19 +++++--- .../django_demo/.forestadmin-schema_v2.json | 17 ++++--- .../utils/forest_schema/emitter_v2.py | 45 ++++++++++++++++--- .../utils/forest_schema/type_v2.py | 20 ++++++--- .../datasource_django/datasource.py | 13 +++++- .../datasource_toolkit/datasources.py | 10 ++++- .../interfaces/models/collections.py | 19 +++++--- 7 files changed, 114 insertions(+), 29 deletions(-) diff --git a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json index 2a6b1355a..780e2de17 100644 --- a/src/_example/django/django_demo/.forestadmin-schema_full_v2.json +++ b/src/_example/django/django_demo/.forestadmin-schema_full_v2.json @@ -1891,7 +1891,7 @@ "type": "String", "description": "", "value": "", - "defaultValue": "", + "prefillValue": "", "isReadOnly": false, "isRequired": false, "widget": null @@ -1901,7 +1901,7 @@ "type": "Number", "description": "", "value": null, - "defaultValue": null, + "prefillValue": null, "isReadOnly": false, "isRequired": true, "widget": null, @@ -1922,7 +1922,7 @@ "type": "String", "description": "", "value": "", - "defaultValue": "", + "prefillValue": "", "isReadOnly": false, "isRequired": false, "widget": null @@ -4200,11 +4200,18 @@ } ], "meta": { - "liana": "agent-python", - "liana_version": "1.5.5", + "agent": "agent-python", + "agent_version": "1.5.5", "stack": { "engine": "python", "engine_version": "3.10.11" - } + }, + "datasources": [ + { + "name": "DjangoDatasource", + "version": "1.5.5", + "django_version": "4.2.11" + } + ] } } \ No newline at end of file diff --git a/src/_example/django/django_demo/.forestadmin-schema_v2.json b/src/_example/django/django_demo/.forestadmin-schema_v2.json index f696f9f5e..0901215de 100644 --- a/src/_example/django/django_demo/.forestadmin-schema_v2.json +++ b/src/_example/django/django_demo/.forestadmin-schema_v2.json @@ -1562,7 +1562,7 @@ "name": "dummy field", "type": "String", "value": "", - "defaultValue": "" + "prefillValue": "" }, { "name": "customer", @@ -1582,7 +1582,7 @@ "name": "reason", "type": "String", "value": "", - "defaultValue": "" + "prefillValue": "" } ] } @@ -3412,11 +3412,18 @@ } ], "meta": { - "liana": "agent-python", - "liana_version": "1.5.5", + "agent": "agent-python", + "agent_version": "1.5.5", "stack": { "engine": "python", "engine_version": "3.10.11" - } + }, + "datasources": [ + { + "name": "DjangoDatasource", + "version": "1.5.5", + "django_version": "4.2.11" + } + ] } } \ No newline at end of file diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py index ff25cb073..213c07dc4 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py @@ -6,15 +6,24 @@ from forestadmin.agent_toolkit.options import Options from forestadmin.agent_toolkit.utils.forest_schema.generator_collection_v2 import SchemaCollectionGeneratorV2 from forestadmin.agent_toolkit.utils.forest_schema.type import AgentMeta -from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import SchemaV2Collection, template_reduce_collection +from forestadmin.agent_toolkit.utils.forest_schema.type_v2 import ( + AgentMetaV2, + SchemaV2Collection, + template_reduce_collection, +) from forestadmin.datasource_toolkit.collections import Collection from forestadmin.datasource_toolkit.datasource_customizer.datasource_customizer import DatasourceCustomizer from forestadmin.datasource_toolkit.datasources import Datasource +from forestadmin.datasource_toolkit.decorators.collection_decorator import CollectionDecorator class SchemaEmitterV2: @classmethod - def serialize(cls, collections: List[SchemaV2Collection], meta: AgentMeta) -> Dict[str, Any]: + def serialize( + cls, + collections: List[SchemaV2Collection], + meta: AgentMetaV2, + ) -> Dict[str, Any]: """return schema ready to send as api_map format""" schema_file_hash = sha1(json.dumps({"collections": collections, "meta": meta}).encode("utf-8")).hexdigest() return { @@ -25,23 +34,39 @@ def serialize(cls, collections: List[SchemaV2Collection], meta: AgentMeta) -> Di }, } + @classmethod + def generate_meta( + cls, + meta: AgentMeta, + datasource: Union[Datasource[Collection], DatasourceCustomizer], + ) -> AgentMetaV2: + used_datasources = set([_get_base_datasource(col) for col in datasource.collections]) + + return { + "agent": meta["liana"], + "agent_version": meta["liana_version"], + "stack": meta["stack"], + "datasources": [d.mk_meta_entry() for d in used_datasources], + } + @classmethod async def get_serialized_schema( cls, options: Options, datasource: Union[Datasource[Collection], DatasourceCustomizer], meta: AgentMeta ): + meta_v2 = cls.generate_meta(meta, datasource) schema_path = f'{options["schema_path"].split(".json")[0]}_v2.json' full_schema_path = f'{options["schema_path"].split(".json")[0]}_full_v2.json' if not options["is_production"]: collections_schema = await SchemaEmitterV2.generate(options["prefix"], datasource) with open(full_schema_path, "w", encoding="utf-8") as schema_file: - json.dump({"collections": collections_schema, "meta": meta}, schema_file, indent=4) + json.dump({"collections": collections_schema, "meta": meta_v2}, schema_file, indent=4) with open(schema_path, "w", encoding="utf-8") as schema_file: reduced_collections = [] for collection in collections_schema: reduced_collections.append(template_reduce_collection(collection)) - json.dump({"collections": reduced_collections, "meta": meta}, schema_file, indent=4) + json.dump({"collections": reduced_collections, "meta": meta_v2}, schema_file, indent=4) else: 1 / 0 try: @@ -55,8 +80,7 @@ async def get_serialized_schema( ) raise - # return cls.serialize(reduced_collections, meta) - return cls.serialize(collections_schema, meta) + return cls.serialize(collections_schema, meta_v2) @staticmethod async def generate( @@ -67,3 +91,12 @@ async def generate( for collection in datasource.collections: collection_schema.append(await SchemaCollectionGeneratorV2.build(prefix, collection)) # type:ignore return sorted(collection_schema, key=lambda collection: collection["name"]) + + +def _get_base_datasource(input_collection: Collection) -> Datasource: + collection = input_collection + + while isinstance(collection, CollectionDecorator): + collection = collection.child_collection + + return collection.datasource # type:ignore diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index fb948632f..8fc827821 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -1,7 +1,7 @@ -from typing import Any, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional from forestadmin.agent_toolkit.utils.forest_schema.type import ( - AgentMeta, + AgentStackMeta, ForestServerSegment, ServerValidationType, WidgetEditConfiguration, @@ -15,6 +15,7 @@ class SchemaV2Relation(TypedDict): name: str type: Literal["ManyToMany", "ManyToOne", "OneToOne", "OneToMany"] foreignCollection: str + throughCollection: NotRequired[str] foreignKey: NotRequired[str] foreignKeyTarget: NotRequired[str] @@ -79,9 +80,18 @@ class SchemaV2Collection(TypedDict): canNativeQuery: NotRequired[bool] +class AgentMetaV2(TypedDict): + agent: str + agent_version: str + stack: AgentStackMeta + datasources: NotRequired[ + List[Dict[str, Any]] + ] # here to store "name", "version", "dialect", ... and other nice to have values without formal keys + + class ForestSchemaV2(TypedDict): - data: List[SchemaV2Collection] - meta: AgentMeta + collections: List[SchemaV2Collection] + meta: AgentMetaV2 # MASKS @@ -96,7 +106,7 @@ class ForestSchemaV2(TypedDict): SCHEMA_V2_ACTION_FIELD_MASK = { "value": None, - "defaultValue": None, + "prefillValue": None, "enumeration": None, "description": "", "isReadOnly": False, diff --git a/src/datasource_django/forestadmin/datasource_django/datasource.py b/src/datasource_django/forestadmin/datasource_django/datasource.py index 88cd40e52..2c6d48672 100644 --- a/src/datasource_django/forestadmin/datasource_django/datasource.py +++ b/src/datasource_django/forestadmin/datasource_django/datasource.py @@ -1,13 +1,24 @@ +from importlib.metadata import version +from typing import Any, Dict + from django.apps import apps from forestadmin.datasource_django.collection import DjangoCollection from forestadmin.datasource_django.interface import BaseDjangoDatasource class DjangoDatasource(BaseDjangoDatasource): - def __init__(self) -> None: + def __init__(self): super().__init__() self._create_collections() + @classmethod + def mk_meta_entry(cls) -> Dict[str, Any]: + return { + "name": "DjangoDatasource", + "version": version("forestadmin-agent-django").replace("b", "-beta."), + "django_version": version("django").replace("b", "-beta."), + } + def _create_collections(self): models = apps.get_models(include_auto_created=True) for model in models: diff --git a/src/datasource_toolkit/forestadmin/datasource_toolkit/datasources.py b/src/datasource_toolkit/forestadmin/datasource_toolkit/datasources.py index 9623aa5f3..b249791d7 100644 --- a/src/datasource_toolkit/forestadmin/datasource_toolkit/datasources.py +++ b/src/datasource_toolkit/forestadmin/datasource_toolkit/datasources.py @@ -1,4 +1,5 @@ -from typing import Dict, List +from importlib.metadata import version +from typing import Any, Dict, List from forestadmin.agent_toolkit.utils.context import User from forestadmin.datasource_toolkit.exceptions import DatasourceToolkitException @@ -15,6 +16,13 @@ class Datasource(DatasourceInterface[BoundCollection]): def __init__(self) -> None: self._collections: Dict[str, BoundCollection] = {} + @classmethod + def mk_meta_entry(cls) -> Dict[str, Any]: + return { + "name": "FromToolkitDatasource", + "version": version("forestadmin-datasource-toolkit").replace("b", "-beta."), + } + @property def schema(self): return {"charts": {}} diff --git a/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py b/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py index 4f3a61973..ddd812673 100644 --- a/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py +++ b/src/datasource_toolkit/forestadmin/datasource_toolkit/interfaces/models/collections.py @@ -1,5 +1,5 @@ import abc -from typing import Callable, Dict, Generic, List, TypedDict, TypeVar +from typing import Any, Callable, Dict, Generic, List, TypedDict, TypeVar from forestadmin.datasource_toolkit.interfaces.actions import Action from forestadmin.datasource_toolkit.interfaces.fields import FieldAlias @@ -26,15 +26,18 @@ class CollectionSchema(TypedDict): class Collection(abc.ABC): - @abc.abstractproperty + @property + @abc.abstractmethod def datasource(self) -> "Datasource[Self]": raise NotImplementedError - @abc.abstractproperty + @property + @abc.abstractmethod def name(self) -> str: raise NotImplementedError - @abc.abstractproperty + @property + @abc.abstractmethod def schema(self) -> CollectionSchema: raise NotImplementedError @@ -44,7 +47,13 @@ def schema(self) -> CollectionSchema: class Datasource(Generic[BoundCollection], abc.ABC): - @abc.abstractproperty + @classmethod + @abc.abstractmethod + def mk_meta_entry(cls) -> Dict[str, Any]: + raise NotImplementedError + + @property + @abc.abstractmethod def collections(self) -> List[BoundCollection]: raise NotImplementedError From d27c325824ffbda438461d6e5a89a543e026c371 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 18 Apr 2024 17:33:12 +0200 Subject: [PATCH 16/16] chore: some details about schema generation --- .../agent_toolkit/utils/forest_schema/emitter_v2.py | 7 ++++--- .../utils/forest_schema/generator_field_v2.py | 4 +++- .../agent_toolkit/utils/forest_schema/type_v2.py | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py index 213c07dc4..f0ec55041 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/emitter_v2.py @@ -62,10 +62,11 @@ async def get_serialized_schema( with open(full_schema_path, "w", encoding="utf-8") as schema_file: json.dump({"collections": collections_schema, "meta": meta_v2}, schema_file, indent=4) + reduced_collections = [] + for collection in collections_schema: + reduced_collections.append(template_reduce_collection(collection)) + collections_schema = reduced_collections with open(schema_path, "w", encoding="utf-8") as schema_file: - reduced_collections = [] - for collection in collections_schema: - reduced_collections.append(template_reduce_collection(collection)) json.dump({"collections": reduced_collections, "meta": meta_v2}, schema_file, indent=4) else: 1 / 0 diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py index 1a6c9f3ec..59a92af97 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/generator_field_v2.py @@ -37,7 +37,9 @@ def build_field(cls, collection: Collection, field_name: str) -> SchemaV2Field: for operator in field_schema["filter_operators"] or {} ] ), - "enumerations": field_schema["enum_values"], # type:ignore + "enumerations": ( + field_schema["enum_values"] if field_schema["enum_values"] is not None else [] + ), # type:ignore "isPrimaryKey": field_schema["is_primary_key"], "isSortable": field_schema["is_sortable"], "isWritable": not field_schema["is_read_only"], diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py index 8fc827821..86be32189 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/utils/forest_schema/type_v2.py @@ -28,7 +28,7 @@ class SchemaV2Field(TypedDict): type: ColumnAlias isPrimaryKey: NotRequired[bool] filterOperators: List[str] - enumerations: NotRequired[Optional[List[str]]] + enumerations: NotRequired[List[str]] isWritable: NotRequired[bool] isSortable: NotRequired[bool] @@ -96,7 +96,7 @@ class ForestSchemaV2(TypedDict): # MASKS SCHEMA_V2_FIELDS_MASK = { - "enumerations": None, + "enumerations": [], "isPrimaryKey": False, "prefillFormValue": None, "isSortable": True,