django-simple-history FTW!

Eight months! Apparently that is the magic number when it comes to the amount of time it takes for me to talk about work I’ve done and get motivated to polish it up a bit. That said, lets talk about django-simple-history! One of my bigger projects at work is a monstrous django project that I’ve built from the ground up that does a bunch of enterprise type nitty-gritty (yay CRUD). At time of inception, one of the big feature requests was to have tracking of _all_ changes to the database. This request included needs for the following:

  • change type (create, update, delete)
  • change time
  • change user

I looked around and saw some basic implementations of different historical record attempts, including AuditTrail but had a requirement of needing models.OneToOneField & models.ForeignKey fields to work (store that there was a relation and not fail on lookups). I had read about something similar in Marty Alchin’s Pro Django book (highly recommend), so I dug that up and was sad to see that it also didn’t work with the relation fields. I reached out to Marty to see if he had a solution and it seemed that at the time he had not even thought about it too much past his initial work. I asked him if he’d be OK with me expanding upon his initial source and creating a project out of his work with changes to support the relation fields and he agreed. The one aspect that had to be left out for now is the tracking of the user who made the change. This _can_ be done. I do it with an extra field added to the history model. That said, hopefully soon I can release that code with a patch to django-simple-history so you can easily do this. For now please contact me for more details about tracking the current user making changes. That said, everything else was free game so I threw it all together and put it on bitbucket.

Thus django-simple-history was born.

I kinda threw the code up after i got it working and totally forgot about it. I’m sad to admit that I didn’t do it due diligence, even if I never planned to make it a package. That was until someone actually forked my code! *SHOCK* They had added a setup.py, so I pulled it in and expanded on it (it was pretty simple at first). Now there is a fully installable app with example code in the README and an actual package uploaded to make django-simple-history 1.0!

Here’s the basic rundown on how you use it using the django tutorials Poll and Choice models (used django 1.2.3 for these tests):

from django.db import models
# import the HistoricalRecords model
from simple_history.models import HistoricalRecords

class Poll(models.Model):
    question = models.CharField(max_length = 200)
    pub_date = models.DateTimeField('date published')

    # create an instance of HistoricalRecords on any model you want to track
    history = HistoricalRecords()

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(max_length=200)
    votes = models.IntegerField()

    history = HistoricalRecords()

That’s it! Once you’ve done this, when you $ ./manage.py syncdb you will get the following:

BEGIN;
CREATE TABLE "test_historicalpoll" (
    "id" integer NOT NULL,
    "question" varchar(200) NOT NULL,
    "pub_date" datetime NOT NULL,
    "history_id" integer NOT NULL PRIMARY KEY,
    "history_date" datetime NOT NULL,
    "history_type" varchar(1) NOT NULL
)
;
CREATE TABLE "test_poll" (
    "id" integer NOT NULL PRIMARY KEY,
    "question" varchar(200) NOT NULL,
    "pub_date" datetime NOT NULL
)
;
CREATE TABLE "test_historicalchoice" (
    "id" integer NOT NULL,
    "poll_id" integer,
    "choice" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "history_id" integer NOT NULL PRIMARY KEY,
    "history_date" datetime NOT NULL,
    "history_type" varchar(1) NOT NULL
)
;
CREATE TABLE "test_choice" (
    "id" integer NOT NULL PRIMARY KEY,
    "poll_id" integer NOT NULL REFERENCES "test_poll" ("id"),
    "choice" varchar(200) NOT NULL,
    "votes" integer NOT NULL
)
;
CREATE INDEX "test_historicalpoll_4a5fc416" ON "test_historicalpoll" ("id");
CREATE INDEX "test_historicalchoice_4a5fc416" ON "test_historicalchoice" ("id");
CREATE INDEX "test_historicalchoice_763e883" ON "test_historicalchoice" ("poll_id");
CREATE INDEX "test_choice_763e883" ON "test_choice" ("poll_id");
COMMIT;

* test was my app name for use in testing… poor choice.

The two historical db’s were created with the extra fields needed to track them historically:

  • test_historicalpoll
  • test_historicalchoice

Now to use, fire up the django shell $ ./manage.py syncdb and try this!

In [2]: from poll.models import Poll, Choice

In [3]: Poll.objects.all()
Out[3]: []

In [4]: import datetime

In [5]: p = Poll(question="what's up?", pub_date=datetime.datetime.now())

In [6]: p.save()

In [7]: p
Out[7]: <Poll: Poll object>

In [9]: p.history.all()
Out[9]: [<HistoricalPoll: Poll object as of 2010-10-25 18:03:29.855689>]

In [10]: p.pub_date = datetime.datetime(2007,4,1,0,0)

In [11]: p.save()

In [13]: p.history.all()
Out[13]: [<HistoricalPoll: Poll object as of 2010-10-25 18:04:13.814128>, <HistoricalPoll: Poll object as of 2010-10-25 18:03:29.855689>]

In [14]: p.choice_set.create(choice='Not Much', votes=0)
Out[14]: <Choice: Choice object>

In [15]: p.choice_set.create(choice='The sky', votes=0)
Out[15]: <Choice: Choice object>

In [16]: c = p.choice_set.create(choice='Just hacking again', votes=0)

In [17]: c.poll
Out[17]: <Poll: Poll object>

In [19]: c.history.all()
Out[19]: [<HistoricalChoice: Choice object as of 2010-10-25 18:05:30.160595>]

In [20]: Choice.history
Out[20]: <simple_history.manager.HistoryManager object at 0x1cc4290>

In [21]: Choice.history.all()
Out[21]: [<HistoricalChoice: Choice object as of 2010-10-25 18:05:30.160595>, <HistoricalChoice: Choice object as of 2010-10-25 18:05:12.183340>, <HistoricalChoice: Choice object as of 2010-10-25 18:04:59.047351>]

The highlighted lines show how to access the history of each object:

  • the first and second being the specific history of an instance (p – Poll, c – Choice)
  • the third is the history of an entire model (Choice)

Please leave a comment if you have any questions about this, or drop a bug report on bitbucket. I’m open to suggestions and/or more help to fill out some of the other relational field types (ManyToMany).

tl;dr – Use django-simple-history, it’s epic.

Continue reading » · Rating: · Written on: 10-26-10 · 7 Comments »

give me a vision to believe in…

Not wanting to be left out in the cold, I took Monday off along with all the other lucky souls who were giving it off to relax and enjoy some R&R. Despite that, early on in the day I got pulled into helping a friend with some time critical work. The better part of my afternoon and night was spent doing research and testing out different technologies that really sparked a fire in me. It brought me back to place I had almost forgot existed; where work becomes fun. It didn’t even matter that I wasn’t getting paid or that it was my day off, I truly enjoyed tackling the problem and planning out / implementing solutions. Throughout the day and even now the only thought in my head is “why cant I make that a career?”

When I think back to early 2009 when I put this site up, I remember how hopeful I was at the prospect of a fresh start in my career. I was spending my days happily coding away on whatever project I pleased producing really awesome tools and generally getting back to the roots of my love for all things tech. I cant remember a time when I was happier (or poorer). In my bliss I imagined that ‘this could be every day of my life’. Work with some really cool tech with the freedom to explore, learn, and succeed all on my own? Yes please.

Then the real world caught up with me.

It’s a shame how life works out sometimes, but we take it in stride and push forward. I’ve fallen into a job that had a lot of similarities to the last job I had, yet at the same time is still very different. The people I have the pleasure of working directly with are truly passionate about the work they do day in an day out. As refreshing as it was to see my coworkers seemly enjoy their work, it made me realize that my love for that type of task was quickly fading. Today, I can feel those times in the days when work becomes work. To be honest, I cant remember a day where the office gave off any sense of drive other than emergency issues which really just equates to enormous amounts of stress. I hear the comments like “you should find new problems to keep yourself interested” or “self motivation is key” and I just shake my head. “What world do they live in?” is all I can think in response. I have all the negatives of having someone above me in a management roll without any of those really important perks.

  • Yes my hours are somewhat flexible.
  • No I cant spend a few hours to make system A instantly more useful.
  • Yes I’m not overly micro-manged.
  • No we cant just get something done, we need to play the office politics game and drag this out _years_.

“Oh but ____ is shielding you from _____.”
*rant* Really? Did I ask for that? Do I need that? I’m a big boy, I can handle myself. I don’t need you to speak for me. In most cases, I don’t want you to. How about you ask me what my thoughts on a subject are? How about we talk at all. In a year and a half, I’ve never really had a one on one with my manager. I’ve never really had more than a handful of three minute group discussions with them. I’m not an incapable human being; I can produce and clearly present well informed opinions and ideas. Please, if you must sit on that tier above me in the or chart and insist of directing my work, do me the kindness of talking to me. Thank you. */rant*

Every day is a constant reminder as to how unimportant we (the employees) are to my company. As I suspect it is with most companies, I generally feel that the higher-ups do not care one way or the other about anyone below them and to some extent I suspect many of them make the majority of their decisions based on how it helps them personally, not their people. My belief is that my current job is to develop tools and processes to make the lives of other employees easier so that they can work more effectively. If I do my job well everyone prospers all the way up the chain. I could do this in a number of places, but at this point I go into meetings (not that we have any) with suggestions that I’m already expecting to get shot down. It’s depressing and over the course of my tenure has inspired a level of cynicism that I didn’t know I was capable of.

I’m not oblivious to the fact that this is most likely what I can expect this from a lot of places. I believe our entire industry is plagued with poor or just outright bad management personnel and practices. I know I’m not alone in saying that there is a growing subdivision of young companies that are challenging the way the workplace looks and operates. I look and see these other companies; their team building events and employee benefits, their general ‘do good’ attitudes and well meaning company visions that give their people a reason to want to go to work every day. They recognize that they’re asking someone to dedicate a part of their life for the companies success; in turn, if it works out they take care of their people. Everyone wants the company to succeed. It’s no longer ‘just a job’, it’s what they want to be doing every day.

I want that.

ps: if this post doesn’t say have ‘career suicide’ stamped all over it, i don’t know what will.

Continue reading » · Rating: · Written on: 10-12-10 · No Comments »