-
-
Notifications
You must be signed in to change notification settings - Fork 366
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
Fix handling of deferred fields on django 1.10+ #317
Fix handling of deferred fields on django 1.10+ #317
Conversation
Codecov Report
@@ Coverage Diff @@
## master #317 +/- ##
==========================================
+ Coverage 94.81% 98.42% +3.61%
==========================================
Files 2 2
Lines 212 254 +42
==========================================
+ Hits 201 250 +49
+ Misses 11 4 -7
Continue to review full report at Codecov.
|
😬 I'll investigate why code coverage went down by so much. |
I fixed a few coverage issues, but I think maybe the codecov integration isn't running the full test suite? All of the code which only runs on older versions of django shows as "not covered". I think this is an artifact of the coverage setup, and not reflective of an inadequacy in the tests I added. |
Support for older versions of django was dropped in 3.0.0.
This commit adds a collection of wrapper classes for tracking fields while still using custom descriptors that may be present. This fixes a bug where deferring a model field with a custom descriptor meant that the descriptor was overridden in all subsequent queries.
As far as I can tell, no changes to documentation is required.
The complications are that when the attribute is set in Django 1.10, it no longer counts as a deferred attribute, and it is not retrieved from the database. Naively updating __set__ to retrieve the value if it is deferred leads to infinite recursion because accessing the attribute involves loading data from the database and trying to set the attribute based on that value. This commit introduces a somewhat hacky flag that records whether we're already trying to set the attribute further up in the call stack.
This should maintain compatibility with the next version of django.
Hopefully this will combine the coverage reports.
… all tox environments run on the travis environment. Note that it is run as one per python version, but multiple versions of django are tested in each.
OK, I got the codecov checks passing by correctly aggregating the different test environments and adding a few assertions to the tests. |
Hi! I don't have my head super in this, but I see that my tests are there and passing, which is great. 👍 It sounds like you can join Jazzband and merge this yourself, if you're comfortable with the code at this point: #279 (comment) Alternatively you might @ a few folks who have merged PRs in the last year for another pair of eyes. |
(Oh one nitpick, looks like the merge on CHANGES.rst put a line in the wrong place.) |
@tony Would you care to take a look? |
@jcushman @lucaswiman I don't use the deferred fields part of django-model-utils. I breezed through the code and it LGTM. I think that's the best I can offer. I don't want to offer more since I don't have a Django website using that part of django-model-utils to test on fully. |
Fixes #278. cc @utapyngo @djstein
Problem
Prior to Django 1.10, deferred attributes were handled by constructing a new model class with custom descriptors for the deferred attributes. After 1.10, deferred fields are tracked by whether the attribute is present in the instance
__dict__
.FieldTracker
handled tracking on these fields by overwriting the descriptors on the model class. This means that in 1.10+, the instance model class is the model, soFieldTracker
can introduce changes to the base class whenever deferred fields are used that persist on other queries. #278 This is reproduced in a test case.Solution
I updated the
finalize_class
method to wrap field descriptors. This preserves the custom descriptor behavior, and means that deferring fields does not lead to permanent changes in base model classes. This is only done for django versions after 1.10: the previous behavior is preserved for 1.8 and 1.9.Since the relevant code branches behavior on unsupported versions of Django, I also removed all branching behavior for versions of django listed as unsupported in the CHANGELOG.
Commandments
CHANGES.rst
file to describe the changes, and quote according issue withGH-<issue_number>
.tracker._deferred_fields
attribute is not set on django versions >=1.10. Since_deferred_fields
is a private attribute, I wouldn't consider this a breaking change.Regarding PEP8, I ran
flake8
, and it generated a number of errors. I think my code didn't introduce new PEP8 violations, but I'm not sure. If you want, I can submit a separate PR fixing all the PEP8 violations, and rebase onto this branch.