decorator.py 3.48 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# ----------------------------------------------------------------------
# @change decorator and worker
# ----------------------------------------------------------------------
# Copyright (C) 2007-2021 The NOC Project
# See LICENSE for details
# ----------------------------------------------------------------------

# Python modules
from logging import getLogger

# NOC modules
from noc.models import is_document, get_model_id
from .policy import change_tracker

logger = getLogger(__name__)


def change(model):
    """
    @change decorator to enable generalized change tracking on the model.
    :param model:
    :return:
    """
    if not hasattr(model, "get_by_id"):
        raise ValueError("[%s] Missed .get_by_id", get_model_id(model))
    if is_document(model):
        _track_document(model)
    else:
        _track_model(model)
    return model


def _track_document(model):
    """
    Setup document change tracking
    :param model:
    :return:
    """
    from mongoengine import signals

    logger.debug("[%s] Tracking changes", get_model_id(model))
    signals.post_save.connect(_on_document_change, sender=model)
    signals.pre_delete.connect(_on_document_delete, sender=model)


def _track_model(model):
    """
    Setup model change tracking
    :param model:
    :return:
    """
    from django.db.models import signals

    logger.debug("[%s] Tracking changes", get_model_id(model))
    signals.post_save.connect(_on_model_change, sender=model)
    signals.pre_delete.connect(_on_model_delete, sender=model)


def _on_document_change(sender, document, created=False, *args, **kwargs):
    model_id = get_model_id(document)
    op = "create" if created else "update"
    logger.debug("[%s|%s] Change detected: %s", model_id, document.id, op)
    changed_fields = list(document._changed_fields if not created else [])
    change_tracker.register(
        op=op,
        model=model_id,
        id=str(document.id),
        fields=changed_fields,
    )


def _on_document_delete(sender, document, *args, **kwargs):
    model_id = get_model_id(document)
    op = "delete"
    logger.debug("[%s|%s] Change detected: %s", model_id, document.id, op)
    change_tracker.register(
        op=op,
        model=model_id,
        id=str(document.id),
        fields=None,
    )


def _on_model_change(sender, instance, created=False, *args, **kwargs):
    def is_changed(field_name):
        ov = instance.initial_data[field_name]
        if hasattr(ov, "pk"):
            ov = ov.pk
        nv = getattr(instance, field_name)
        if hasattr(nv, "pk"):
            nv = nv.pk
        return str(ov) != str(nv)

94
95
96
97
98
99
100
101
102
    # Check for instance proxying
    if hasattr(instance, "get_changed_instance"):
        instance = instance.get_changed_instance()
        changed_fields = []
    else:
        changed_fields = [
            f_name for f_name in getattr(instance, "initial_data", []) if is_changed(f_name)
        ]
    # Register change
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    model_id = get_model_id(instance)
    op = "create" if created else "update"
    logger.debug("[%s|%s] Change detected: %s", model_id, instance.id, op)
    change_tracker.register(
        op=op,
        model=model_id,
        id=str(instance.id),
        fields=changed_fields,
    )


def _on_model_delete(sender, instance, *args, **kwargs):
    model_id = get_model_id(instance)
    op = "delete"
    logger.debug("[%s|%s] Change detected: %s", model_id, instance.id, op)
    change_tracker.register(
        op=op,
        model=model_id,
        id=str(instance.id),
        fields=None,
    )