Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Django 1.11 and Python 3 #12

Merged
merged 13 commits into from
Apr 16, 2018
Merged

Conversation

WTFox
Copy link

@WTFox WTFox commented Jan 22, 2018

Adds support for Python 3 and Django 1.11.

A high level overview

  • Removes the use of django.conf.urls.patterns since support was added for plain lists.
  • Replaces instances of unicode with str. Also adds python_2_unicode_compatible to Django models so that only __str__() has to be implemented.
  • Casts dict.keys() to a list since it's not that.
  • Removes instances of del dict[some_key] in iterations to avoid throwing a Iterable size changed during iteration error.
  • Adds a helper function to tests to make comparing lists of dicts more consistent.
  • Updates the project meta information.

Notes:

  • django.conf.urls.patterns support was removed in 1.8, so I've updated the readme to suggest using Django >= 1.8
  • This project is compatible with python >= 2.7 and python 3 >= 3.6.

Dangers with upgrading to Python 3

While this library works out of the box with Python 3 there is a caveat that could cause unexpected side effects. For that to happen the following conditions have to be true:

  • You have a GenericRelation to simple_audit.Audit from a Model that you've registered with Simple Audit
  • You're upgrading from Python 2 to Python 3

Lets say that a model contains a GenericRelation back to simple_audit.Audit so that the audits can be queried like instance.audits.all() and a new row gets added to the db. Simple Audit will mark that as an ADD operation and will create a record for it. It will look for all fields on the model and get the new and existing values for each one. It works as expected for all fields until it gets to our audits field, the GenericRelation.

What happens is that it looks for the value and calls unicode on it. The value in this case is a GenericRelationObjectManager instance, but calling str() on it will resolve it to simple_audit.Audit.None. This went largely unnoticed because the subsequent changes to the model would produce the same value for audits.

Python 3 comes in like a wrecking ball and obviously unicode() is no longer available. We have str() now for string types. Replacing the unicode usage with str does something entirely unexpected. Instead of resolving to the same simple_audit.Audit.None string, it now produces a string of the objects repr with memory locations e.g. '<django.contrib.contenttypes.fields.GenericRelatedObjectManager object at 0x10a72d510>'

This matters because when Simple Audit gets triggered, it gets the existing value and new instance value of all fields on the model - including fields that have a GenericRelation back to simple_audit.Audit. Which means there will always be a difference because of memory locations being printed in the string.

Long story short, upgrading to Python 3 can and probably will add extraneous information to your audit_change table and will muddy the information making it harder to digest.

@WTFox WTFox force-pushed the support-django1.11-python2or3 branch from b15e13d to 542fc71 Compare January 22, 2018 20:12
@WTFox WTFox changed the title Support django1.11 python2or3 Add support for Django 1.11 and Python 3 Jan 22, 2018
@WTFox WTFox self-assigned this Jan 22, 2018
@WTFox WTFox requested a review from sburns January 22, 2018 22:00
@WTFox
Copy link
Author

WTFox commented Jan 22, 2018

@sburns This seems ready according to CI, but I'd like to test this out in our app first. Not exactly sure how to accomplish that so looking for your advice.

Copy link

@sburns sburns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I think this looks fine, but I do want to test in our staging environment. Can you build a playbook of things to exercise to ensure simple audit is still working (ie what models we audit, where to see they're still working, etc)

@@ -236,9 +234,9 @@ def save_audit(instance, operation, kwargs={}):
format_value(v[1]),
) for k, v in changed_fields.items()])
elif operation == Audit.DELETE:
description = _('Deleted %s') % unicode(instance)
description = _('Deleted %s') % str(instance)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str on python2 does very different things than unicode. Should we bring in six and use six.text_type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's necessary in this case. str and unicode definitely behave differently, but with the unicode_literals import at the top of this file it should bridge the gap where needed. Also, this is just storing a string in the db logging what changed and how it changed.

This is what I was using for reference link

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unicode_literals stops you from having to type the u on u'my hardcoded string'.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch. I can bring in six.text_type and replace the instances of str with that.

@sburns
Copy link

sburns commented Jan 23, 2018

As far as how to test in staging, we can point our reqs.txt to this version. We may want to reach out to @leandrosouza and see if there's a possibility of us taking over maintainer rights so we could cut a release to PyPI

@leandrosouza
Copy link

Don't worry guys I will test by tomorrow and deploy a new version to Pypi

@sburns
Copy link

sburns commented Jan 23, 2018

@WTFox can you make a PR to his fork?

@WTFox
Copy link
Author

WTFox commented Jan 23, 2018

@WTFox can you make a PR to his fork?

Submitted leandrosouza#27

@WTFox
Copy link
Author

WTFox commented Jan 30, 2018

As stated in the PR body, there will be some differences when upgrading to Python 3. We have two ways of mitigating this as far as I can tell.

  1. Implement a solution to specify fields to ignore when auditing a Model.*
  2. Remove the GenericRelations to simple_audit.Audit and do reverse lookups with Django's ContentTypes
  • optionally ignore fields based on a [] variable in the Model's Meta. If we specify in there what fields we want to ignore auditing, we can exclude those fields in the to_dict() method that builds dictionaries to compare.

@sburns
Copy link

sburns commented Jan 31, 2018

I think it will be easier and less intrusive to simple-audit for us to remove our GenericRelations to simple_audit.Audit and write a shareable function to take an instance and return a queryset to its audits. We could then do something like

class RegisteredModel:
    ...

    @property
    def audits(self):
        return function_to_get_audits(self)

And then not need much of an outward API change for ourselves because instance.audits will still do what anything else expects.

@briandailey
Copy link

@sburns @WTFox what happened with this? not important anymore?

@WTFox
Copy link
Author

WTFox commented Apr 16, 2018

@sburns @WTFox what happened with this? not important anymore?

@briandailey We're actually pinned to this commit in our requirements.txt. The author was going to review this and merge it upstream, but it seems that never happened. So, being that this is (and has been) used in production for 4 months 😬 , I'd say this can be merged.

@WTFox WTFox merged commit ad61127 into master Apr 16, 2018
@briandailey briandailey deleted the support-django1.11-python2or3 branch April 16, 2018 21:31
@briandailey briandailey restored the support-django1.11-python2or3 branch April 16, 2018 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants