GeneralManager 0.5.1__py3-none-any.whl → 0.6.0__py3-none-any.whl

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.
@@ -1,522 +1,14 @@
1
1
  from __future__ import annotations
2
- import json
3
2
  from typing import (
4
3
  Type,
5
- ClassVar,
6
4
  Any,
7
- Callable,
8
- TYPE_CHECKING,
9
- Generator,
10
- TypeVar,
11
- cast,
12
5
  )
13
6
  from django.db import models, transaction
14
- from django.contrib.auth import get_user_model
15
7
  from simple_history.utils import update_change_reason # type: ignore
16
- from datetime import datetime, timedelta
17
- from simple_history.models import HistoricalRecords # type: ignore
18
- from general_manager.measurement.measurement import Measurement
19
- from general_manager.measurement.measurementField import MeasurementField
20
- from decimal import Decimal
21
- from general_manager.factory.autoFactory import AutoFactory
22
- from django.core.exceptions import ValidationError
23
- from general_manager.interface.baseInterface import (
24
- InterfaceBase,
25
- Bucket,
26
- classPostCreationMethod,
27
- classPreCreationMethod,
28
- generalManagerClassName,
29
- attributes,
30
- interfaceBaseClass,
31
- newlyCreatedGeneralManagerClass,
32
- newlyCreatedInterfaceClass,
33
- relatedClass,
34
- GeneralManagerType,
35
- AttributeTypedDict,
8
+ from general_manager.interface.databaseBasedInterface import (
9
+ DBBasedInterface,
10
+ GeneralManagerModel,
36
11
  )
37
- from general_manager.manager.input import Input
38
-
39
- if TYPE_CHECKING:
40
- from general_manager.manager.generalManager import GeneralManager
41
- from general_manager.manager.meta import GeneralManagerMeta
42
- from django.contrib.auth.models import AbstractUser
43
- from general_manager.rule.rule import Rule
44
-
45
- modelsModel = TypeVar("modelsModel", bound=models.Model)
46
-
47
-
48
- def getFullCleanMethode(model: Type[models.Model]) -> Callable[..., None]:
49
- def full_clean(self: models.Model, *args: Any, **kwargs: Any):
50
- errors: dict[str, Any] = {}
51
- try:
52
- super(model, self).full_clean(*args, **kwargs) # type: ignore
53
- except ValidationError as e:
54
- errors.update(e.message_dict)
55
-
56
- rules: list[Rule] = getattr(self._meta, "rules")
57
- for rule in rules:
58
- if not rule.evaluate(self):
59
- error_message = rule.getErrorMessage()
60
- if error_message:
61
- errors.update(error_message)
62
-
63
- if errors:
64
- raise ValidationError(errors)
65
-
66
- return full_clean
67
-
68
-
69
- class GeneralManagerModel(models.Model):
70
- _general_manager_class: ClassVar[Type[GeneralManager]]
71
- is_active = models.BooleanField(default=True)
72
- changed_by = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
73
- changed_by_id: int
74
- history = HistoricalRecords(inherit=True)
75
-
76
- @property
77
- def _history_user(self) -> AbstractUser:
78
- return self.changed_by
79
-
80
- @_history_user.setter
81
- def _history_user(self, value: AbstractUser) -> None:
82
- self.changed_by = value
83
-
84
- class Meta:
85
- abstract = True
86
-
87
-
88
- class DBBasedInterface(InterfaceBase):
89
- _model: ClassVar[Type[GeneralManagerModel]]
90
- input_fields: dict[str, Input] = {"id": Input(int)}
91
-
92
- def __init__(
93
- self,
94
- *args: list[Any],
95
- search_date: datetime | None = None,
96
- **kwargs: dict[str, Any],
97
- ):
98
- super().__init__(*args, **kwargs)
99
- self.pk = self.identification["id"]
100
- self._instance = self.getData(search_date)
101
-
102
- def getData(self, search_date: datetime | None = None) -> GeneralManagerModel:
103
- model = self._model
104
- instance = model.objects.get(pk=self.pk)
105
- if search_date and not search_date > datetime.now() - timedelta(seconds=5):
106
- instance = self.getHistoricalRecord(instance, search_date)
107
- return instance
108
-
109
- @classmethod
110
- def filter(cls, **kwargs: Any) -> DatabaseBucket:
111
- return DatabaseBucket(
112
- cls._model.objects.filter(**kwargs),
113
- cls._parent_class,
114
- cls.__createFilterDefinitions(**kwargs),
115
- )
116
-
117
- @classmethod
118
- def exclude(cls, **kwargs: Any) -> DatabaseBucket:
119
- return DatabaseBucket(
120
- cls._model.objects.exclude(**kwargs),
121
- cls._parent_class,
122
- cls.__createFilterDefinitions(**kwargs),
123
- )
124
-
125
- @staticmethod
126
- def __createFilterDefinitions(**kwargs: Any) -> dict[str, Any]:
127
- filter_definitions: dict[str, Any] = {}
128
- for key, value in kwargs.items():
129
- filter_definitions[key] = value
130
- return filter_definitions
131
-
132
- @classmethod
133
- def getHistoricalRecord(
134
- cls, instance: GeneralManagerModel, search_date: datetime | None = None
135
- ) -> GeneralManagerModel:
136
- return instance.history.filter(history_date__lte=search_date).last() # type: ignore
137
-
138
- @classmethod
139
- def getAttributeTypes(cls) -> dict[str, AttributeTypedDict]:
140
- TRANSLATION: dict[Type[models.Field[Any, Any]], type] = {
141
- models.fields.BigAutoField: int,
142
- models.AutoField: int,
143
- models.CharField: str,
144
- models.TextField: str,
145
- models.BooleanField: bool,
146
- models.IntegerField: int,
147
- models.FloatField: float,
148
- models.DateField: datetime,
149
- models.DateTimeField: datetime,
150
- MeasurementField: Measurement,
151
- models.DecimalField: Decimal,
152
- models.EmailField: str,
153
- models.FileField: str,
154
- models.ImageField: str,
155
- models.URLField: str,
156
- models.TimeField: datetime,
157
- }
158
- fields: dict[str, AttributeTypedDict] = {}
159
- field_name_list, to_ignore_list = cls.handleCustomFields(cls._model)
160
- for field_name in field_name_list:
161
- field: models.Field = getattr(cls._model, field_name)
162
- fields[field_name] = {
163
- "type": type(field),
164
- "is_required": not field.null,
165
- "is_editable": field.editable,
166
- "default": field.default,
167
- }
168
-
169
- for field_name in cls.__getModelFields():
170
- if field_name not in to_ignore_list:
171
- field: models.Field = getattr(cls._model, field_name).field
172
- fields[field_name] = {
173
- "type": type(field),
174
- "is_required": not field.null,
175
- "is_editable": field.editable,
176
- "default": field.default,
177
- }
178
-
179
- for field_name in cls.__getForeignKeyFields():
180
- field = cls._model._meta.get_field(field_name)
181
- related_model = field.related_model
182
- if related_model and hasattr(
183
- related_model,
184
- "_general_manager_class",
185
- ):
186
- fields[field_name] = {
187
- "type": related_model._general_manager_class,
188
- "is_required": not field.null,
189
- "is_editable": field.editable,
190
- "default": field.default,
191
- }
192
- elif related_model is not None:
193
- fields[field_name] = {
194
- "type": related_model,
195
- "is_required": not field.null,
196
- "is_editable": field.editable,
197
- "default": field.default,
198
- }
199
-
200
- for field_name, field_call in [
201
- *cls.__getManyToManyFields(),
202
- *cls.__getReverseRelations(),
203
- ]:
204
- if field_name in fields.keys():
205
- if field_call not in fields.keys():
206
- field_name = field_call
207
- else:
208
- raise ValueError("Field name already exists.")
209
- related_model = cls._model._meta.get_field(field_name).related_model
210
- if related_model and hasattr(
211
- related_model,
212
- "_general_manager_class",
213
- ):
214
- fields[f"{field_name}_list"] = {
215
- "type": related_model._general_manager_class,
216
- "is_required": False,
217
- "is_editable": False,
218
- "default": None,
219
- }
220
- elif related_model is not None:
221
- fields[f"{field_name}_list"] = {
222
- "type": related_model,
223
- "is_required": False,
224
- "is_editable": False,
225
- "default": None,
226
- }
227
-
228
- return {
229
- field_name: {**field, "type": TRANSLATION.get(field["type"], field["type"])}
230
- for field_name, field in fields.items()
231
- }
232
-
233
- @classmethod
234
- def getAttributes(cls) -> dict[str, Any]:
235
- field_values: dict[str, Any] = {}
236
-
237
- field_name_list, to_ignore_list = cls.handleCustomFields(cls._model)
238
- for field_name in field_name_list:
239
- field_values[field_name] = lambda self, field_name=field_name: getattr(
240
- self._instance, field_name
241
- )
242
-
243
- for field_name in cls.__getModelFields():
244
- if field_name not in to_ignore_list:
245
- field_values[field_name] = lambda self, field_name=field_name: getattr(
246
- self._instance, field_name
247
- )
248
-
249
- for field_name in cls.__getForeignKeyFields():
250
- related_model = cls._model._meta.get_field(field_name).related_model
251
- if related_model and hasattr(
252
- related_model,
253
- "_general_manager_class",
254
- ):
255
- generalManagerClass = related_model._general_manager_class
256
- field_values[f"{field_name}"] = (
257
- lambda self, field_name=field_name: generalManagerClass(
258
- getattr(self._instance, field_name).pk
259
- )
260
- )
261
- else:
262
- field_values[f"{field_name}"] = (
263
- lambda self, field_name=field_name: getattr(
264
- self._instance, field_name
265
- )
266
- )
267
-
268
- for field_name, field_call in [
269
- *cls.__getManyToManyFields(),
270
- *cls.__getReverseRelations(),
271
- ]:
272
- if field_name in field_values.keys():
273
- if field_call not in field_values.keys():
274
- field_name = field_call
275
- else:
276
- raise ValueError("Field name already exists.")
277
- if hasattr(
278
- cls._model._meta.get_field(field_name).related_model,
279
- "_general_manager_class",
280
- ):
281
- field_values[
282
- f"{field_name}_list"
283
- ] = lambda self, field_name=field_name: self._instance._meta.get_field(
284
- field_name
285
- ).related_model._general_manager_class.filter(
286
- **{self._instance.__class__.__name__.lower(): self.pk}
287
- )
288
- else:
289
- field_values[f"{field_name}_list"] = (
290
- lambda self, field_call=field_call: getattr(
291
- self._instance, field_call
292
- ).all()
293
- )
294
- return field_values
295
-
296
- @staticmethod
297
- def handleCustomFields(
298
- model: Type[models.Model] | models.Model,
299
- ) -> tuple[list[str], list[str]]:
300
- field_name_list: list[str] = []
301
- to_ignore_list: list[str] = []
302
- for field_name in DBBasedInterface._getCustomFields(model):
303
- to_ignore_list.append(f"{field_name}_value")
304
- to_ignore_list.append(f"{field_name}_unit")
305
- field_name_list.append(field_name)
306
-
307
- return field_name_list, to_ignore_list
308
-
309
- @staticmethod
310
- def _getCustomFields(model: Type[models.Model] | models.Model) -> list[str]:
311
- return [
312
- field.name
313
- for field in model.__dict__.values()
314
- if isinstance(field, models.Field)
315
- ]
316
-
317
- @classmethod
318
- def __getModelFields(cls):
319
- return [
320
- field.name
321
- for field in cls._model._meta.get_fields()
322
- if not field.many_to_many and not field.related_model
323
- ]
324
-
325
- @classmethod
326
- def __getForeignKeyFields(cls):
327
- return [
328
- field.name
329
- for field in cls._model._meta.get_fields()
330
- if field.is_relation and (field.many_to_one or field.one_to_one)
331
- ]
332
-
333
- @classmethod
334
- def __getManyToManyFields(cls):
335
- return [
336
- (field.name, field.name)
337
- for field in cls._model._meta.get_fields()
338
- if field.is_relation and field.many_to_many
339
- ]
340
-
341
- @classmethod
342
- def __getReverseRelations(cls):
343
- return [
344
- (field.name, f"{field.name}_set")
345
- for field in cls._model._meta.get_fields()
346
- if field.is_relation and field.one_to_many
347
- ]
348
-
349
- @staticmethod
350
- def _preCreate(
351
- name: generalManagerClassName, attrs: attributes, interface: interfaceBaseClass
352
- ) -> tuple[attributes, interfaceBaseClass, relatedClass]:
353
- # Felder aus der Interface-Klasse sammeln
354
- model_fields: dict[str, Any] = {}
355
- meta_class = None
356
- for attr_name, attr_value in interface.__dict__.items():
357
- if not attr_name.startswith("__"):
358
- if attr_name == "Meta" and isinstance(attr_value, type):
359
- # Meta-Klasse speichern
360
- meta_class = attr_value
361
- elif attr_name == "Factory":
362
- # Factory nicht in model_fields speichern
363
- pass
364
- else:
365
- model_fields[attr_name] = attr_value
366
- model_fields["__module__"] = attrs.get("__module__")
367
- # Meta-Klasse hinzufügen oder erstellen
368
- rules: list[Rule] | None = None
369
- if meta_class:
370
- model_fields["Meta"] = meta_class
371
-
372
- if hasattr(meta_class, "rules"):
373
- rules = getattr(meta_class, "rules")
374
- delattr(meta_class, "rules")
375
-
376
- # Modell erstellen
377
- model = type(name, (GeneralManagerModel,), model_fields)
378
- if meta_class and rules:
379
- setattr(model._meta, "rules", rules)
380
- # full_clean Methode hinzufügen
381
- model.full_clean = getFullCleanMethode(model)
382
- # Interface-Typ bestimmen
383
- attrs["_interface_type"] = interface._interface_type
384
- interface_cls = type(interface.__name__, (interface,), {})
385
- setattr(interface_cls, "_model", model)
386
- attrs["Interface"] = interface_cls
387
-
388
- # add factory class
389
- factory_definition = getattr(interface, "Factory", None)
390
- factory_attributes: dict[str, Any] = {}
391
- if factory_definition:
392
- for attr_name, attr_value in factory_definition.__dict__.items():
393
- if not attr_name.startswith("__"):
394
- factory_attributes[attr_name] = attr_value
395
- factory_attributes["interface"] = interface_cls
396
- factory_attributes["Meta"] = type("Meta", (), {"model": model})
397
- factory_class = type(f"{name}Factory", (AutoFactory,), factory_attributes)
398
- # factory_class._meta.model = model
399
- attrs["Factory"] = factory_class
400
-
401
- return attrs, interface_cls, model
402
-
403
- @staticmethod
404
- def _postCreate(
405
- new_class: newlyCreatedGeneralManagerClass,
406
- interface_class: newlyCreatedInterfaceClass,
407
- model: relatedClass,
408
- ) -> None:
409
- interface_class._parent_class = new_class
410
- setattr(model, "_general_manager_class", new_class)
411
-
412
- @classmethod
413
- def handleInterface(
414
- cls,
415
- ) -> tuple[classPreCreationMethod, classPostCreationMethod]:
416
- """
417
- This method returns a pre and a post GeneralManager creation method
418
- and is called inside the GeneralManagerMeta class to initialize the
419
- Interface.
420
- The pre creation method is called before the GeneralManager instance
421
- is created to modify the kwargs.
422
- The post creation method is called after the GeneralManager instance
423
- is created to modify the instance and add additional data.
424
- """
425
- return cls._preCreate, cls._postCreate
426
-
427
- @classmethod
428
- def getFieldType(cls, field_name: str) -> type:
429
- """
430
- This method returns the field type for the given field name.
431
- """
432
- field = cls._model._meta.get_field(field_name)
433
- if field.is_relation and field.related_model:
434
- return field.related_model._general_manager_class
435
- return type(field)
436
-
437
-
438
- class ReadOnlyInterface(DBBasedInterface):
439
- _interface_type = "readonly"
440
-
441
- @classmethod
442
- def sync_data(cls) -> None:
443
- model: Type[models.Model] | None = getattr(cls, "_model", None)
444
- parent_class = getattr(cls, "_parent_class", None)
445
- if model is None or parent_class is None:
446
- raise ValueError("Attribute '_model' and '_parent_class' must be set.")
447
- json_data = getattr(parent_class, "_json_data", None)
448
- if not json_data:
449
- raise ValueError(
450
- f"For ReadOnlyInterface '{parent_class.__name__}' must be set '_json_data'"
451
- )
452
-
453
- # JSON-Daten parsen
454
- if isinstance(json_data, str):
455
- data_list = json.loads(json_data)
456
- if isinstance(json_data, list):
457
- data_list: list[Any] = json_data
458
- else:
459
- raise ValueError(
460
- "_json_data must be a JSON string or a list of dictionaries"
461
- )
462
-
463
- unique_fields = getattr(parent_class, "_unique_fields", [])
464
- if not unique_fields:
465
- raise ValueError(
466
- f"For ReadOnlyInterface '{parent_class.__name__}' must be defined '_unique_fields'"
467
- )
468
-
469
- with transaction.atomic():
470
- json_unique_values: set[Any] = set()
471
-
472
- # Daten synchronisieren
473
- for data in data_list:
474
- lookup = {field: data[field] for field in unique_fields}
475
- unique_identifier = tuple(lookup[field] for field in unique_fields)
476
- json_unique_values.add(unique_identifier)
477
-
478
- instance, _ = model.objects.get_or_create(**lookup)
479
- updated = False
480
- for field_name, value in data.items():
481
- if getattr(instance, field_name, None) != value:
482
- setattr(instance, field_name, value)
483
- updated = True
484
- if updated:
485
- instance.save()
486
-
487
- # Existierende Einträge abrufen und löschen, wenn nicht im JSON vorhanden
488
- existing_instances = model.objects.all()
489
- for instance in existing_instances:
490
- lookup = {field: getattr(instance, field) for field in unique_fields}
491
- unique_identifier = tuple(lookup[field] for field in unique_fields)
492
- if unique_identifier not in json_unique_values:
493
- instance.delete()
494
-
495
- @staticmethod
496
- def readOnlyPostCreate(func: Callable[..., Any]) -> Callable[..., Any]:
497
- def wrapper(
498
- mcs: Type[GeneralManagerMeta],
499
- new_class: Type[GeneralManager],
500
- interface_cls: Type[ReadOnlyInterface],
501
- model: Type[GeneralManagerModel],
502
- ):
503
- func(mcs, new_class, interface_cls, model)
504
- mcs.read_only_classes.append(interface_cls)
505
-
506
- return wrapper
507
-
508
- @classmethod
509
- def handleInterface(cls) -> tuple[classPreCreationMethod, classPostCreationMethod]:
510
- """
511
- This method returns a pre and a post GeneralManager creation method
512
- and is called inside the GeneralManagerMeta class to initialize the
513
- Interface.
514
- The pre creation method is called before the GeneralManager instance
515
- is created to modify the kwargs.
516
- The post creation method is called after the GeneralManager instance
517
- is created to modify the instance and add additional data.
518
- """
519
- return cls._preCreate, cls.readOnlyPostCreate(cls._postCreate)
520
12
 
521
13
 
522
14
  class DatabaseInterface(DBBasedInterface):
@@ -592,6 +84,19 @@ class DatabaseInterface(DBBasedInterface):
592
84
  def __save_with_history(
593
85
  cls, instance: GeneralManagerModel, creator_id: int, history_comment: str | None
594
86
  ) -> int:
87
+ """
88
+ Saves a model instance with validation and optional history tracking.
89
+
90
+ Sets the `changed_by_id` field, validates the instance, applies a history comment if provided, and saves the instance within an atomic transaction.
91
+
92
+ Args:
93
+ instance: The model instance to save.
94
+ creator_id: The ID of the user making the change.
95
+ history_comment: Optional comment describing the reason for the change.
96
+
97
+ Returns:
98
+ The primary key of the saved instance.
99
+ """
595
100
  instance.changed_by_id = creator_id
596
101
  instance.full_clean()
597
102
  if history_comment:
@@ -599,147 +104,3 @@ class DatabaseInterface(DBBasedInterface):
599
104
  instance.save()
600
105
 
601
106
  return instance.pk
602
-
603
-
604
- class DatabaseBucket(Bucket[GeneralManagerType]):
605
-
606
- def __init__(
607
- self,
608
- data: models.QuerySet[modelsModel],
609
- manager_class: Type[GeneralManagerType],
610
- filter_definitions: dict[str, list[Any]] = {},
611
- exclude_definitions: dict[str, list[Any]] = {},
612
- ):
613
- if data is None:
614
- data = manager_class.filter(**filter_definitions).exclude(
615
- **exclude_definitions
616
- )
617
- self._data = data
618
-
619
- self._manager_class = manager_class
620
- self.filters = {**filter_definitions}
621
- self.excludes = {**exclude_definitions}
622
-
623
- def __iter__(self) -> Generator[GeneralManagerType]:
624
- for item in self._data:
625
- yield self._manager_class(item.pk)
626
-
627
- def __or__(
628
- self,
629
- other: Bucket[GeneralManagerType] | GeneralManager[GeneralManagerType],
630
- ) -> DatabaseBucket[GeneralManagerType]:
631
- from general_manager.manager.generalManager import GeneralManager
632
-
633
- if isinstance(other, GeneralManager) and other.__class__ == self._manager_class:
634
- return self.__or__(self.filter(id__in=[getattr(other, "id")]))
635
- if not isinstance(other, self.__class__):
636
- raise ValueError("Cannot combine different bucket types")
637
- if self._manager_class != other._manager_class:
638
- raise ValueError("Cannot combine different bucket managers")
639
- return self.__class__(
640
- self._data | other._data,
641
- self._manager_class,
642
- {},
643
- )
644
-
645
- def __mergeFilterDefinitions(
646
- self, basis: dict[str, list[Any]], **kwargs: Any
647
- ) -> dict[str, list[Any]]:
648
- """
649
- Combines existing filter definitions with additional keyword arguments.
650
-
651
- Args:
652
- basis: Dictionary mapping filter keys to lists of values. Additional keyword arguments are merged into this dictionary.
653
-
654
- Returns:
655
- A dictionary where each key maps to a list containing all values from both the original basis and the new keyword arguments.
656
- """
657
- kwarg_filter: dict[str, list[Any]] = {}
658
- for key, value in basis.items():
659
- kwarg_filter[key] = value
660
- for key, value in kwargs.items():
661
- if key not in kwarg_filter:
662
- kwarg_filter[key] = []
663
- kwarg_filter[key].append(value)
664
- return kwarg_filter
665
-
666
- def filter(self, **kwargs: Any) -> DatabaseBucket[GeneralManagerType]:
667
- """
668
- Returns a new bucket with manager instances matching the specified filter criteria.
669
-
670
- Additional filter keyword arguments are merged with any existing filters to further restrict the queryset.
671
- """
672
- merged_filter = self.__mergeFilterDefinitions(self.filters, **kwargs)
673
- return self.__class__(
674
- self._data.filter(**kwargs),
675
- self._manager_class,
676
- merged_filter,
677
- self.excludes,
678
- )
679
-
680
- def exclude(self, **kwargs: Any) -> DatabaseBucket[GeneralManagerType]:
681
- """
682
- Returns a new DatabaseBucket excluding items that match the specified criteria.
683
-
684
- Keyword arguments specify field lookups to exclude from the queryset. The resulting bucket contains only items that do not match these filters.
685
- """
686
- merged_exclude = self.__mergeFilterDefinitions(self.excludes, **kwargs)
687
- return self.__class__(
688
- self._data.exclude(**kwargs),
689
- self._manager_class,
690
- self.filters,
691
- merged_exclude,
692
- )
693
-
694
- def first(self) -> GeneralManagerType | None:
695
- first_element = self._data.first()
696
- if first_element is None:
697
- return None
698
- return self._manager_class(first_element.pk)
699
-
700
- def last(self) -> GeneralManagerType | None:
701
- first_element = self._data.last()
702
- if first_element is None:
703
- return None
704
- return self._manager_class(first_element.pk)
705
-
706
- def count(self) -> int:
707
- return self._data.count()
708
-
709
- def all(self) -> DatabaseBucket:
710
- return self.__class__(self._data.all(), self._manager_class)
711
-
712
- def get(self, **kwargs: Any) -> GeneralManagerType:
713
- element = self._data.get(**kwargs)
714
- return self._manager_class(element.pk)
715
-
716
- def __getitem__(self, item: int | slice) -> GeneralManagerType | DatabaseBucket:
717
- if isinstance(item, slice):
718
- return self.__class__(self._data[item], self._manager_class)
719
- return self._manager_class(self._data[item].pk)
720
-
721
- def __len__(self) -> int:
722
- return self._data.count()
723
-
724
- def __repr__(self) -> str:
725
- return f"{self._manager_class.__name__}Bucket ({self._data})"
726
-
727
- def __contains__(self, item: GeneralManagerType | models.Model) -> bool:
728
- from general_manager.manager.generalManager import GeneralManager
729
-
730
- if isinstance(item, GeneralManager):
731
- return getattr(item, "id") in self._data.values_list("pk", flat=True)
732
- return item in self._data
733
-
734
- def sort(
735
- self,
736
- key: tuple[str] | str,
737
- reverse: bool = False,
738
- ) -> DatabaseBucket:
739
- if isinstance(key, str):
740
- key = (key,)
741
- if reverse:
742
- sorted_data = self._data.order_by(*[f"-{k}" for k in key])
743
- else:
744
- sorted_data = self._data.order_by(*key)
745
- return self.__class__(sorted_data, self._manager_class)