Code reference › models › common

class

LogMixin

pretalx.common.models.mixins.LogMixin source

Adds logging utilities for integrating pretalx.common.models.ActivityLog.

Attributes 2

log_parentNone
log_prefixNone

Methods 4

Defined here

delete(*args, log_kwargs=None, skip_log=False, **kwargs)source
def delete(self, *args, log_kwargs=None, skip_log=False, **kwargs):
    parent = self.log_parent
    result = super().delete(*args, **kwargs)
    if (
        not skip_log
        and parent
        and getattr(parent, "log_action", None)
        and self.log_prefix
    ):
        log_kwargs = log_kwargs or {}
        parent.log_action(f"{self.log_prefix}.delete", **log_kwargs)
    return result
get_instance_data()sourceGet a dictionary of field values for this instance.

Used for change tracking in log_action. Excludes auto-updated fields like timestamps and sensitive data. Does not handle many-to-many fields.

def get_instance_data(self):
    """Get a dictionary of field values for this instance.

    Used for change tracking in log_action. Excludes auto-updated
    fields like timestamps and sensitive data.
    Does not handle many-to-many fields.
    """
    excluded_fields = {
        "created",
        "updated",
        "is_active",
        "last_login",
        "user",
        "event",
        "code",
    }
    data = {}

    for field in self._meta.fields:
        if (
            field.name in excluded_fields
            or field.name in SENSITIVE_KEYS
            or "thumbnail" in field.name
            or getattr(field, "auto_now", False)
            or getattr(field, "auto_now_add", False)
        ):
            continue

        if isinstance(field, models.ForeignKey):
            data[field.name] = getattr(self, field.attname, None)
            continue

        value = getattr(self, field.name, None)

        if isinstance(field, models.FileField):
            data[field.name] = value.name if value else None
        elif isinstance(field, models.UUIDField):
            data[field.name] = str(value) if value else None
        elif isinstance(value, LazyI18nString):
            if isinstance(getattr(value, "data", None), dict):
                data[field.name] = {k: v for k, v in value.data.items() if v}
            else:
                data[field.name] = str(value)
        else:
            data[field.name] = json_roundtrip(value)
    return data
log_action(action, data=None, person=None, orga=False, content_object=None, old_data=None, new_data=None)source
def log_action(
    self,
    action,
    data=None,
    person=None,
    orga=False,
    content_object=None,
    old_data=None,
    new_data=None,
):
    if self._state.adding or not isinstance(self.pk, int):
        return

    if action.startswith("."):
        if self.log_prefix:
            action = f"{self.log_prefix}{action}"
        else:
            return

    if old_data is not None or new_data is not None:
        from pretalx.common.log import (  # noqa: PLC0415 -- thin method
            compute_log_changes,
        )

        changes = compute_log_changes(old_data, new_data)
        if not changes and not data:
            return
        if changes:
            if data is None:
                data = {}
            data["changes"] = changes

    if data:
        if not isinstance(data, dict):
            raise TypeError(
                f"Logged data should always be a dictionary, not {type(data)}."
            )
        for key in data:
            if any(sensitive_key in key for sensitive_key in SENSITIVE_KEYS):
                data[key] = "********" if data[key] else data[key]
        data = json_roundtrip(data)

    return ActivityLog.objects.create(
        person=person,
        content_object=content_object or self,
        action_type=action,
        data=data,
        is_orga_action=orga,
        **self._log_event_kwargs(),
    )
logged_actions()source
def logged_actions(self):
    return (
        ActivityLog.objects.filter(
            content_type=ContentType.objects.get_for_model(type(self)),
            object_id=self.pk,
        )
        .select_related("event", "person")
        .prefetch_related("content_object")
    )