import uuid from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models from auditlog.models import AuditlogHistoryField from auditlog.registry import AuditlogModelRegistry, auditlog m2m_only_auditlog = AuditlogModelRegistry(create=False, update=False, delete=False) @auditlog.register() class SimpleModel(models.Model): """ A simple model with no special things going on. """ text = models.TextField(blank=True) boolean = models.BooleanField(default=False) integer = models.IntegerField(blank=True, null=True) datetime = models.DateTimeField(auto_now=True) char = models.CharField(null=True, max_length=100, default=lambda: "default value") history = AuditlogHistoryField(delete_related=True) def __str__(self): return str(self.text) class AltPrimaryKeyModel(models.Model): """ A model with a non-standard primary key. """ key = models.CharField(max_length=100, primary_key=True) text = models.TextField(blank=True) boolean = models.BooleanField(default=False) integer = models.IntegerField(blank=True, null=True) datetime = models.DateTimeField(auto_now=True) history = AuditlogHistoryField(delete_related=True, pk_indexable=False) class UUIDPrimaryKeyModel(models.Model): """ A model with a UUID primary key. """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) text = models.TextField(blank=True) boolean = models.BooleanField(default=False) integer = models.IntegerField(blank=True, null=True) datetime = models.DateTimeField(auto_now=True) history = AuditlogHistoryField(delete_related=True, pk_indexable=False) class ModelPrimaryKeyModel(models.Model): """ A model with another model as primary key. """ key = models.OneToOneField( "SimpleModel", primary_key=True, on_delete=models.CASCADE, related_name="reverse_primary_key", ) text = models.TextField(blank=True) boolean = models.BooleanField(default=False) integer = models.IntegerField(blank=True, null=True) datetime = models.DateTimeField(auto_now=True) history = AuditlogHistoryField(delete_related=True, pk_indexable=False) class ProxyModel(SimpleModel): """ A model that is a proxy for another model. """ class Meta: proxy = True class RelatedModelParent(models.Model): """ Use multi table inheritance to make a OneToOneRel field """ class RelatedModel(RelatedModelParent): """ A model with a foreign key. """ related = models.ForeignKey( "SimpleModel", related_name="related_models", on_delete=models.CASCADE ) one_to_one = models.OneToOneField( to="SimpleModel", on_delete=models.CASCADE, related_name="reverse_one_to_one" ) history = AuditlogHistoryField(delete_related=True) def __str__(self): return f"RelatedModel #{self.pk} -> {self.related.id}" class ManyRelatedModel(models.Model): """ A model with many-to-many relations. """ recursive = models.ManyToManyField("self") related = models.ManyToManyField("ManyRelatedOtherModel", related_name="related") history = AuditlogHistoryField(delete_related=True) def get_additional_data(self): related = self.related.first() return {"related_model_id": related.id if related else None} class ManyRelatedOtherModel(models.Model): """ A model related to ManyRelatedModel as many-to-many. """ history = AuditlogHistoryField(delete_related=True) class ReusableThroughRelatedModel(models.Model): """ A model related to multiple other models through a model. """ label = models.CharField(max_length=100) class ReusableThroughModel(models.Model): """ A through model that can be associated multiple different models. """ label = models.ForeignKey( ReusableThroughRelatedModel, on_delete=models.CASCADE, related_name="%(app_label)s_%(class)s_items", ) one = models.ForeignKey( "ModelForReusableThroughModel", on_delete=models.CASCADE, null=True, blank=True ) two = models.ForeignKey( "OtherModelForReusableThroughModel", on_delete=models.CASCADE, null=True, blank=True, ) class ModelForReusableThroughModel(models.Model): """ A model with many-to-many relations through a shared model. """ name = models.CharField(max_length=200) related = models.ManyToManyField( ReusableThroughRelatedModel, through=ReusableThroughModel ) history = AuditlogHistoryField(delete_related=True) class OtherModelForReusableThroughModel(models.Model): """ Another model with many-to-many relations through a shared model. """ name = models.CharField(max_length=200) related = models.ManyToManyField( ReusableThroughRelatedModel, through=ReusableThroughModel ) history = AuditlogHistoryField(delete_related=True) @auditlog.register(include_fields=["label"]) class SimpleIncludeModel(models.Model): """ A simple model used for register's include_fields kwarg """ label = models.CharField(max_length=100) text = models.TextField(blank=True) history = AuditlogHistoryField(delete_related=True) class SimpleExcludeModel(models.Model): """ A simple model used for register's exclude_fields kwarg """ label = models.CharField(max_length=100) text = models.TextField(blank=True) history = AuditlogHistoryField(delete_related=True) class SimpleMappingModel(models.Model): """ A simple model used for register's mapping_fields kwarg """ sku = models.CharField(max_length=100) vtxt = models.CharField(verbose_name="Version", max_length=100) not_mapped = models.CharField(max_length=100) history = AuditlogHistoryField(delete_related=True) @auditlog.register(mask_fields=["address"]) class SimpleMaskedModel(models.Model): """ A simple model used for register's mask_fields kwarg """ address = models.CharField(max_length=100) text = models.TextField() history = AuditlogHistoryField(delete_related=True) class AdditionalDataIncludedModel(models.Model): """ A model where get_additional_data is defined which allows for logging extra information about the model in JSON """ label = models.CharField(max_length=100) text = models.TextField(blank=True) related = models.ForeignKey(to=SimpleModel, on_delete=models.CASCADE) history = AuditlogHistoryField(delete_related=True) def get_additional_data(self): """ Returns JSON that captures a snapshot of additional details of the model instance. This method, if defined, is accessed by auditlog manager and added to each logentry instance on creation. """ object_details = { "related_model_id": self.related.id, "related_model_text": self.related.text, } return object_details class DateTimeFieldModel(models.Model): """ A model with a DateTimeField, used to test DateTimeField changes are detected properly. """ label = models.CharField(max_length=100) timestamp = models.DateTimeField() date = models.DateField() time = models.TimeField() naive_dt = models.DateTimeField(null=True, blank=True) history = AuditlogHistoryField(delete_related=True) class ChoicesFieldModel(models.Model): """ A model with a CharField restricted to a set of choices. This model is used to test the changes_display_dict method. """ RED = "r" YELLOW = "y" GREEN = "g" STATUS_CHOICES = ( (RED, "Red"), (YELLOW, "Yellow"), (GREEN, "Green"), ) status = models.CharField(max_length=1, choices=STATUS_CHOICES) multiplechoice = models.CharField(max_length=255, choices=STATUS_CHOICES) history = AuditlogHistoryField(delete_related=True) class CharfieldTextfieldModel(models.Model): """ A model with a max length CharField and a Textfield. This model is used to test the changes_display_dict method's ability to truncate long text. """ longchar = models.CharField(max_length=255) longtextfield = models.TextField() history = AuditlogHistoryField(delete_related=True) # Only define PostgreSQL-specific models when ArrayField is available if settings.TEST_DB_BACKEND == "postgresql": from django.contrib.postgres.fields import ArrayField class PostgresArrayFieldModel(models.Model): """ Test auditlog with Postgres's ArrayField """ RED = "r" YELLOW = "y" GREEN = "g" STATUS_CHOICES = ( (RED, "Red"), (YELLOW, "Yellow"), (GREEN, "Green"), ) arrayfield = ArrayField( models.CharField(max_length=1, choices=STATUS_CHOICES), size=3 ) history = AuditlogHistoryField(delete_related=True) else: class PostgresArrayFieldModel(models.Model): class Meta: managed = False class NoDeleteHistoryModel(models.Model): integer = models.IntegerField(blank=True, null=True) history = AuditlogHistoryField(delete_related=False) class JSONModel(models.Model): json = models.JSONField(default=dict, encoder=DjangoJSONEncoder) history = AuditlogHistoryField(delete_related=False) class NullableJSONModel(models.Model): json = models.JSONField(null=True, blank=True) history = AuditlogHistoryField(delete_related=False) class SerializeThisModel(models.Model): label = models.CharField(max_length=24, unique=True) timestamp = models.DateTimeField() nullable = models.IntegerField(null=True) nested = models.JSONField() mask_me = models.CharField(max_length=255, null=True) code = models.UUIDField(null=True) date = models.DateField(null=True) history = AuditlogHistoryField(delete_related=False) def natural_key(self): return self.label class SerializeOnlySomeOfThisModel(models.Model): this = models.CharField(max_length=24) not_this = models.CharField(max_length=24) history = AuditlogHistoryField(delete_related=False) class SerializePrimaryKeyRelatedModel(models.Model): serialize_this = models.ForeignKey(to=SerializeThisModel, on_delete=models.CASCADE) subheading = models.CharField(max_length=255) value = models.IntegerField() history = AuditlogHistoryField(delete_related=False) class SerializeNaturalKeyRelatedModel(models.Model): serialize_this = models.ForeignKey(to=SerializeThisModel, on_delete=models.CASCADE) subheading = models.CharField(max_length=255) value = models.IntegerField() history = AuditlogHistoryField(delete_related=False) class SimpleNonManagedModel(models.Model): """ A simple model with no special things going on. """ text = models.TextField(blank=True) boolean = models.BooleanField(default=False) integer = models.IntegerField(blank=True, null=True) datetime = models.DateTimeField(auto_now=True) history = AuditlogHistoryField(delete_related=True) def __str__(self): return self.text class Meta: managed = False class SecretManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(is_secret=False) @auditlog.register() class SwappedManagerModel(models.Model): is_secret = models.BooleanField(default=False) name = models.CharField(max_length=255) objects = SecretManager() class AutoManyRelatedModel(models.Model): related = models.ManyToManyField(SimpleModel) class CustomMaskModel(models.Model): credit_card = models.CharField(max_length=16) text = models.TextField() history = AuditlogHistoryField(delete_related=True) auditlog.register(AltPrimaryKeyModel) auditlog.register(UUIDPrimaryKeyModel) auditlog.register(ModelPrimaryKeyModel) auditlog.register(ProxyModel) auditlog.register(RelatedModel) auditlog.register(ManyRelatedModel) auditlog.register(ManyRelatedModel.recursive.through) m2m_only_auditlog.register(ManyRelatedModel, m2m_fields={"related"}) m2m_only_auditlog.register(ModelForReusableThroughModel, m2m_fields={"related"}) m2m_only_auditlog.register(OtherModelForReusableThroughModel, m2m_fields={"related"}) auditlog.register(SimpleExcludeModel, exclude_fields=["text"]) auditlog.register(SimpleMappingModel, mapping_fields={"sku": "Product No."}) auditlog.register(AdditionalDataIncludedModel) auditlog.register(DateTimeFieldModel) auditlog.register(ChoicesFieldModel) auditlog.register(CharfieldTextfieldModel) if settings.TEST_DB_BACKEND == "postgresql": auditlog.register(PostgresArrayFieldModel) auditlog.register(NoDeleteHistoryModel) auditlog.register(JSONModel) auditlog.register(NullableJSONModel) auditlog.register( SerializeThisModel, serialize_data=True, mask_fields=["mask_me"], ) auditlog.register( SerializeOnlySomeOfThisModel, serialize_data=True, serialize_auditlog_fields_only=True, exclude_fields=["not_this"], ) auditlog.register(SerializePrimaryKeyRelatedModel, serialize_data=True) auditlog.register( SerializeNaturalKeyRelatedModel, serialize_data=True, serialize_kwargs={"use_natural_foreign_keys": True}, ) auditlog.register( CustomMaskModel, mask_fields=["credit_card"], mask_callable="auditlog_tests.test_app.mask.custom_mask_str", )