In django auditlog Create,Update and Delete is working fine for me.For search I try to customize the given default package. In that package they are mentioned The valid actions are :py:attr:`Action.CREATE`, :py:attr:`Action.UPDATE` and :py:attr:`Action.DELETE`.though I have tried but I am unable to achieve the goal. Here I have mentioned code below
@python_2_unicode_compatible
class LogEntry(models.Model):
"""
Represents an entry in the audit log. The content type is saved along with the textual and numeric (if available)
primary key, as well as the textual representation of the object when it was saved. It holds the action performed
and the fields that were changed in the transaction.
If AuditlogMiddleware is used, the actor will be set automatically. Keep in mind that editing / re-saving LogEntry
instances may set the actor to a wrong value - editing LogEntry instances is not recommended (and it should not be
necessary).
"""
class Action:
"""
The actions that Auditlog distinguishes: creating, updating and deleting objects. Viewing objects is not logged.
The values of the actions are numeric, a higher integer value means a more intrusive action. This may be useful
in some cases when comparing actions because the ``__lt``, ``__lte``, ``__gt``, ``__gte`` lookup filters can be
used in queries.
The valid actions are :py:attr:`Action.CREATE`, :py:attr:`Action.UPDATE` and :py:attr:`Action.DELETE`.
"""
CREATE = 0
UPDATE = 1
DELETE = 2
choices = (
(CREATE, _("create")),
(UPDATE, _("update")),
(DELETE, _("delete")),
)
content_type = models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE, related_name='+', verbose_name=_("content type"))
object_pk = models.CharField(db_index=True, max_length=255, verbose_name=_("object pk"))
object_id = models.BigIntegerField(blank=True, db_index=True, null=True, verbose_name=_("object id"))
object_repr = models.TextField(verbose_name=_("object representation"))
action = models.PositiveSmallIntegerField(choices=Action.choices, verbose_name=_("action"))
changes = models.TextField(blank=True, verbose_name=_("change message"))
actor = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True, related_name='+', verbose_name=_("actor"))
# remote_addr = models.GenericIPAddressField(blank=True, null=True, verbose_name=_("remote address"))
timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_("timestamp"))
# additional_data = JSONField(blank=True, null=True, verbose_name=_("additional data"))
objects = LogEntryManager()
class Meta:
get_latest_by = 'timestamp'
ordering = ['-timestamp']
verbose_name = _("log entry")
verbose_name_plural = _("log entries")
def __str__(self):
if self.action == self.Action.CREATE:
fstring = _("Created {repr:s}")
elif self.action == self.Action.UPDATE:
print("updating",self.action)
fstring = _("Updated {repr:s}")
print(fstring)
elif self.action == self.Action.DELETE:
fstring = _("Deleted {repr:s}")
else:
fstring = _("Logged {repr:s}")
return fstring.format(repr=self.object_repr)
@property
def changes_dict(self):
"""
:return: The changes recorded in this log entry as a dictionary object.
"""
try:
return json.loads(self.changes)
except ValueError:
return {}
@property
def changes_str(self, colon=': ', arrow=smart_text(' \u2192 '), separator='; '):
"""
Return the changes recorded in this log entry as a string. The formatting of the string can be customized by
setting alternate values for colon, arrow and separator. If the formatting is still not satisfying, please use
:py:func:`LogEntry.changes_dict` and format the string yourself.
:param colon: The string to place between the field name and the values.
:param arrow: The string to place between each old and new value.
:param separator: The string to place between each field.
:return: A readable string of the changes in this log entry.
"""
substrings = []
for field, values in iteritems(self.changes_dict):
substring = smart_text('{field_name:s}{colon:s}{old:s}{arrow:s}{new:s}').format(
field_name=field,
colon=colon,
old=values[0],
arrow=arrow,
new=values[1],
)
substrings.append(substring)
return separator.join(substrings)
@property
def changes_display_dict(self):
"""
:return: The changes recorded in this log entry intended for display to users as a dictionary object.
"""
# Get the model and model_fields
from auditlog.registry import auditlog
model = self.content_type.model_class()
model_fields = auditlog.get_model_fields(model._meta.model)
changes_display_dict = {}
# grab the changes_dict and iterate through
for field_name, values in iteritems(self.changes_dict):
# try to get the field attribute on the model
try:
field = model._meta.get_field(field_name)
except FieldDoesNotExist:
changes_display_dict[field_name] = values
continue
values_display = []
# handle choices fields and Postgres ArrayField to get human readable version
choices_dict = None
if hasattr(field, 'choices') and len(field.choices) > 0:
choices_dict = dict(field.choices)
if hasattr(field, 'base_field') and getattr(field.base_field, 'choices', False):
choices_dict = dict(field.base_field.choices)
if choices_dict:
for value in values:
try:
value = ast.literal_eval(value)
if type(value) is [].__class__:
values_display.append(', '.join([choices_dict.get(val, 'None') for val in value]))
else:
values_display.append(choices_dict.get(value, 'None'))
except ValueError:
values_display.append(choices_dict.get(value, 'None'))
except:
values_display.append(choices_dict.get(value, 'None'))
else:
try:
field_type = field.get_internal_type()
except AttributeError:
# if the field is a relationship it has no internal type and exclude it
continue
for value in values:
# handle case where field is a datetime, date, or time type
if field_type in ["DateTimeField", "DateField", "TimeField"]:
try:
value = parser.parse(value)
if field_type == "DateField":
value = value.date()
elif field_type == "TimeField":
value = value.time()
elif field_type == "DateTimeField":
value = value.replace(tzinfo=timezone.utc)
value = value.astimezone(gettz(settings.TIME_ZONE))
value = formats.localize(value)
except ValueError:
pass
# check if length is longer than 140 and truncate with ellipsis
if len(value) > 140:
value = "{}...".format(value[:140])
values_display.append(value)
verbose_name = model_fields['mapping_fields'].get(
field.name, getattr(field, 'verbose_name',
field.name))
changes_display_dict[verbose_name] = values_display
return changes_display_dict
Registry.py:
def log_create(sender, instance, created, **kwargs):
"""
Signal receiver that creates a log entry when a model instance is first saved to the database.
Direct use is discouraged, connect your model through :py:func:`auditlog.registry.register` instead.
"""
if created:
changes = model_instance_diff(None, instance)
log_entry = LogEntry.objects.log_create(
instance,
action=LogEntry.Action.CREATE,
changes=json.dumps(changes),
)
def log_update(sender, instance, **kwargs):
"""
Signal receiver that creates a log entry when a model instance is changed and saved to the database.
Direct use is discouraged, connect your model through :py:func:`auditlog.registry.register` instead.
"""
try:
except sender.DoesNotExist:
pass
else:
new = instance
changes = model_instance_diff(old, new)
# Log an entry only if there are changes
if changes:
log_entry = LogEntry.objects.log_create(
instance,
action=LogEntry.Action.UPDATE,
changes=json.dumps(changes),
)
def log_delete(sender, instance, **kwargs):
"""
Signal receiver that creates a log entry when a model instance is deleted from the database.
Direct use is discouraged, connect your model through :py:func:`auditlog.registry.register` instead.
"""
changes = model_instance_diff(instance, None)
log_entry = LogEntry.objects.log_create(
instance,
action=LogEntry.Action.DELETE,
changes=json.dumps(changes),
)