@statezero/core 0.2.23 → 0.2.25

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 +123 -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,30 @@ 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 required path for data picking (full dot-notation path)
116
+ const innerRequiredPath = innerResult.requiredPath || innerResult.field;
117
+ const requiredPath = `${part}.${innerRequiredPath}`;
118
+ // Wrap the inner result in $elemMatch for this M2M field
119
+ return {
120
+ field: part,
121
+ operator: { $elemMatch: { [innerResult.field]: innerResult.operator } },
122
+ isM2M: true,
123
+ requiredPath // Full path for data picking
124
+ };
125
+ }
102
126
  // Add this relationship field to the path
103
127
  processedPath.push(part);
104
128
  // If this is not the last part, update the current model to the related model
@@ -168,6 +192,10 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
168
192
  if (normalizedValue && typeof normalizedValue === 'object' && 'pk' in normalizedValue) {
169
193
  raw = normalizedValue.pk;
170
194
  }
195
+ // For M2M fields, use $elemMatch to check if any element's pk matches
196
+ if (isM2M) {
197
+ return { field: finalPath, operator: { $elemMatch: { pk: { $eq: raw } } }, isM2M: true };
198
+ }
171
199
  return { field: finalPath, operator: { $eq: raw } };
172
200
  }
173
201
  // Default to direct equality
@@ -217,18 +245,19 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
217
245
  // For relationship fields with lookups, we need special handling
218
246
  if (lookup === 'isnull') {
219
247
  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
248
+ // For M2M fields, isnull=True means null OR empty array
222
249
  // isnull=False means has at least one item
223
- // Use a custom function since sift doesn't support $or inside field operators
250
+ // Use document-level $where to check the array itself (not iterate elements)
251
+ const m2mField = field;
252
+ const checkEmpty = value;
224
253
  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
- }
254
+ field: '$where', // document-level operator
255
+ requiredPath: field, // need the M2M field itself for data picking
256
+ operator: function (doc) {
257
+ const fieldValue = doc[m2mField];
258
+ const isEmpty = fieldValue === null ||
259
+ (Array.isArray(fieldValue) && fieldValue.length === 0);
260
+ return checkEmpty ? isEmpty : !isEmpty;
232
261
  }
233
262
  };
234
263
  }
@@ -239,10 +268,17 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
239
268
  };
240
269
  }
241
270
  else if (lookup === 'in') {
271
+ // For M2M, check if any element's pk is in the provided array
272
+ if (isM2M) {
273
+ return { field, operator: { $elemMatch: { pk: { $in: value } } }, isM2M: true };
274
+ }
242
275
  return { field, operator: { $in: value } };
243
276
  }
244
277
  else {
245
278
  // Default handling for relationship fields
279
+ if (isM2M) {
280
+ return { field, operator: { $elemMatch: { pk: { $eq: value } } }, isM2M: true };
281
+ }
246
282
  return { field, operator: { $eq: value } };
247
283
  }
248
284
  }
@@ -545,10 +581,11 @@ function createFilterWithDateOperations(criteria, ModelClass) {
545
581
  function convertToSiftCriteria(conditions, ModelClass) {
546
582
  const result = {};
547
583
  const datePartFilters = new Map(); // Map to collect date part filters by field
584
+ const m2mConditions = new Map(); // Map to collect M2M $elemMatch conditions by field
548
585
  for (const [key, value] of Object.entries(conditions)) {
549
586
  try {
550
587
  const processedResult = processFieldPath(key, value, ModelClass);
551
- const { field, operator, isDatePart } = processedResult;
588
+ const { field, operator, isDatePart, isM2M } = processedResult;
552
589
  if (isDatePart) {
553
590
  // Handle date part operators separately
554
591
  if (!datePartFilters.has(field)) {
@@ -556,6 +593,13 @@ function convertToSiftCriteria(conditions, ModelClass) {
556
593
  }
557
594
  datePartFilters.get(field).push({ [field]: operator });
558
595
  }
596
+ else if (isM2M && operator.$elemMatch) {
597
+ // Collect M2M conditions to merge into single $elemMatch
598
+ if (!m2mConditions.has(field)) {
599
+ m2mConditions.set(field, []);
600
+ }
601
+ m2mConditions.get(field).push(operator.$elemMatch);
602
+ }
559
603
  else {
560
604
  // For regular operators, merge if we already have criteria for this field
561
605
  if (result[field]) {
@@ -570,6 +614,17 @@ function convertToSiftCriteria(conditions, ModelClass) {
570
614
  throw new Error(`Failed to process field '${key}': ${error.message}`);
571
615
  }
572
616
  }
617
+ // Merge M2M conditions: all conditions on same M2M field go into single $elemMatch
618
+ // so the SAME related object must match ALL conditions
619
+ for (const [field, elemMatchConditions] of m2mConditions.entries()) {
620
+ if (elemMatchConditions.length === 1) {
621
+ result[field] = { $elemMatch: elemMatchConditions[0] };
622
+ }
623
+ else {
624
+ // Multiple conditions - wrap in $and so same element must match all
625
+ result[field] = { $elemMatch: { $and: elemMatchConditions } };
626
+ }
627
+ }
573
628
  // If we have date part filters, combine them with the result
574
629
  if (datePartFilters.size > 0) {
575
630
  const andConditions = [];
@@ -663,6 +718,9 @@ function convertFilterNodeToSiftCriteria(filterNode, ModelClass) {
663
718
  return null;
664
719
  if (childCriteria.length === 1)
665
720
  return childCriteria[0];
721
+ // Chained filters use $and at top level - this gives ANY/ANY semantics for M2M
722
+ // (each $elemMatch can be satisfied by different elements)
723
+ // This matches Django's chained .filter() behavior
666
724
  return { $and: childCriteria };
667
725
  }
668
726
  // For compound OR nodes
@@ -863,12 +921,24 @@ export function getRequiredFields(queryBuild, ModelClass) {
863
921
  function addPath(rawKey) {
864
922
  try {
865
923
  // We pass `null` as the value, since we only care about .field
866
- const { field } = processFieldPath(rawKey, null, ModelClass);
867
- paths.add(field);
924
+ const { field, isM2M, requiredPath } = processFieldPath(rawKey, null, ModelClass);
925
+ // Use requiredPath if available (for M2M traversal), otherwise use field
926
+ // For M2M fields at the end of a path (no requiredPath), we need the pk field
927
+ let finalPath;
928
+ if (requiredPath) {
929
+ finalPath = requiredPath;
930
+ }
931
+ else if (isM2M) {
932
+ finalPath = `${field}.pk`;
933
+ }
934
+ else {
935
+ finalPath = field;
936
+ }
937
+ paths.add(finalPath);
868
938
  }
869
939
  catch (err) {
870
- // if a key doesnt map, warn and skip it
871
- console.warn(`getRequiredFields: couldnt process "${rawKey}": ${err.message}`);
940
+ // if a key doesn't map, warn and skip it
941
+ console.warn(`getRequiredFields: couldn't process "${rawKey}": ${err.message}`);
872
942
  }
873
943
  }
874
944
  // Recursively walk your filter AST
@@ -917,42 +987,48 @@ export function getRequiredFields(queryBuild, ModelClass) {
917
987
  * @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
918
988
  * @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
919
989
  */
990
+ /**
991
+ * Recursively sets a value in a result object following a path.
992
+ * Handles arrays (M2M fields) by mapping over each element.
993
+ */
994
+ function setNestedValueRecursive(result, source, pathParts) {
995
+ if (source == null || pathParts.length === 0) {
996
+ return;
997
+ }
998
+ const [current, ...rest] = pathParts;
999
+ const sourceValue = source[current];
1000
+ if (sourceValue === undefined) {
1001
+ return;
1002
+ }
1003
+ if (rest.length === 0) {
1004
+ // Last part - set the value directly, keeping M2M as full objects
1005
+ result[current] = sourceValue;
1006
+ }
1007
+ else if (Array.isArray(sourceValue)) {
1008
+ // M2M array with nested path - recursively extract from each element
1009
+ if (!(current in result)) {
1010
+ result[current] = [];
1011
+ }
1012
+ sourceValue.forEach((item, idx) => {
1013
+ if (result[current][idx] === undefined) {
1014
+ result[current][idx] = {};
1015
+ }
1016
+ setNestedValueRecursive(result[current][idx], item, rest);
1017
+ });
1018
+ }
1019
+ else if (typeof sourceValue === 'object') {
1020
+ // Regular nested object (FK)
1021
+ if (!(current in result)) {
1022
+ result[current] = {};
1023
+ }
1024
+ setNestedValueRecursive(result[current], sourceValue, rest);
1025
+ }
1026
+ }
920
1027
  export function pickRequiredFields(requiredPaths, instance) {
921
1028
  const result = {};
922
1029
  requiredPaths.forEach(path => {
923
1030
  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
- });
1031
+ setNestedValueRecursive(result, instance, parts);
956
1032
  });
957
1033
  return result;
958
1034
  }
@@ -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
+ }