{"action":"created","comment":{"url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/comments/48161971","id":48161971,"diff_hunk":"@@ -701,106 +564,61 @@ def _add_node(self, parent, model, relation, reverse, related_name,\n             parent.children.append(node)\n \n     def _find_relations(self, node, depth=0):\n-        \"\"\"Finds all relations given a node.\n-\n-        NOTE: the many-to-many relations are evaluated first to prevent\n-        'through' models being bound as a ForeignKey relationship.\n-        \"\"\"\n+        \"\"\"Finds all relations given a node.\"\"\"\n         depth += 1\n \n         model = node.model\n-        opts = model._meta\n-\n-        # determine relational fields to determine paths\n-        forward_fields = opts.fields\n-        reverse_fields = opts.get_all_related_objects()\n-\n-        forward_o2o = filter(self._filter_one2one, forward_fields)\n-        reverse_o2o = filter(self._filter_related_one2one, reverse_fields)\n \n-        forward_fk = filter(self._filter_fk, forward_fields)\n-        reverse_fk = filter(self._filter_related_fk, reverse_fields)\n-\n-        forward_m2m = filter(self._filter_m2m, opts.many_to_many)\n-        reverse_m2m = filter(self._filter_related_m2m,\n-                             opts.get_all_related_many_to_many_objects())\n-\n-        # iterate m2m relations\n-        for f in forward_m2m:\n-            kwargs = {\n-                'parent': node,\n-                'model': f.rel.to,\n-                'relation': 'manytomany',\n-                'reverse': False,\n-                'related_name': f.name,\n-                'accessor_name': f.name,\n-                'nullable': True,\n-                'depth': depth,\n-            }\n-            self._add_node(**kwargs)\n+        # NOTE: the many-to-many relations are evaluated first to prevent\n+        # 'through' models being bound as a ForeignKey relationship.\n+        fields = sorted(model._meta.get_fields(), reverse=True,\n+                        key=lambda f: f.many_to_many)\n \n-        # iterate over related m2m fields\n-        for r in reverse_m2m:\n-            kwargs = {\n-                'parent': node,\n-                'model': r.model,\n-                'relation': 'manytomany',\n-                'reverse': True,\n-                'related_name': r.field.related_query_name(),\n-                'accessor_name': r.get_accessor_name(),\n-                'nullable': True,\n-                'depth': depth,\n-            }\n-            self._add_node(**kwargs)\n-\n-        # iterate over one2one fields\n-        for f in forward_o2o:\n-            kwargs = {\n-                'parent': node,\n-                'model': f.rel.to,\n-                'relation': 'onetoone',\n-                'reverse': False,\n-                'related_name': f.name,\n-                'accessor_name': f.name,\n-                'nullable': False,\n-                'depth': depth,\n-            }\n-            self._add_node(**kwargs)\n-\n-        # iterate over related one2one fields\n-        for r in reverse_o2o:\n-            kwargs = {\n-                'parent': node,\n-                'model': r.model,\n-                'relation': 'onetoone',\n-                'reverse': True,\n-                'related_name': r.field.related_query_name(),\n-                'accessor_name': r.get_accessor_name(),\n-                'nullable': False,\n-                'depth': depth,\n-            }\n-            self._add_node(**kwargs)\n+        # determine relational fields to determine paths\n+        forward_fields = [\n+            f for f in fields\n+            if (f.one_to_one or f.many_to_many or f.many_to_one)\n+            and not f.auto_created\n+            and self._join_allowed(f.model, f.rel.to, f)\n+        ]\n+        reverse_fields = [\n+            f for f in fields\n+            if (f.one_to_many or f.one_to_one or f.many_to_many)\n+            and f.auto_created\n+            and self._join_allowed(f.model, f.related_model, f.field)\n+        ]\n+\n+        def get_relation_type(f):\n+            if f.one_to_one:\n+                return 'onetone'\n+            elif f.many_to_many:\n+                return 'manytomany'\n+            elif f.one_to_many or f.many_to_one:\n+                return 'foreignkey'\n+            else:\n+                raise ValueError(f)  # pragma: no cover","path":"modeltree/tree.py","position":408,"original_position":408,"commit_id":"ae5d7420168593336bf99a06f269fd1dc316a44e","original_commit_id":"ae5d7420168593336bf99a06f269fd1dc316a44e","user":{"login":"bruth","id":515142,"avatar_url":"https://avatars.githubusercontent.com/u/515142?v=3","gravatar_id":"","url":"https://api.github.com/users/bruth","html_url":"https://github.com/bruth","followers_url":"https://api.github.com/users/bruth/followers","following_url":"https://api.github.com/users/bruth/following{/other_user}","gists_url":"https://api.github.com/users/bruth/gists{/gist_id}","starred_url":"https://api.github.com/users/bruth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bruth/subscriptions","organizations_url":"https://api.github.com/users/bruth/orgs","repos_url":"https://api.github.com/users/bruth/repos","events_url":"https://api.github.com/users/bruth/events{/privacy}","received_events_url":"https://api.github.com/users/bruth/received_events","type":"User","site_admin":false},"body":"Nit. No need to put this in the `else` block since all previous blocks return.","created_at":"2015-12-21T16:25:01Z","updated_at":"2015-12-21T16:25:01Z","html_url":"https://github.com/chop-dbhi/modeltree/pull/22#discussion_r48161971","pull_request_url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22","_links":{"self":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/comments/48161971"},"html":{"href":"https://github.com/chop-dbhi/modeltree/pull/22#discussion_r48161971"},"pull_request":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22"}}},"pull_request":{"url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22","id":52497850,"html_url":"https://github.com/chop-dbhi/modeltree/pull/22","diff_url":"https://github.com/chop-dbhi/modeltree/pull/22.diff","patch_url":"https://github.com/chop-dbhi/modeltree/pull/22.patch","issue_url":"https://api.github.com/repos/chop-dbhi/modeltree/issues/22","number":22,"state":"open","locked":false,"title":"Django 1.8+ support","user":{"login":"lucaswiman","id":123110,"avatar_url":"https://avatars.githubusercontent.com/u/123110?v=3","gravatar_id":"","url":"https://api.github.com/users/lucaswiman","html_url":"https://github.com/lucaswiman","followers_url":"https://api.github.com/users/lucaswiman/followers","following_url":"https://api.github.com/users/lucaswiman/following{/other_user}","gists_url":"https://api.github.com/users/lucaswiman/gists{/gist_id}","starred_url":"https://api.github.com/users/lucaswiman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lucaswiman/subscriptions","organizations_url":"https://api.github.com/users/lucaswiman/orgs","repos_url":"https://api.github.com/users/lucaswiman/repos","events_url":"https://api.github.com/users/lucaswiman/events{/privacy}","received_events_url":"https://api.github.com/users/lucaswiman/received_events","type":"User","site_admin":false},"body":"@bruth This pull request is a work in progress to add Django 1.8+ support for `modeltree`.\r\n\r\nI wanted to submit this earlier in an uncompleted state to ask what versions of Django you're interested in supporting.\r\n\r\nAll django versions below 1.8 have been end-of-lifed with the release of Django 1.9. Would you be comfortable dropping Django 1.7 and below from list of supported versions?  Supporting Django 1.4+ seems to introduce a lot of conditional importing logic, and Django 1.8 introduced some pretty substantial changes to how the ORM works.\r\n\r\n## Changes so far\r\n- Added 1.8-1.9 to the travis config and setup file.\r\n- Added a tox.ini file to make it easy to run tests across multiple versions of Django. [Happy to remove this if you'd rather not have it clutter up the repo.]\r\n- Fixed the basic import bugs from deprecations in various versions of Django.\r\n\r\n## Still TODO\r\n- [x] Fix remaining test failures\r\n\r\nThanks very much for the work you've put in to this library!","created_at":"2015-12-03T06:49:06Z","updated_at":"2015-12-21T16:25:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"34d9a9c605083b5efef074dc9b34d90f10817802","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22/commits","review_comments_url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22/comments","review_comment_url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/comments{/number}","comments_url":"https://api.github.com/repos/chop-dbhi/modeltree/issues/22/comments","statuses_url":"https://api.github.com/repos/chop-dbhi/modeltree/statuses/ae5d7420168593336bf99a06f269fd1dc316a44e","head":{"label":"lucaswiman:django-1.8-support","ref":"django-1.8-support","sha":"ae5d7420168593336bf99a06f269fd1dc316a44e","user":{"login":"lucaswiman","id":123110,"avatar_url":"https://avatars.githubusercontent.com/u/123110?v=3","gravatar_id":"","url":"https://api.github.com/users/lucaswiman","html_url":"https://github.com/lucaswiman","followers_url":"https://api.github.com/users/lucaswiman/followers","following_url":"https://api.github.com/users/lucaswiman/following{/other_user}","gists_url":"https://api.github.com/users/lucaswiman/gists{/gist_id}","starred_url":"https://api.github.com/users/lucaswiman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lucaswiman/subscriptions","organizations_url":"https://api.github.com/users/lucaswiman/orgs","repos_url":"https://api.github.com/users/lucaswiman/repos","events_url":"https://api.github.com/users/lucaswiman/events{/privacy}","received_events_url":"https://api.github.com/users/lucaswiman/received_events","type":"User","site_admin":false},"repo":{"id":47302935,"name":"modeltree","full_name":"lucaswiman/modeltree","owner":{"login":"lucaswiman","id":123110,"avatar_url":"https://avatars.githubusercontent.com/u/123110?v=3","gravatar_id":"","url":"https://api.github.com/users/lucaswiman","html_url":"https://github.com/lucaswiman","followers_url":"https://api.github.com/users/lucaswiman/followers","following_url":"https://api.github.com/users/lucaswiman/following{/other_user}","gists_url":"https://api.github.com/users/lucaswiman/gists{/gist_id}","starred_url":"https://api.github.com/users/lucaswiman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lucaswiman/subscriptions","organizations_url":"https://api.github.com/users/lucaswiman/orgs","repos_url":"https://api.github.com/users/lucaswiman/repos","events_url":"https://api.github.com/users/lucaswiman/events{/privacy}","received_events_url":"https://api.github.com/users/lucaswiman/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/lucaswiman/modeltree","description":"Django model relation tree traversal, i.e. automatic joins.","fork":true,"url":"https://api.github.com/repos/lucaswiman/modeltree","forks_url":"https://api.github.com/repos/lucaswiman/modeltree/forks","keys_url":"https://api.github.com/repos/lucaswiman/modeltree/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lucaswiman/modeltree/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lucaswiman/modeltree/teams","hooks_url":"https://api.github.com/repos/lucaswiman/modeltree/hooks","issue_events_url":"https://api.github.com/repos/lucaswiman/modeltree/issues/events{/number}","events_url":"https://api.github.com/repos/lucaswiman/modeltree/events","assignees_url":"https://api.github.com/repos/lucaswiman/modeltree/assignees{/user}","branches_url":"https://api.github.com/repos/lucaswiman/modeltree/branches{/branch}","tags_url":"https://api.github.com/repos/lucaswiman/modeltree/tags","blobs_url":"https://api.github.com/repos/lucaswiman/modeltree/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lucaswiman/modeltree/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lucaswiman/modeltree/git/refs{/sha}","trees_url":"https://api.github.com/repos/lucaswiman/modeltree/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lucaswiman/modeltree/statuses/{sha}","languages_url":"https://api.github.com/repos/lucaswiman/modeltree/languages","stargazers_url":"https://api.github.com/repos/lucaswiman/modeltree/stargazers","contributors_url":"https://api.github.com/repos/lucaswiman/modeltree/contributors","subscribers_url":"https://api.github.com/repos/lucaswiman/modeltree/subscribers","subscription_url":"https://api.github.com/repos/lucaswiman/modeltree/subscription","commits_url":"https://api.github.com/repos/lucaswiman/modeltree/commits{/sha}","git_commits_url":"https://api.github.com/repos/lucaswiman/modeltree/git/commits{/sha}","comments_url":"https://api.github.com/repos/lucaswiman/modeltree/comments{/number}","issue_comment_url":"https://api.github.com/repos/lucaswiman/modeltree/issues/comments{/number}","contents_url":"https://api.github.com/repos/lucaswiman/modeltree/contents/{+path}","compare_url":"https://api.github.com/repos/lucaswiman/modeltree/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lucaswiman/modeltree/merges","archive_url":"https://api.github.com/repos/lucaswiman/modeltree/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lucaswiman/modeltree/downloads","issues_url":"https://api.github.com/repos/lucaswiman/modeltree/issues{/number}","pulls_url":"https://api.github.com/repos/lucaswiman/modeltree/pulls{/number}","milestones_url":"https://api.github.com/repos/lucaswiman/modeltree/milestones{/number}","notifications_url":"https://api.github.com/repos/lucaswiman/modeltree/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lucaswiman/modeltree/labels{/name}","releases_url":"https://api.github.com/repos/lucaswiman/modeltree/releases{/id}","created_at":"2015-12-03T02:48:22Z","updated_at":"2015-12-03T02:48:23Z","pushed_at":"2015-12-21T07:10:50Z","git_url":"git://github.com/lucaswiman/modeltree.git","ssh_url":"git@github.com:lucaswiman/modeltree.git","clone_url":"https://github.com/lucaswiman/modeltree.git","svn_url":"https://github.com/lucaswiman/modeltree","homepage":"http://modeltree.harvest.io","size":384,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"chop-dbhi:master","ref":"master","sha":"b6454a052db4475d97ab240cd68794e37798888c","user":{"login":"chop-dbhi","id":709892,"avatar_url":"https://avatars.githubusercontent.com/u/709892?v=3","gravatar_id":"","url":"https://api.github.com/users/chop-dbhi","html_url":"https://github.com/chop-dbhi","followers_url":"https://api.github.com/users/chop-dbhi/followers","following_url":"https://api.github.com/users/chop-dbhi/following{/other_user}","gists_url":"https://api.github.com/users/chop-dbhi/gists{/gist_id}","starred_url":"https://api.github.com/users/chop-dbhi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chop-dbhi/subscriptions","organizations_url":"https://api.github.com/users/chop-dbhi/orgs","repos_url":"https://api.github.com/users/chop-dbhi/repos","events_url":"https://api.github.com/users/chop-dbhi/events{/privacy}","received_events_url":"https://api.github.com/users/chop-dbhi/received_events","type":"Organization","site_admin":false},"repo":{"id":1596928,"name":"modeltree","full_name":"chop-dbhi/modeltree","owner":{"login":"chop-dbhi","id":709892,"avatar_url":"https://avatars.githubusercontent.com/u/709892?v=3","gravatar_id":"","url":"https://api.github.com/users/chop-dbhi","html_url":"https://github.com/chop-dbhi","followers_url":"https://api.github.com/users/chop-dbhi/followers","following_url":"https://api.github.com/users/chop-dbhi/following{/other_user}","gists_url":"https://api.github.com/users/chop-dbhi/gists{/gist_id}","starred_url":"https://api.github.com/users/chop-dbhi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chop-dbhi/subscriptions","organizations_url":"https://api.github.com/users/chop-dbhi/orgs","repos_url":"https://api.github.com/users/chop-dbhi/repos","events_url":"https://api.github.com/users/chop-dbhi/events{/privacy}","received_events_url":"https://api.github.com/users/chop-dbhi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/chop-dbhi/modeltree","description":"Django model relation tree traversal, i.e. automatic joins.","fork":false,"url":"https://api.github.com/repos/chop-dbhi/modeltree","forks_url":"https://api.github.com/repos/chop-dbhi/modeltree/forks","keys_url":"https://api.github.com/repos/chop-dbhi/modeltree/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chop-dbhi/modeltree/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chop-dbhi/modeltree/teams","hooks_url":"https://api.github.com/repos/chop-dbhi/modeltree/hooks","issue_events_url":"https://api.github.com/repos/chop-dbhi/modeltree/issues/events{/number}","events_url":"https://api.github.com/repos/chop-dbhi/modeltree/events","assignees_url":"https://api.github.com/repos/chop-dbhi/modeltree/assignees{/user}","branches_url":"https://api.github.com/repos/chop-dbhi/modeltree/branches{/branch}","tags_url":"https://api.github.com/repos/chop-dbhi/modeltree/tags","blobs_url":"https://api.github.com/repos/chop-dbhi/modeltree/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chop-dbhi/modeltree/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chop-dbhi/modeltree/git/refs{/sha}","trees_url":"https://api.github.com/repos/chop-dbhi/modeltree/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chop-dbhi/modeltree/statuses/{sha}","languages_url":"https://api.github.com/repos/chop-dbhi/modeltree/languages","stargazers_url":"https://api.github.com/repos/chop-dbhi/modeltree/stargazers","contributors_url":"https://api.github.com/repos/chop-dbhi/modeltree/contributors","subscribers_url":"https://api.github.com/repos/chop-dbhi/modeltree/subscribers","subscription_url":"https://api.github.com/repos/chop-dbhi/modeltree/subscription","commits_url":"https://api.github.com/repos/chop-dbhi/modeltree/commits{/sha}","git_commits_url":"https://api.github.com/repos/chop-dbhi/modeltree/git/commits{/sha}","comments_url":"https://api.github.com/repos/chop-dbhi/modeltree/comments{/number}","issue_comment_url":"https://api.github.com/repos/chop-dbhi/modeltree/issues/comments{/number}","contents_url":"https://api.github.com/repos/chop-dbhi/modeltree/contents/{+path}","compare_url":"https://api.github.com/repos/chop-dbhi/modeltree/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chop-dbhi/modeltree/merges","archive_url":"https://api.github.com/repos/chop-dbhi/modeltree/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chop-dbhi/modeltree/downloads","issues_url":"https://api.github.com/repos/chop-dbhi/modeltree/issues{/number}","pulls_url":"https://api.github.com/repos/chop-dbhi/modeltree/pulls{/number}","milestones_url":"https://api.github.com/repos/chop-dbhi/modeltree/milestones{/number}","notifications_url":"https://api.github.com/repos/chop-dbhi/modeltree/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chop-dbhi/modeltree/labels{/name}","releases_url":"https://api.github.com/repos/chop-dbhi/modeltree/releases{/id}","created_at":"2011-04-11T01:09:57Z","updated_at":"2015-11-11T21:42:50Z","pushed_at":"2015-12-21T07:10:50Z","git_url":"git://github.com/chop-dbhi/modeltree.git","ssh_url":"git@github.com:chop-dbhi/modeltree.git","clone_url":"https://github.com/chop-dbhi/modeltree.git","svn_url":"https://github.com/chop-dbhi/modeltree","homepage":"http://modeltree.harvest.io","size":287,"stargazers_count":15,"watchers_count":15,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":5,"mirror_url":null,"open_issues_count":6,"forks":5,"open_issues":6,"watchers":15,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22"},"html":{"href":"https://github.com/chop-dbhi/modeltree/pull/22"},"issue":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/issues/22"},"comments":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/issues/22/comments"},"review_comments":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22/comments"},"review_comment":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/pulls/22/commits"},"statuses":{"href":"https://api.github.com/repos/chop-dbhi/modeltree/statuses/ae5d7420168593336bf99a06f269fd1dc316a44e"}}}}