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