Skip to content

Commit

Permalink
Added: getObject()
Browse files Browse the repository at this point in the history
  • Loading branch information
mkalioby committed Jul 30, 2024
1 parent ca34099 commit dfad7d6
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 35 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ v3.0
This is a major release so please test carefully.

* Removed: `old_state` of the object from database, but still the changes can be done against previous object.
* Added: `get_history(reverse=True)` to get all object changes, this returns a Queryset so it can be filtered
* Added: `View Data Changes` in Django Model Admin to see the object changes
* Change: add urls under `tracker` namespace.
* Added: `get_history(reverse=True)` to get all object changes, this returns a Queryset so it can be filtered.
* Added: `get_object()` to the history Model to return the state of the object in that snapshot.
* Added: `View Data Changes` in Django Model Admin to see the object changes.
* Change: add urls under `tracker` namespace.

4 changes: 3 additions & 1 deletion ModelTracker/Tracker.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import lru_cache

from django.db import models
import sys
from .models import *
Expand All @@ -8,14 +10,14 @@
import threading
import datetime


class ModelTracker(models.Model):
thread = threading.local()

def __init__(self,*args,**kwargs):
models.Model.__init__(self, *args, **kwargs)
#self.old_state = copy.deepcopy(self.__dict__)


def get_history(self,reverse=True):
"""Returns queryset"""
q = History.objects.filter(table=self._meta.db_table, primary_key = self.pk)
Expand Down
36 changes: 7 additions & 29 deletions ModelTracker/management/commands/restoreObject.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,23 @@
from django.core.management.base import BaseCommand, CommandError
import __future__
from django.core.management.base import BaseCommand
from ModelTracker.models import History
import datetime
from django.apps import apps
def getModel(table_name):
return next((m for m in apps.get_models() if m._meta.db_table==table_name), None)


class Command(BaseCommand):
help = 'Restore Object to old status'

def add_arguments(self, parser):
parser.add_argument('--id', nargs='?', type=str,default=None)
parser.add_argument("--state",type=str,nargs='?',default="new")
# parser.add_argument("--state",type=str,nargs='?',default="new")
parser.add_argument("--user",type=str,nargs='?',default="CLI")


def handle(self, *args, **options):
if not options.get("id",None):
print ("Change ID is needed")
exit(1)
print (options)

h = History.objects.get(id=int(options["id"]))
model = getModel(h.table)
if model == None:
print("Can't find the Model")
exit(2)
d=[f.name for f in model._meta.get_fields()]
if options["state"]=="old": state=h.old_state
else: state=h.new_state
keys2del=[]
for key in state:
if (key.startswith("_") and "_cache" in key) or (key not in d and not ("_id" in key and key[:-3] in d)):
keys2del.append(key)
if type(state[key])==type({}):
if state[key].get("_type",None) == "datetime":
state[key]=datetime.datetime.strptime(state[key]["value"],"%Y-%m-%d %H:%M:%S")
elif state[key].get("_type",None) == "date":
state[key]=datetime.datetime.strptime(state[key]["value"],"%Y-%m-%d")
for key in keys2del:
del state[key]

print(state)
m=model(**state)
m = h.get_object()
m.save(options["user"],event_name="Restore Record to %s (%s)"%(options["id"],options["state"]))


Expand Down
27 changes: 26 additions & 1 deletion ModelTracker/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import datetime
from functools import lru_cache

from django.db import models
from django.apps import apps

from ModelTracker.utils import get_fields,get_model


try:
from django.db.models import JSONField
except ImportError:
Expand All @@ -8,7 +16,6 @@
raise ImportError("Can't find a JSONField implementation, please install jsonfield if django < 4.0")



class History(models.Model):
id=models.AutoField(primary_key=True)
name=models.CharField(max_length=255,default="")
Expand All @@ -19,8 +26,26 @@ class History(models.Model):
done_by=models.CharField(max_length=255)
done_on=models.DateTimeField(auto_now_add=True)

def get_object(self):
model = get_model(self.table)
fields = get_fields(model)
keys2del = []
state = self.new_state
for key in state:
if (key.startswith("_") and "_cache" in key) or (key not in fields and not ("_id" in key and key[:-3] in fields)):
keys2del.append(key)
if type(state[key])==type({}):
if state[key].get("_type",None) == "datetime":
state[key]=datetime.datetime.strptime(state[key]["value"],"%Y-%m-%d %H:%M:%S")
elif state[key].get("_type",None) == "date":
state[key]=datetime.datetime.strptime(state[key]["value"],"%Y-%m-%d")
for key in keys2del:
del state[key]
return model(**state)

def __eq__(self, other):
return self.id == other.id

def __str__(self):
if self.name:
return "%s for %s in %s"%(self.name, self.primary_key, self.table)
Expand Down
11 changes: 11 additions & 0 deletions ModelTracker/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from functools import lru_cache

from django.apps import apps


@lru_cache(50)
def get_model(table_name):
return next((m for m in apps.get_models() if m._meta.db_table==table_name), None)

def get_fields(model):
return [f.name for f in model._meta.get_fields()]
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

setup(
name='django-model-tracker',
version='3.0b1',
version='3.0b2',
description='Track Django Model Objects over time',
author='Mohamed El-Kalioby',
author_email = '[email protected]',
Expand Down

0 comments on commit dfad7d6

Please sign in to comment.