class SoftDeleteMixin(models.Model): """ This is a mixin to support soft deletion for specific models. This behavior is required to keep everything in the database but still hide it from the end users. Example: Country changes currency - the old one has to be kept but hidden (soft deleted) The functionality achieved by using the SoftDeleteMixin and the ValidityQuerySet. Both of them are depending on the `deleted_at` field, which defaults to EPOCH_ZERO to allow unique constrains in the db. IMPORTANT: Default has to be a value - boolean field or nullable datetime would not work IMPORTANT #2: This model does not prevent cascaded deletion - this can only happen if the soft deleted model points to one which actually deletes the entity from the database """ deleted_at = models.DateTimeField(default=EPOCH_ZERO, verbose_name='Deleted At') # IMPORTANT: The order of these two queryset is important. The normal queryset has to be defined first to have that # as a default queryset admin_objects = QuerySet.as_manager() objects = ValidityQuerySet.as_manager() class Meta: abstract = True def delete(self, *args, **kwargs): self.deleted_at = now() self.save()
class Firm(models.Model): objects = QuerySet.as_manager() name = models.CharField('Name', max_length=255) def str(self): return self.name
class BaseModel(models.Model): created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now=True) objects = QuerySet.as_manager() class Meta: abstract = True
class Company(models.Model): objects = QuerySet.as_manager() name = models.CharField('Name', max_length=255) symbol = models.CharField('Symbol', max_length=50, unique=True) currency = models.ForeignKey('main.Currency', on_delete=models.CASCADE) country = models.CharField('Country', max_length=2) def __str__(self): return f'{self.name} ({self.symbol})'
class SoftDeleteModel(models.Model): is_active = models.BooleanField(default=True) all_objects = QuerySet.as_manager() objects = ActiveQuerySet.as_manager() class Meta: abstract = True def delete(self, *args, **kwargs): self.is_active = False self.save()
class Currency(models.Model): objects = QuerySet.as_manager() name = models.CharField('Name', max_length=255, unique=True) code = models.CharField('Code', max_length=3, unique=True) symbol = models.CharField('Symbol', max_length=5) zero_currency = models.BooleanField('Is zero decimal currency', default=False) def __str__(self): return f'{self.name} - {self.symbol}' class Meta: verbose_name = 'Currency' verbose_name_plural = 'Currencies'
def attribute_class_filter(queryset: QuerySet, user: User = None) -> Tuple[QuerySet, QuerySet]: if not user: return queryset, queryset.as_manager().filter().none() # Get all of the classes that we aren't in. restricted_attribute_classes = AttributeClass.objects.exclude(users=user) attribute_class_queries = { "ExportRun": {"data_provider_task_records__provider__attribute_class__in": restricted_attribute_classes}, "RunZipFile": {"data_provider_task_records__provider__attribute_class__in": restricted_attribute_classes}, "Job": {"data_provider_tasks__provider__attribute_class__in": restricted_attribute_classes}, "DataProvider": {"attribute_class__in": restricted_attribute_classes}, "DataProviderTask": {"provider__attribute_class__in": restricted_attribute_classes}, "DataProviderTaskRecord": {"provider__attribute_class__in": restricted_attribute_classes}, } item = queryset.first() attribute_class_query = {} if item: # Get all of the objects that don't include attribute classes that we aren't in. attribute_class_query = attribute_class_queries.get(type(item).__name__, {}) filtered = queryset.filter(**attribute_class_query).distinct() queryset = queryset.exclude(**attribute_class_query).distinct() return queryset, filtered