Skip to main content

A Django app for logging changes in model fields.

Project description

A Django app for logging changes in model fields.

How to set up?

  1. Add fieldlogger to your INSTALLED_APPS

  2. Run python manage.py migrate to initialize the model

  3. Add FIELD_LOGGER_SETTINGS to your settings.py file.

FIELD_LOGGER_SETTINGS = {
    'ENCODER': 'path.to.your.json.Encoder', # (default: None)
    'DECODER': 'path.to.your.json.Decoder', # (default: None)
    'LOGGING_ENABLED': True, # (default: True)
    'FAIL_SILENTLY': True, # (default: True)
    'LOGGING_APPS': {
        'your_app': {
            'logging_enabled': True, # (default: True)
            'fail_silently': True, # (default: True)
            'models': {
                'YourModel': {
                    'logging_enabled': True, # (default: True)
                    'fail_silently': True, # (default: True)
                    'fields': ['field1', 'field2'], # (default: [])
                    'exclude_fields': ['field3', 'field4'], # (default: [])
                    'callbacks': [
                        lambda instance, fields, logs: print(instance, fields, logs),
                        'yourapp.app.callbacks.your_function_name'
                    ], # (default: [])
                },
            },
            'callbacks': [
                lambda instances, fields, logs: print(instances, fields, logs),
                'yourapp.app.callbacks.your_function_name'
            ], # (default: [])
        },
    },
    'CALLBACKS': [
        lambda instance, fields, logs: print(instance, fields, logs),
        'yourapp.app.callbacks.your_function_name'
    ], # (default: [])
}
  • ENCODER and DECODER are optional. If you want to encode/decode your model instance fields, you can specify your encoder/decoder classes here. Your encoder/decoder classes must be subclasses of json.JSONEncoder and json.JSONDecoder respectively.

  • LOGGING_ENABLED is optional. If you want to disable logging globally, you can set this to False.

  • FAIL_SILENTLY is optional. If it is set to False, exceptions will be raised if the callback function fails.

  • LOGGING_APPS apps to be logged.

    • models models to be logged.

      • fields is optional. If you want to log only specific fields, you can specify them here. If you want to log all fields, you can use __all__ as a value.

      • exclude_fields is optional. If fields is not specified, all fields in the model will be logged except the ones specified here.

      • callbacks is optional. If you want to add a callback function to be called after logging all models in all apps, you can add it here. Callback functions must be callable objects. You can optionally specify a callback function path in your configuration. The best practice is to place your callback function in yourapp/callbacks.py. Callback functions must have three parameters as follows:

        # callback as a named function
        def your_callback(instance, fields, logs):
            # your code here
        
        # callback as a lambda function
        lambda instance, fields, logs: # your code here
        • instance the model instance that is being logged.

        • fields list of fields that are being logged.

        • logs dict of logs that are being created. The key is the field name and the value is the FieldLog instance.

How it works?

  • Obtains the FIELD_LOGGER_SETTINGS from your respective settings file based on your environment.

  • Initializes LOGGING_APPS with the relative project paths of your models based on your configuration variable.

  • Binds to pre_save signal of each loggable model.

  • For each field specified in the configuration variable, creates a record in the FieldLog model for each instance update.

Example

This section serves as a small example to demonstrate how to use this package.

Supposing you have this configuration in your settings.py file:

FIELD_LOGGER_SETTINGS = {
    'LOGGING_APPS': {
        'drivers': {
            'models': {
                'Driver': {
                    'fields': ['driver_name']
                },
            },
        },
    },
}

Supposing you have a model called Driver with fields called latest_speed, driver_name, driver_id:

driver = Driver.objects.last()
driver.latest_speed = 5
driver.save()  # fieldlogger won't create a record since 'latest_speed' was not among the loggable fields

driver.driver_name = 'John Doe'
driver.save()  # a record with this driver is created

driver.driver_name = 'Jane Doe'
driver.save()  # a record with this driver is created

instance_id = driver.id
app_label = driver._meta.app_label
model_name = driver._meta.model_name

log = FieldLog.objects.filter(instance_id=instance_id, app_label=app_label, table_name=model).last()
print(log.field, log.old_value, log.new_value)  # prints: driver_name John Doe Jane Doe

Callback example

Supposing you have this function in yourapp/callbacks.py which sets the extra_data field of the FieldLog model:

def set_extra_data_for_driver_name(instance, fields, logs):
    log = logs.get('driver_name')
    if log:
        log.extra_data = {
            'name_length': len(log.new_value)
        }
        log.save()

Then you can add this callback function to your configuration like this:

FIELD_LOGGER_SETTINGS = {
    'LOGGING_APPS': {
        'drivers': {
            'models': {
                'Driver': {
                    'fields': ['driver_name'],
                    'callbacks': [
                        'yourapp.callbacks.set_extra_data_for_driver_name'
                    ]
                },
            },
        },
    },
}

The model structure

This package provides you a django model which is called FieldLog; which tracks each change to a model instance specified in your configuration mapping. An example record is as follows:

{
    'id': 2,
    'app_label': 'drivers',
    'model': 'driver',
    'instance_id': 1,
    'field': 'latest_speed',
    'timestamp': datetime.datetime(2024, 1, 16, 9, 1, 14, 619568, tzinfo=<UTC>),
    'old_value': 'John Doe',
    'new_value': 'Jane Doe',
    'extra_data': {}, # this is a JSONField, you can store any extra data here using callbacks or by overriding it directly
    'created': False, # this is a boolean field, if it is True, it means that instance is a newly created instance
}

Additionally, FieldLog model provides the following properties:

  • model: returns the model class of the instance that is being logged.

  • instance: returns the instance that is being logged.

  • previous_log: returns the previous log of the instance that is being logged.

The FieldLoggerMixin

This package provides you a mixin class which is called FieldLoggerMixin. This mixin class provides you the following property:

  • fieldlog_set since the FieldLog model has not a direct relation to the model that is being logged, you can use this property to get the logs of the instance that is being logged.

    driver = Driver.objects.last()
    logs = driver.fieldlog_set.all()

MIT License

Copyright (c) 2024 Sergio Rodríguez

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_field_logger-0.0.42.tar.gz (10.9 kB view details)

Uploaded Source

Built Distribution

django_field_logger-0.0.42-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file django_field_logger-0.0.42.tar.gz.

File metadata

  • Download URL: django_field_logger-0.0.42.tar.gz
  • Upload date:
  • Size: 10.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.10.13

File hashes

Hashes for django_field_logger-0.0.42.tar.gz
Algorithm Hash digest
SHA256 4cbdb0bacdbb697e649f843baac3d6b8e7652cf9239d6b85f7cbe3a90f8e9f58
MD5 d7daa2d02f404b734a605ff1b67e2aa2
BLAKE2b-256 66160778b0ab8f763bb25f446811e5090237dee5d6493f8664d6a70498fdfc88

See more details on using hashes here.

File details

Details for the file django_field_logger-0.0.42-py3-none-any.whl.

File metadata

File hashes

Hashes for django_field_logger-0.0.42-py3-none-any.whl
Algorithm Hash digest
SHA256 f77ac3097378516a17d30243eec3e0850e35f1b08f1030718f2899942bb0bb77
MD5 1289feeed8784dd26075bfb2178eab94
BLAKE2b-256 4406e78887cada30f11859cb3b57de74eac6f14a18630fe4aeac647beb3c3307

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page