Skip to main content

Models and mixins for recording changes in Django models

Project description

https://img.shields.io/pypi/l/django-record.svg https://secure.travis-ci.org/kuc2477/django-record.png?branch=master https://coveralls.io/repos/kuc2477/django-record/badge.svg?branch=master https://img.shields.io/pypi/v/django-record.svg Latest Version

Author

Compatibility

  • Python3 is currently not supported.

Dependencies

Installation

pip install django-record

Rationale

Often there are situations where you want to record your properties of your models and where you want to track their changes. Although that recording process can be implemented by handcrafted, ad-hoc signals or overriding save() methods of your models, it’s not a generic way, and it’ll eventually mess up your code base.

django-record automatically creates an snapshot-like extra record when an audited Django model instance has been changed either directly or indirectly, without messing up your code base.

RecordModel will detect any changes in recording_fields of recording_model at it’s post_save time or auditing_relatives’s post_save time and create an new record for it.

You can access records via record manager records in your recorded model instance. You can also access recorded model’s instance via recording, which is in effect ForeignKey. from your records.

More conveniently, just mixin RecordedModelMixin to your model and provide recording_fields and auditing_relatives as RecordModel to record specific model.

Mixins

django-record provides mixins which auto-registers RecordModel for your mixed in model to help your model instance recording.

  • RecordedModelMixin
    Attributes
    • recording_fields (list): A List consists of either to-be-recoreded field names or tuples of a property name and it’s field instance to be saved in database.

    • auditing_relatives (list): A List of audited relatives. An extra record will be created on every save() calls of these relative instances that indirectly affects the recording instance, along with recording on direct save() calls from recording_model instances.

    • RecordMeta (class): A class storing meta information for recording. Set audit_all_relatives to audit all relatives of your recorded model’s instance. Note that auditing all relatives can cause a performance issue in large scale database.

    Example

    from django.db import models
    from django.db.models import Sum
    
    from django_record.models import TimeStampedModel
    from django_record.mixins import RecordedModelMixin
    
    
    class Article(RecordedModelMixin, TimeStampedModel):
        author = models.ForeignKey(User, related_name='articles')
        title = models.CharField(max_length=100)
    
        @property
        def total_comment_count(self):
            return self.comments.count()
    
        @property
        def total_score(self):
            return 0 if not self.votes.exists() else \
            int(self.votes.aggregate(Sum('score'))['score__sum'])
    
        @property
        def full_name_of_author(self):
            return self.author.username
    
        recording_fields = [
            'title',
            ('total_comment_count', models.IntegerField()),
            ('total_score', models.IntegerField()),
            ('full_name_of_author', models.CharField(max_length=100))
        ]
    
        class RecordMeta:
           audit_all_relatives = True
    
    
    class Comment(RecordedModelMixin, TimeStampedModel):
        article = models.ForeignKey(Article, related_name='comments')
        text = models.TextField()
    
        @property
        def title_of_article_with_prefix(self):
            return 'title: ' + self.article.title
    
        recording_fields = [
            'text',
            ('title_of_article_with_prefix', models.CharField(max_length=200))
        ]
    
        class RecordMeta:
            audit_all_relatives = True
    
    
    class Vote(models.Model):
        article = models.ForeignKey(Article, related_name='votes')
        score = models.IntegerField()

Models

django-record provides models for recording model instances, including RecordModel and TimeStampedModel.

  • RecordModel
    Attributes
    • recording_model (class): A model class to be recorded. An extra record will be created on every changed save() calls of it’s instance or audited relative’s save() calls.

    • recording_fields (list): A List consists of either to-be-recoreded field names or tuples of a property name and it’s field instance to be saved in database.

    • auditing_relatives (list): A List of audited relatives. An extra record will be created on every save() calls of these relative instances that indirectly affects the recording instance, along with recording on direct save() calls from recording_model instances.

    • RecordMeta (class): A class storing meta information for recording. Set audit_all_relatives to audit all relatives of your recorded model’s instance. Note that auditing all relatives can cause a performance issue in large scale database.

    Example

    from django.db import models
    from django.db.models import Sum
    
    from django_record.models import TimeStampedModel
    from django_record.models import RecordModel
    
    
    # Models
    
    class Article(TimeStampedModel):
        author = models.ForeignKey(User, related_name='articles')
        title = models.CharField(max_length=100)
    
        @property
        def total_comment_count(self):
            return self.comments.count()
    
        @property
        def total_score(self):
            return 0 if not self.votes.exists() else \
            int(self.votes.aggregate(Sum('score'))['score__sum'])
    
        @property
        def full_name_of_author(self):
            return self.author.username
    
    
    class Comment(TimeStampedModel):
        article = models.ForeignKey(Article, related_name='comments')
        text = models.TextField()
    
        @property
        def title_of_article_with_prefix(self):
            return 'title: ' + self.article.title
    
    
    class Vote(models.Model):
        article = models.ForeignKey(Article, related_name='votes')
        score = models.IntegerField()
    
    
    # Record Model
    
    class ArticleRecord(RecordModel):
        recording_model = Article
        recording_fields = [
            'title',
            ('full_name_of_author', models.CharField(max_length=50)),
            ('total_comment_count', models.IntegerField()),
            ('total_score', models.IntegerField())
        ]
    
        class RecordMeta:
           auditing_all_relatives = True
    
    
    class CommentRecord(RecordModel):
        recording_model = Comment
        recording_fields = [
            'text',
            ('title_of_article_with_prefix', models.CharField(max_length=200))
        ]
    
        class RecordMeta:
            audit_all_relatives = True

Note

  • Recursive auditing is not currently supported. Indirect effect only those from direct relatives will be detected and recorded.

  • Only primitive types are supported for properties. You must offer appropriate django field for them.

  • RecordModel is also a subclass of TimeStampedModel, so make sure that you don’t record either ‘created’ or ‘modified’ fields.

Usage

    from django.db import models
    from django.db.models import Sum

    from django_record.models import TimeStampedModel
    from django_record.models import RecordModel


    # Models

    class Article(RecordedModelMixin, TimeStampedModel):
        author = models.ForeignKey(User, related_name='articles')
        title = models.CharField(max_length=100)

        @property
        def total_comment_count(self):
            return self.comments.count()

        @property
        def total_score(self):
            return 0 if not self.votes.exists() else \
            int(self.votes.aggregate(Sum('score'))['score__sum'])

        @property
        def full_name_of_author(self):
            return self.author.username

        recording_fields = [
            'title',
            ('full_name_of_author', models.CharField(max_length=50)),
            ('total_comment_count', models.IntegerField()),
            ('total_score', models.IntegerField()),
        ]
        auditing_relatives = [
           'user', 'comments', 'votes'
        ]


    class Comment(RecordedModelMixin, TimeStampedModel):
        article = models.ForeignKey(Article, related_name='comments')
        text = models.TextField()

        @property
        def title_of_article_with_prefix(self):
            return 'title: ' + self.article.title

        recording_fields = [
           'article',
           ('title_of_article_with_prefix', models.CharField(100)),
        ]
        auditing_relatives = [
           'article',
        ]


    class Vote(models.Model):
        article = models.ForeignKey(Article, related_name='votes')
        score = models.IntegerField()


>>> a =  Article.objects.first()
>>> v = a.votes.first()
>>>
>>> v.score = 999
>>> v.save()                                # recorder creates a new article record, updating 'total_score'.
>>>
>>> r =  a.records.latest()
>>> a.total_score == r.total_score
>>> True

...

>>> count_before = a.total_comment_count
>>>
>>> Comment.objects.create(article=a, text='text of comment')   # recorder creates first record for created comment and
>>>                                                             # a new record for existing article, updating 'total_comment_count'.
>>> r = a.records.latest()
>>> r.total_comment_count == count_before + 1
>>> True

...

>>> records_before_yesterday = d.records.filter(created__lte=yesterday)     # you can filter records by created time.
>>> records_of_today = d.records.filter(created__gte=today)

...

Changes

06.13.2015

  • Following shortcut properties added to recording_model. All of properties below are ordinary django querysets.

    • records_in_hour: Records created in last recent hour.

    • records_in_day: Records created today.

    • records_in_week: Records created in this week.

    • records_in_month: Records created in this month.

    • records_in_year: Records created in this year.

  • Following shortcut properties providing resampled records has been added to recording_model. All of properties below are ordinary django querysets.

    • resampled_records_in_hour: Records created in last recent hour, resampled with minutes.

    • resampled_records_in_day: Records created today, resampled with hours.

    • resampled_records_in_week: Records created in this week, resamped with days.

    • resampled_records_in_month: Records created in this month, resampled with days.

    • resamped_records_in_year: Records created in this year, resampled with months.

05.18.2015

  • RecordedModelMixin added.

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-record-0.2.3.tar.gz (11.8 kB view hashes)

Uploaded Source

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