@statezero/core 0.2.23 → 0.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/filtering/localFiltering.d.ts +1 -8
  2. package/dist/filtering/localFiltering.js +129 -47
  3. package/dist/models/backend1/django_app/comprehensivemodel.schema.json +3 -3
  4. package/dist/models/backend1/django_app/custompkmodel.schema.json +4 -4
  5. package/dist/models/backend1/django_app/dailyrate.schema.json +5 -5
  6. package/dist/models/backend1/django_app/dummymodel.schema.json +2 -2
  7. package/dist/models/backend1/django_app/index.js +3 -0
  8. package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +118 -0
  9. package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +71 -0
  10. package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +94 -0
  11. package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +118 -0
  12. package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +71 -0
  13. package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +94 -0
  14. package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +134 -0
  15. package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +71 -0
  16. package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +112 -0
  17. package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +2 -2
  18. package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +1 -1
  19. package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +5 -5
  20. package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +4 -4
  21. package/dist/models/backend1/django_app/order.schema.json +4 -4
  22. package/dist/models/backend1/django_app/orderitem.schema.json +4 -4
  23. package/dist/models/backend1/django_app/product.schema.json +11 -11
  24. package/dist/models/backend1/django_app/productcategory.schema.json +2 -2
  25. package/dist/models/backend1/django_app/rateplan.schema.json +2 -2
  26. package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +1 -1
  27. package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +4 -4
  28. package/dist/models/default/django_app/comprehensivemodel.schema.json +3 -3
  29. package/dist/models/default/django_app/custompkmodel.schema.json +4 -4
  30. package/dist/models/default/django_app/dailyrate.schema.json +5 -5
  31. package/dist/models/default/django_app/dummymodel.schema.json +2 -2
  32. package/dist/models/default/django_app/index.js +3 -0
  33. package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +118 -0
  34. package/dist/models/default/django_app/m2mdepthtestlevel1.js +71 -0
  35. package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +94 -0
  36. package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +118 -0
  37. package/dist/models/default/django_app/m2mdepthtestlevel2.js +71 -0
  38. package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +94 -0
  39. package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +134 -0
  40. package/dist/models/default/django_app/m2mdepthtestlevel3.js +71 -0
  41. package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +112 -0
  42. package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +2 -2
  43. package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +1 -1
  44. package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +5 -5
  45. package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +4 -4
  46. package/dist/models/default/django_app/order.schema.json +4 -4
  47. package/dist/models/default/django_app/orderitem.schema.json +4 -4
  48. package/dist/models/default/django_app/product.schema.json +11 -11
  49. package/dist/models/default/django_app/productcategory.schema.json +2 -2
  50. package/dist/models/default/django_app/rateplan.schema.json +2 -2
  51. package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +1 -1
  52. package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +4 -4
  53. package/package.json +1 -1
@@ -7,14 +7,7 @@
7
7
  * @returns {string[]} Array of dot-notation paths, e.g. ['author.id','createdAt.year']
8
8
  */
9
9
  export function getRequiredFields(queryBuild: Object, ModelClass: Class): string[];
10
- /**
11
- * Pick out only the required fields from a (possibly nested) model object.
12
- *
13
- * @param {string[]} requiredPaths – e.g. ['id','related.name','related.age']
14
- * @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
15
- * @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
16
- */
17
- export function pickRequiredFields(requiredPaths: string[], instance: Object): Object;
10
+ export function pickRequiredFields(requiredPaths: any, instance: any): {};
18
11
  /**
19
12
  * Filter and order a collection of data objects according to a QuerySet's AST.
20
13
  * This combines getRequiredFields, pickRequiredFields, and processQuery in one function.
@@ -99,6 +99,36 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
99
99
  const relationship = currentModel.relationshipFields.get(part);
100
100
  const relatedModel = relationship.ModelClass();
101
101
  const relationshipType = relationship.relationshipType;
102
+ // If this is not the last part and it's M2M, recursively process the remaining path
103
+ if (!isLastPart && relationshipType === 'many-to-many') {
104
+ // Build the remaining path including any lookup operators
105
+ const remainingFieldParts = fieldParts.slice(i + 1);
106
+ let fullRemainingPath = remainingFieldParts.join('__');
107
+ if (lookupChain.length > 0) {
108
+ fullRemainingPath += '__' + lookupChain.join('__');
109
+ }
110
+ else if (lookup) {
111
+ fullRemainingPath += '__' + lookup;
112
+ }
113
+ // Recursively process the remaining path with the related model
114
+ const innerResult = processFieldPath(fullRemainingPath, value, relatedModel, options);
115
+ // Build the full field path including any FK traversal before this M2M field
116
+ // e.g., for owner__roles__name, processedPath=['owner'], part='roles'
117
+ // so fullFieldPath becomes 'owner.roles'
118
+ const fullFieldPath = processedPath.length > 0
119
+ ? processedPath.join('.') + '.' + part
120
+ : part;
121
+ // Build the required path for data picking (full dot-notation path)
122
+ const innerRequiredPath = innerResult.requiredPath || innerResult.field;
123
+ const requiredPath = `${fullFieldPath}.${innerRequiredPath}`;
124
+ // Wrap the inner result in $elemMatch for this M2M field
125
+ return {
126
+ field: fullFieldPath,
127
+ operator: { $elemMatch: { [innerResult.field]: innerResult.operator } },
128
+ isM2M: true,
129
+ requiredPath // Full path for data picking
130
+ };
131
+ }
102
132
  // Add this relationship field to the path
103
133
  processedPath.push(part);
104
134
  // If this is not the last part, update the current model to the related model
@@ -168,6 +198,10 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
168
198
  if (normalizedValue && typeof normalizedValue === 'object' && 'pk' in normalizedValue) {
169
199
  raw = normalizedValue.pk;
170
200
  }
201
+ // For M2M fields, use $elemMatch to check if any element's pk matches
202
+ if (isM2M) {
203
+ return { field: finalPath, operator: { $elemMatch: { pk: { $eq: raw } } }, isM2M: true };
204
+ }
171
205
  return { field: finalPath, operator: { $eq: raw } };
172
206
  }
173
207
  // Default to direct equality
@@ -217,18 +251,19 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
217
251
  // For relationship fields with lookups, we need special handling
218
252
  if (lookup === 'isnull') {
219
253
  if (isM2M) {
220
- // For M2M fields, isnull=True means null OR empty array, but not undefined because
221
- // that can mean the field was not fetched so we can't be sure
254
+ // For M2M fields, isnull=True means null OR empty array
222
255
  // isnull=False means has at least one item
223
- // Use a custom function since sift doesn't support $or inside field operators
256
+ // Use document-level $where to check the array itself (not iterate elements)
257
+ const m2mField = field;
258
+ const checkEmpty = value;
224
259
  return {
225
- field,
226
- operator: {
227
- $where: function (fieldValue) {
228
- const isEmpty = fieldValue === null ||
229
- (Array.isArray(fieldValue) && fieldValue.length === 0);
230
- return value ? isEmpty : !isEmpty;
231
- }
260
+ field: '$where', // document-level operator
261
+ requiredPath: field, // need the M2M field itself for data picking
262
+ operator: function (doc) {
263
+ const fieldValue = doc[m2mField];
264
+ const isEmpty = fieldValue === null ||
265
+ (Array.isArray(fieldValue) && fieldValue.length === 0);
266
+ return checkEmpty ? isEmpty : !isEmpty;
232
267
  }
233
268
  };
234
269
  }
@@ -239,10 +274,17 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
239
274
  };
240
275
  }
241
276
  else if (lookup === 'in') {
277
+ // For M2M, check if any element's pk is in the provided array
278
+ if (isM2M) {
279
+ return { field, operator: { $elemMatch: { pk: { $in: value } } }, isM2M: true };
280
+ }
242
281
  return { field, operator: { $in: value } };
243
282
  }
244
283
  else {
245
284
  // Default handling for relationship fields
285
+ if (isM2M) {
286
+ return { field, operator: { $elemMatch: { pk: { $eq: value } } }, isM2M: true };
287
+ }
246
288
  return { field, operator: { $eq: value } };
247
289
  }
248
290
  }
@@ -545,10 +587,11 @@ function createFilterWithDateOperations(criteria, ModelClass) {
545
587
  function convertToSiftCriteria(conditions, ModelClass) {
546
588
  const result = {};
547
589
  const datePartFilters = new Map(); // Map to collect date part filters by field
590
+ const m2mConditions = new Map(); // Map to collect M2M $elemMatch conditions by field
548
591
  for (const [key, value] of Object.entries(conditions)) {
549
592
  try {
550
593
  const processedResult = processFieldPath(key, value, ModelClass);
551
- const { field, operator, isDatePart } = processedResult;
594
+ const { field, operator, isDatePart, isM2M } = processedResult;
552
595
  if (isDatePart) {
553
596
  // Handle date part operators separately
554
597
  if (!datePartFilters.has(field)) {
@@ -556,6 +599,13 @@ function convertToSiftCriteria(conditions, ModelClass) {
556
599
  }
557
600
  datePartFilters.get(field).push({ [field]: operator });
558
601
  }
602
+ else if (isM2M && operator.$elemMatch) {
603
+ // Collect M2M conditions to merge into single $elemMatch
604
+ if (!m2mConditions.has(field)) {
605
+ m2mConditions.set(field, []);
606
+ }
607
+ m2mConditions.get(field).push(operator.$elemMatch);
608
+ }
559
609
  else {
560
610
  // For regular operators, merge if we already have criteria for this field
561
611
  if (result[field]) {
@@ -570,6 +620,17 @@ function convertToSiftCriteria(conditions, ModelClass) {
570
620
  throw new Error(`Failed to process field '${key}': ${error.message}`);
571
621
  }
572
622
  }
623
+ // Merge M2M conditions: all conditions on same M2M field go into single $elemMatch
624
+ // so the SAME related object must match ALL conditions
625
+ for (const [field, elemMatchConditions] of m2mConditions.entries()) {
626
+ if (elemMatchConditions.length === 1) {
627
+ result[field] = { $elemMatch: elemMatchConditions[0] };
628
+ }
629
+ else {
630
+ // Multiple conditions - wrap in $and so same element must match all
631
+ result[field] = { $elemMatch: { $and: elemMatchConditions } };
632
+ }
633
+ }
573
634
  // If we have date part filters, combine them with the result
574
635
  if (datePartFilters.size > 0) {
575
636
  const andConditions = [];
@@ -663,6 +724,9 @@ function convertFilterNodeToSiftCriteria(filterNode, ModelClass) {
663
724
  return null;
664
725
  if (childCriteria.length === 1)
665
726
  return childCriteria[0];
727
+ // Chained filters use $and at top level - this gives ANY/ANY semantics for M2M
728
+ // (each $elemMatch can be satisfied by different elements)
729
+ // This matches Django's chained .filter() behavior
666
730
  return { $and: childCriteria };
667
731
  }
668
732
  // For compound OR nodes
@@ -863,12 +927,24 @@ export function getRequiredFields(queryBuild, ModelClass) {
863
927
  function addPath(rawKey) {
864
928
  try {
865
929
  // We pass `null` as the value, since we only care about .field
866
- const { field } = processFieldPath(rawKey, null, ModelClass);
867
- paths.add(field);
930
+ const { field, isM2M, requiredPath } = processFieldPath(rawKey, null, ModelClass);
931
+ // Use requiredPath if available (for M2M traversal), otherwise use field
932
+ // For M2M fields at the end of a path (no requiredPath), we need the pk field
933
+ let finalPath;
934
+ if (requiredPath) {
935
+ finalPath = requiredPath;
936
+ }
937
+ else if (isM2M) {
938
+ finalPath = `${field}.pk`;
939
+ }
940
+ else {
941
+ finalPath = field;
942
+ }
943
+ paths.add(finalPath);
868
944
  }
869
945
  catch (err) {
870
- // if a key doesnt map, warn and skip it
871
- console.warn(`getRequiredFields: couldnt process "${rawKey}": ${err.message}`);
946
+ // if a key doesn't map, warn and skip it
947
+ console.warn(`getRequiredFields: couldn't process "${rawKey}": ${err.message}`);
872
948
  }
873
949
  }
874
950
  // Recursively walk your filter AST
@@ -917,42 +993,48 @@ export function getRequiredFields(queryBuild, ModelClass) {
917
993
  * @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
918
994
  * @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
919
995
  */
996
+ /**
997
+ * Recursively sets a value in a result object following a path.
998
+ * Handles arrays (M2M fields) by mapping over each element.
999
+ */
1000
+ function setNestedValueRecursive(result, source, pathParts) {
1001
+ if (source == null || pathParts.length === 0) {
1002
+ return;
1003
+ }
1004
+ const [current, ...rest] = pathParts;
1005
+ const sourceValue = source[current];
1006
+ if (sourceValue === undefined) {
1007
+ return;
1008
+ }
1009
+ if (rest.length === 0) {
1010
+ // Last part - set the value directly, keeping M2M as full objects
1011
+ result[current] = sourceValue;
1012
+ }
1013
+ else if (Array.isArray(sourceValue)) {
1014
+ // M2M array with nested path - recursively extract from each element
1015
+ if (!(current in result)) {
1016
+ result[current] = [];
1017
+ }
1018
+ sourceValue.forEach((item, idx) => {
1019
+ if (result[current][idx] === undefined) {
1020
+ result[current][idx] = {};
1021
+ }
1022
+ setNestedValueRecursive(result[current][idx], item, rest);
1023
+ });
1024
+ }
1025
+ else if (typeof sourceValue === 'object') {
1026
+ // Regular nested object (FK)
1027
+ if (!(current in result)) {
1028
+ result[current] = {};
1029
+ }
1030
+ setNestedValueRecursive(result[current], sourceValue, rest);
1031
+ }
1032
+ }
920
1033
  export function pickRequiredFields(requiredPaths, instance) {
921
1034
  const result = {};
922
1035
  requiredPaths.forEach(path => {
923
1036
  const parts = path.split('.');
924
- // Traverse the source instance to get the value
925
- let value = instance;
926
- for (const key of parts) {
927
- if (value == null || !(key in value)) {
928
- value = undefined;
929
- break;
930
- }
931
- value = value[key];
932
- }
933
- if (value === undefined) {
934
- // skip missing
935
- return;
936
- }
937
- // Convert M2M arrays from Model instances to PKs for comparison
938
- // This handles the case where the live representation returns [Model1, Model2]
939
- // but we need [pk1, pk2] for sift filtering
940
- if (Array.isArray(value) && value.length > 0 && value[0]?.pk !== undefined) {
941
- value = value.map(item => item.pk);
942
- }
943
- // Build nested structure in the result
944
- let cursor = result;
945
- parts.forEach((key, idx) => {
946
- if (idx === parts.length - 1) {
947
- cursor[key] = value;
948
- }
949
- else {
950
- if (!(key in cursor)) {
951
- cursor[key] = {};
952
- }
953
- cursor = cursor[key];
954
- }
955
- });
1037
+ setNestedValueRecursive(result, instance, parts);
956
1038
  });
957
1039
  return result;
958
1040
  }
@@ -9,8 +9,8 @@
9
9
  "char_field"
10
10
  ],
11
11
  "searchable_fields": [
12
- "char_field",
13
- "text_field"
12
+ "text_field",
13
+ "char_field"
14
14
  ],
15
15
  "ordering_fields": [
16
16
  "int_field"
@@ -105,7 +105,7 @@
105
105
  "format": "date-time",
106
106
  "max_length": null,
107
107
  "choices": null,
108
- "default": "2026-01-18T14:21:49.533255+00:00",
108
+ "default": "2026-01-18T17:06:00.558317+00:00",
109
109
  "validators": [],
110
110
  "max_digits": null,
111
111
  "decimal_places": null,
@@ -5,15 +5,15 @@
5
5
  "plural_title": "Custom Pk Models",
6
6
  "primary_key_field": "custom_pk",
7
7
  "filterable_fields": [
8
- "custom_pk",
9
- "name"
8
+ "name",
9
+ "custom_pk"
10
10
  ],
11
11
  "searchable_fields": [
12
12
  "name"
13
13
  ],
14
14
  "ordering_fields": [
15
- "custom_pk",
16
- "name"
15
+ "name",
16
+ "custom_pk"
17
17
  ],
18
18
  "properties": {
19
19
  "custom_pk": {
@@ -5,16 +5,16 @@
5
5
  "plural_title": "Daily Rates",
6
6
  "primary_key_field": "id",
7
7
  "filterable_fields": [
8
+ "id",
9
+ "date",
8
10
  "rate_plan",
9
- "closed_to_departure",
10
11
  "min_stay_arrival",
11
- "min_stay_through",
12
- "stop_sell",
13
12
  "price",
14
- "date",
15
13
  "max_stay",
14
+ "min_stay_through",
15
+ "stop_sell",
16
16
  "closed_to_arrival",
17
- "id"
17
+ "closed_to_departure"
18
18
  ],
19
19
  "searchable_fields": [],
20
20
  "ordering_fields": [
@@ -5,8 +5,8 @@
5
5
  "plural_title": "Dummy Models",
6
6
  "primary_key_field": "id",
7
7
  "filterable_fields": [
8
- "value",
9
- "name"
8
+ "name",
9
+ "value"
10
10
  ],
11
11
  "searchable_fields": [
12
12
  "name"
@@ -16,3 +16,6 @@ export * from './rateplan';
16
16
  export * from './dailyrate';
17
17
  export * from './restrictedfieldrelatedmodel';
18
18
  export * from './modelwithrestrictedfields';
19
+ export * from './m2mdepthtestlevel3';
20
+ export * from './m2mdepthtestlevel2';
21
+ export * from './m2mdepthtestlevel1';
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Model-specific QuerySet implementation
3
+ */
4
+ export class M2MDepthTestLevel1QuerySet {
5
+ }
6
+ /**
7
+ * Model-specific Manager implementation
8
+ */
9
+ export class M2MDepthTestLevel1Manager {
10
+ constructor(ModelClass: any);
11
+ newQuerySet(): M2MDepthTestLevel1QuerySet;
12
+ }
13
+ /**
14
+ * Implementation of the M2MDepthTestLevel1 model
15
+ */
16
+ export class M2MDepthTestLevel1 {
17
+ static configKey: string;
18
+ static modelName: string;
19
+ static primaryKeyField: string;
20
+ static objects: M2MDepthTestLevel1Manager;
21
+ static fields: string[];
22
+ static schema: {
23
+ model_name: string;
24
+ title: string;
25
+ class_name: string;
26
+ plural_title: string;
27
+ primary_key_field: string;
28
+ filterable_fields: string[];
29
+ searchable_fields: string[];
30
+ ordering_fields: string[];
31
+ properties: {
32
+ id: {
33
+ type: string;
34
+ title: string;
35
+ required: boolean;
36
+ description: null;
37
+ nullable: boolean;
38
+ format: string;
39
+ max_length: null;
40
+ choices: null;
41
+ default: null;
42
+ validators: never[];
43
+ max_digits: null;
44
+ decimal_places: null;
45
+ read_only: boolean;
46
+ ref: null;
47
+ };
48
+ name: {
49
+ type: string;
50
+ title: string;
51
+ required: boolean;
52
+ description: null;
53
+ nullable: boolean;
54
+ format: null;
55
+ max_length: number;
56
+ choices: null;
57
+ default: null;
58
+ validators: never[];
59
+ max_digits: null;
60
+ decimal_places: null;
61
+ read_only: boolean;
62
+ ref: null;
63
+ };
64
+ level2s: {
65
+ type: string;
66
+ title: string;
67
+ required: boolean;
68
+ description: null;
69
+ nullable: boolean;
70
+ format: string;
71
+ max_length: null;
72
+ choices: null;
73
+ default: null;
74
+ validators: never[];
75
+ max_digits: null;
76
+ decimal_places: null;
77
+ read_only: boolean;
78
+ ref: null;
79
+ };
80
+ };
81
+ relationships: {
82
+ level2s: {
83
+ type: string;
84
+ model: string;
85
+ class_name: string;
86
+ primary_key_field: string;
87
+ };
88
+ };
89
+ default_ordering: null;
90
+ definitions: {
91
+ MoneyField: {
92
+ type: string;
93
+ properties: {
94
+ amount: {
95
+ type: string;
96
+ };
97
+ currency: {
98
+ type: string;
99
+ };
100
+ };
101
+ };
102
+ };
103
+ datetime_format: string;
104
+ date_format: string;
105
+ time_format: string;
106
+ display: null;
107
+ };
108
+ static relationshipFields: Map<string, {
109
+ ModelClass: () => any;
110
+ relationshipType: string;
111
+ }>;
112
+ constructor(data: any);
113
+ /**
114
+ * Define property getters and setters for all model fields
115
+ * @private
116
+ */
117
+ private _defineProperties;
118
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ */
4
+ import { Model, Manager, QuerySet, getModelClass } from '../../../../src';
5
+ import { wrapReactiveModel } from '../../../../src';
6
+ import schemaData from './m2mdepthtestlevel1.schema.json';
7
+ /**
8
+ * Model-specific QuerySet implementation
9
+ */
10
+ export class M2MDepthTestLevel1QuerySet extends QuerySet {
11
+ }
12
+ /**
13
+ * Model-specific Manager implementation
14
+ */
15
+ export class M2MDepthTestLevel1Manager extends Manager {
16
+ constructor(ModelClass) {
17
+ super(ModelClass, M2MDepthTestLevel1QuerySet);
18
+ }
19
+ newQuerySet() {
20
+ return new M2MDepthTestLevel1QuerySet(this.ModelClass);
21
+ }
22
+ }
23
+ /**
24
+ * Implementation of the M2MDepthTestLevel1 model
25
+ */
26
+ export class M2MDepthTestLevel1 extends Model {
27
+ constructor(data) {
28
+ M2MDepthTestLevel1.validateFields(data);
29
+ super(data);
30
+ // Define getters and setters for all fields
31
+ this._defineProperties();
32
+ return wrapReactiveModel(this);
33
+ }
34
+ /**
35
+ * Define property getters and setters for all model fields
36
+ * @private
37
+ */
38
+ _defineProperties() {
39
+ // For each field, define a property that gets/sets from internal storage
40
+ M2MDepthTestLevel1.fields.forEach(field => {
41
+ Object.defineProperty(this, field, {
42
+ get: function () {
43
+ return this.getField(field);
44
+ },
45
+ set: function (value) {
46
+ this.setField(field, value);
47
+ },
48
+ enumerable: true, // Make sure fields are enumerable for serialization
49
+ configurable: true
50
+ });
51
+ });
52
+ // Add a special read-only getter for the repr field
53
+ Object.defineProperty(this, 'repr', {
54
+ get: function () {
55
+ return this.getField('repr');
56
+ },
57
+ enumerable: true, // Make sure repr is enumerable
58
+ configurable: true
59
+ });
60
+ }
61
+ }
62
+ // Bind this model to its backend
63
+ M2MDepthTestLevel1.configKey = 'backend1';
64
+ M2MDepthTestLevel1.modelName = 'django_app.m2mdepthtestlevel1';
65
+ M2MDepthTestLevel1.primaryKeyField = 'id';
66
+ M2MDepthTestLevel1.objects = new M2MDepthTestLevel1Manager(M2MDepthTestLevel1);
67
+ M2MDepthTestLevel1.fields = ['id', 'name', 'level2s'];
68
+ M2MDepthTestLevel1.schema = schemaData;
69
+ M2MDepthTestLevel1.relationshipFields = new Map([
70
+ ['level2s', { 'ModelClass': () => getModelClass('django_app.m2mdepthtestlevel2', 'backend1'), 'relationshipType': 'many-to-many' }]
71
+ ]);
@@ -0,0 +1,94 @@
1
+ {
2
+ "model_name": "django_app.m2mdepthtestlevel1",
3
+ "title": "M2M Depth Test Level1",
4
+ "class_name": "M2MDepthTestLevel1",
5
+ "plural_title": "M2M Depth Test Level1S",
6
+ "primary_key_field": "id",
7
+ "filterable_fields": [
8
+ "name",
9
+ "level2s",
10
+ "id"
11
+ ],
12
+ "searchable_fields": [
13
+ "name"
14
+ ],
15
+ "ordering_fields": [
16
+ "name"
17
+ ],
18
+ "properties": {
19
+ "id": {
20
+ "type": "integer",
21
+ "title": "Id",
22
+ "required": true,
23
+ "description": null,
24
+ "nullable": false,
25
+ "format": "id",
26
+ "max_length": null,
27
+ "choices": null,
28
+ "default": null,
29
+ "validators": [],
30
+ "max_digits": null,
31
+ "decimal_places": null,
32
+ "read_only": true,
33
+ "ref": null
34
+ },
35
+ "name": {
36
+ "type": "string",
37
+ "title": "Name",
38
+ "required": true,
39
+ "description": null,
40
+ "nullable": false,
41
+ "format": null,
42
+ "max_length": 100,
43
+ "choices": null,
44
+ "default": null,
45
+ "validators": [],
46
+ "max_digits": null,
47
+ "decimal_places": null,
48
+ "read_only": false,
49
+ "ref": null
50
+ },
51
+ "level2s": {
52
+ "type": "array",
53
+ "title": "Level2s",
54
+ "required": true,
55
+ "description": null,
56
+ "nullable": false,
57
+ "format": "many-to-many",
58
+ "max_length": null,
59
+ "choices": null,
60
+ "default": null,
61
+ "validators": [],
62
+ "max_digits": null,
63
+ "decimal_places": null,
64
+ "read_only": false,
65
+ "ref": null
66
+ }
67
+ },
68
+ "relationships": {
69
+ "level2s": {
70
+ "type": "many-to-many",
71
+ "model": "django_app.m2mdepthtestlevel2",
72
+ "class_name": "M2MDepthTestLevel2",
73
+ "primary_key_field": "id"
74
+ }
75
+ },
76
+ "default_ordering": null,
77
+ "definitions": {
78
+ "MoneyField": {
79
+ "type": "object",
80
+ "properties": {
81
+ "amount": {
82
+ "type": "number"
83
+ },
84
+ "currency": {
85
+ "type": "string"
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "datetime_format": "iso-8601",
91
+ "date_format": "iso-8601",
92
+ "time_format": "iso-8601",
93
+ "display": null
94
+ }