jsondjangodjango-reversion

filter particular field name and value from field_dict of package django-reversion


I have a function which returns json data as history from Version of reversion.models.

from django.http import HttpResponse
from reversion.models import Version
from django.contrib.admin.models import LogEntry
import json
def history_list(request):
    history_list = Version.objects.all().order_by('-revision__date_created')
    data = []
    for i in history_list:
        data.append({
            'date_time': str(i.revision.date_created),
            'user': str(i.revision.user),
            'object': i.object_repr,
            'field': i.revision.comment.split(' ')[-1],
            'new_value_field': str(i.field_dict),
            'type': i.content_type.name,
            'comment': i.revision.comment
        })

    data_ser = json.dumps(data)
    return HttpResponse(data_ser, content_type="application/json")

When I run the above snippet I get the output json as

[{"type": "fruits", "field": "colour", "object": "anyobject", "user": "anyuser", "new_value_field": "{'price': $23, 'weight': 2kgs, 'colour': 'red'}", "comment": "Changed colour."}]

From the function above,

'comment': i.revision.comment

returns json as "comment": "changed colour" and colour is the field which I have written in the function to retrieve it from comment as

'field': i.revision.comment.split(' ')[-1]

But i assume getting fieldname and value from field_dict is a better approach

Problem: from the above json list I would like to filter new_field_value and old_value. In the new_filed_value only value of colour.


Solution

  • Getting the changed fields isn't as easy as checking the comment, as this can be overridden.

    Django-reversion just takes care of storing each version, not comparing.

    Your best option is to look at the django-reversion-compare module and its admin.py code.

    The majority of the code in there is designed to produce a neat side-by-side HTML diff page, but the code should be able to be re-purposed to generate a list of changed fields per object (as there can be more than one changed field per version).


    The code should* include a view independent way to get the changed fields at some point, but this should get you started:

    from reversion_compare.admin import CompareObjects
    from reversion.revisions import default_revision_manager
    
    def changed_fields(obj, version1, version2):
        """
        Create a generic html diff from the obj between version1 and version2:
            A diff of every changes field values.
        This method should be overwritten, to create a nice diff view
        coordinated with the model.
        """
        diff = []
    
        # Create a list of all normal fields and append many-to-many fields
        fields = [field for field in obj._meta.fields]
        concrete_model = obj._meta.concrete_model
        fields += concrete_model._meta.many_to_many
    
        # This gathers the related reverse ForeignKey fields, so we can do ManyToOne compares
        reverse_fields = []
        # From: http://stackoverflow.com/questions/19512187/django-list-all-reverse-relations-of-a-model
        changed_fields = []
        for field_name in obj._meta.get_all_field_names():
            f = getattr(
                obj._meta.get_field_by_name(field_name)[0],
                'field',
                None
            )
            if isinstance(f, models.ForeignKey) and f not in fields:
                reverse_fields.append(f.rel)
    
        fields += reverse_fields
    
        for field in fields:
            try:
                field_name = field.name
            except:
                # is a reverse FK field
                field_name = field.field_name
    
            is_reversed = field in reverse_fields
            obj_compare = CompareObjects(field, field_name, obj, version1, version2, default_revision_manager, is_reversed)
    
            if obj_compare.changed():
                changed_fields.append(field)
    
        return changed_fields
    

    This can then be called like so:

    changed_fields(MyModel,history_list_item1, history_list_item2)
    

    Where history_list_item1 and history_list_item2 correspond to various actual Version items.

    *: Said as a contributor, I'll get right on it.