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

Create new FieldTracker that works well with foreign keys #52

Merged
merged 15 commits into from
Jun 3, 2013
Merged

Conversation

treyhunner
Copy link
Member

This will fix the issue mentioned in #43.

@treyhunner
Copy link
Member Author

I'm not sure whether it's overkill to duplicate all FieldTracker tests for ModelTracker. At the moment inheritance is used at least, but the models are just copy-pasted.

self.fields = fields
self.field_map = field_map

def get_field(self, field):
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should be named something like get_field_value instead, since that's what it returns, not a Field object.

@carljm
Copy link
Collaborator

carljm commented May 23, 2013

Looks good! Made a few comments.

Regarding the tests, I would probably be inclined to cut out all the ModelTracker tests except for the one test necessary to gain code coverage of its set_field_map() method (and demonstrate how its behavior differs from FieldTracker). I don't feel strongly about it though; all its tests will be removed anyway in a few releases when we remove it.

Also, this PR will need documentation updates before it's merged. I'd handle the docs similarly to the code; change the main documentation to refer entirely to FieldTracker; at the end of the section have a note with a brief mention of ModelTracker, how its behavior differs, and the fact that it's deprecated and shouldn't be used in new code.

Return None values instead of an empty dict
The FieldTracker has_changed method no longer returns True for any input
when the instance is unsaved and no longer raises a FieldError for
fields after the first save.  The original ModelTracker behavior is
maintained.
@treyhunner
Copy link
Member Author

ModelTracker and FieldTracker now differ subtly for both changed and has_changed methods (as well as current and previous for foreign key fields).

@carljm I believe I've taken all of your feedback so far into account at this point.

Still to do:

  • update documentation to refer to FieldTracker
  • mention the deprecated ModelTracker and explain how it differs
  • update CHANGES file to note new FieldTracker and deprecated ModelTracker
  • add test for tracking non-field attributes with FieldTracker


def test_first_save(self):
def test_pre_save_changed(self):
self.assertChanged(name=None)
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a little odd to me, but I'm not sure how we could fix this in a general way.

What's going on here:

# default values
assert self.instance.name == ''
assert self.instance.number is None

# previous values default to None for new instance
assert self.instance.tracker.previous('name') is None
assert self.instance.tracker.previous('number') is None

# '' != None
assert self.instance.tracker.has_changed('name')
#  None == None
assert not self.instance.tracker.has_changed('number')

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is actually fine. I think the most important thing is to be clear about what the invariants are, and be consistent: e.g. that an unsaved instance is considered to have None for all previous() field values, and has_changed simply compares previous() with the current attribute value.

@carljm
Copy link
Collaborator

carljm commented May 27, 2013

Looking good! A couple quick notes on the tests; these don't need to block this PR (especially since they pre-date it), but things to consider in the future:

  • For things which should only be tested on a specific Django version, it's better to use real test-skipping (the skipIf decorator and friends from unittest2) than if statements in tests. This is possible now that we no longer support Django 1.2.
  • For methods which test equality with a data dict (e.g. assertChanged) it's better to explicitly pass in a single dict than to exploit **kwargs, for several reasons: (1) the test gives a clearer visual picture of the data structure its asserting against, and (2) you don't run into smelliness when you need additional optional args (like tracker). Popping arbitrary args out of a **kwargs that is otherwise being treated as an opaque data container is a namespace-invasion that's a recipe for future confusion. It's not worth the minimal savings in typing in the tests.

@treyhunner
Copy link
Member Author

For things which should only be tested on a specific Django version, it's better to use real test-skipping (the skipIf decorator and friends from unittest2) than if statements in tests. This is possible now that we no longer support Django 1.2.

I agree. The Django 1.5 check should be refactored into a separate test with a skipIf.

For methods which test equality with a data dict (e.g. assertChanged) it's better to explicitly pass in a single dict than to exploit *_kwargs, for several reasons: (1) the test gives a clearer visual picture of the data structure its asserting against, and (2) you don't run into smelliness when you need additional optional args (like tracker). Popping arbitrary args out of a *_kwargs that is otherwise being treated as an opaque data container is a namespace-invasion that's a recipe for future confusion. It's not worth the minimal savings in typing in the tests.

I agree. This is especially confusing in the current tests because I've used **kwargs for multiple field-based assertions in a single method (good imo) and for comparison to a dict (bad). I definitely think the latter should be changed from **kwargs to a dict.

@carljm
Copy link
Collaborator

carljm commented May 28, 2013

Odd, the comments about get_field_map seem to have disappeared.

I hadn't thought of the tracking-non-fields use case. If you add a test for that, I'm fine with your implementation of get_field_map - now I understand why it's more complicated than I thought it needed to be :-)

@carljm
Copy link
Collaborator

carljm commented Jun 3, 2013

Besides the minor doc additions I just mentioned, is this waiting for anything else?

@treyhunner
Copy link
Member Author

Feel free to add both of those notes to the branch if you have a particular
wording in mind. I will review the current tests later today to ensure the
coverage is appropriate before we merge.
On Jun 3, 2013 10:32 AM, "Carl Meyer" notifications@github.com wrote:

Besides the minor doc additions I just mentioned, is this waiting for
anything else?


Reply to this email directly or view it on GitHubhttps://github.com//pull/52#issuecomment-18857199
.

* master:
  Remove misinformation from changelog; oops.
  Tweak AUTHORS and changelog.
  PassThroughManager calls superclass `get_query_set`.
  Update AUTHORS and changelog.
  Fix tox.ini so runtests.sh works.
  Test and fix for second grandchild bug.
@carljm
Copy link
Collaborator

carljm commented Jun 3, 2013

Ok, doc updates pushed. If you're willing, please review my summary of the differences between ModelTracker and FieldTracker to ensure I didn't miss anything or get any subtleties wrong.

@treyhunner
Copy link
Member Author

The differences listed in the README note look good to me.

I just added the missing tests for tracking a non-field model attribute. Those tests fail right now because round(8.5) == 8 in Python 3. After I fix the tests everything should be ready to merge.

@treyhunner
Copy link
Member Author

I fixed the rounding issue. This should be good to merge now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants