{"action":"created","comment":{"url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/comments/58629382","id":58629382,"diff_hunk":"@@ -0,0 +1,111 @@\n+import django\n+from django.core import checks\n+from django.utils.functional import cached_property\n+\n+if django.VERSION < (1, 8):\n+    from django.db.models.loading import get_model\n+else:\n+    from django.apps import apps\n+    get_model = apps.get_model\n+\n+\n+UNDEFINED = object()\n+\n+\n+class NonPrimaryForeignKey(object):\n+    \"\"\"\n+    Descriptor class for handling non-primary foreign keys.\n+\n+    The implementation is loosely based on ``GenericForeignKey`` from the\n+    ``django.contrib.contenttypes`` module.\n+    \"\"\"\n+\n+    def __init__(self, to_model, from_field, to_field):","path":"nonprimary_foreignkey/fields.py","position":23,"original_position":23,"commit_id":"f13caa0f144e7b5b6414d9af4bf7b0942808131c","original_commit_id":"f13caa0f144e7b5b6414d9af4bf7b0942808131c","user":{"login":"jdavisp3","id":43582,"avatar_url":"https://avatars.githubusercontent.com/u/43582?v=3","gravatar_id":"","url":"https://api.github.com/users/jdavisp3","html_url":"https://github.com/jdavisp3","followers_url":"https://api.github.com/users/jdavisp3/followers","following_url":"https://api.github.com/users/jdavisp3/following{/other_user}","gists_url":"https://api.github.com/users/jdavisp3/gists{/gist_id}","starred_url":"https://api.github.com/users/jdavisp3/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jdavisp3/subscriptions","organizations_url":"https://api.github.com/users/jdavisp3/orgs","repos_url":"https://api.github.com/users/jdavisp3/repos","events_url":"https://api.github.com/users/jdavisp3/events{/privacy}","received_events_url":"https://api.github.com/users/jdavisp3/received_events","type":"User","site_admin":false},"body":"Might be nice to allow `to_field=None` and default to the name of the `from_field`.","created_at":"2016-04-05T23:09:53Z","updated_at":"2016-04-05T23:09:53Z","html_url":"https://github.com/lucaswiman/nonprimary-foreignkey/pull/2#discussion_r58629382","pull_request_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/comments/58629382"},"html":{"href":"https://github.com/lucaswiman/nonprimary-foreignkey/pull/2#discussion_r58629382"},"pull_request":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2"}}},"pull_request":{"url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2","id":65383781,"html_url":"https://github.com/lucaswiman/nonprimary-foreignkey/pull/2","diff_url":"https://github.com/lucaswiman/nonprimary-foreignkey/pull/2.diff","patch_url":"https://github.com/lucaswiman/nonprimary-foreignkey/pull/2.patch","issue_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/2","number":2,"state":"open","locked":false,"title":"Implement field for handling non-primary foreign keys","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":"This PR introduces a descriptor for Django models that supports foreign-key-like operations on non-primary fields. The intended use case is for models which have some field which uniquely identifies it, but may not be the primary key of the model. Relational databases support this use case well, but Django seems to have completely ignored it.\r\n\r\nDjango also doesn't support non-integer primary keys very well for any models which need to interact with the `contrib.contenttypes` framework, which means some \"natural\" identifiers, like a barcode or UUID are not suitable as a primary key in a django application.\r\n\r\n## Implementation details\r\n\r\nThe key methods to implement are the standard python descriptor fields `__get__` and `__set__`, as well as `get_prefetch_queryset`. The latter method allows maintaining a constant number of queries in bulk operations and list views by calling `QuerySet.prefetch_related`. The implementation is based on `GenericForeignKey` and the confusingly-named `ReverseSingleRelatedObjectDescriptor` used by `ForeignKey`.\r\n\r\nThe test suite runs on Django 1.7-1.9, as well as Python 2.7 and 3.4.\r\n\r\ncc @cmason1978","created_at":"2016-04-05T22:42:06Z","updated_at":"2016-04-05T23:09:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"53d2d63b07abd8e60c4e9d1b3a740ca6bfe82597","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2/commits","review_comments_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2/comments","review_comment_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/comments{/number}","comments_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/2/comments","statuses_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/statuses/f13caa0f144e7b5b6414d9af4bf7b0942808131c","head":{"label":"lucaswiman:initial","ref":"initial","sha":"f13caa0f144e7b5b6414d9af4bf7b0942808131c","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":55456172,"name":"nonprimary-foreignkey","full_name":"lucaswiman/nonprimary-foreignkey","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/nonprimary-foreignkey","description":"Allows foreign-key prefetches on non-primary-key fields","fork":false,"url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey","forks_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/forks","keys_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/teams","hooks_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/hooks","issue_events_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/events{/number}","events_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/events","assignees_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/assignees{/user}","branches_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/branches{/branch}","tags_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/tags","blobs_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/refs{/sha}","trees_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/statuses/{sha}","languages_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/languages","stargazers_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/stargazers","contributors_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/contributors","subscribers_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/subscribers","subscription_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/subscription","commits_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/commits{/sha}","git_commits_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/commits{/sha}","comments_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/comments{/number}","issue_comment_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/comments{/number}","contents_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/contents/{+path}","compare_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/merges","archive_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/downloads","issues_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues{/number}","pulls_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls{/number}","milestones_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/milestones{/number}","notifications_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/labels{/name}","releases_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/releases{/id}","deployments_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/deployments","created_at":"2016-04-05T00:49:01Z","updated_at":"2016-04-05T00:49:20Z","pushed_at":"2016-04-05T22:42:06Z","git_url":"git://github.com/lucaswiman/nonprimary-foreignkey.git","ssh_url":"git@github.com:lucaswiman/nonprimary-foreignkey.git","clone_url":"https://github.com/lucaswiman/nonprimary-foreignkey.git","svn_url":"https://github.com/lucaswiman/nonprimary-foreignkey","homepage":null,"size":22,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"lucaswiman:master","ref":"master","sha":"162ed3241e521275995e2389d15c291c0fccdbc1","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":55456172,"name":"nonprimary-foreignkey","full_name":"lucaswiman/nonprimary-foreignkey","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/nonprimary-foreignkey","description":"Allows foreign-key prefetches on non-primary-key fields","fork":false,"url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey","forks_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/forks","keys_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/teams","hooks_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/hooks","issue_events_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/events{/number}","events_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/events","assignees_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/assignees{/user}","branches_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/branches{/branch}","tags_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/tags","blobs_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/refs{/sha}","trees_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/statuses/{sha}","languages_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/languages","stargazers_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/stargazers","contributors_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/contributors","subscribers_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/subscribers","subscription_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/subscription","commits_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/commits{/sha}","git_commits_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/git/commits{/sha}","comments_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/comments{/number}","issue_comment_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/comments{/number}","contents_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/contents/{+path}","compare_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/merges","archive_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/downloads","issues_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues{/number}","pulls_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls{/number}","milestones_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/milestones{/number}","notifications_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/labels{/name}","releases_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/releases{/id}","deployments_url":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/deployments","created_at":"2016-04-05T00:49:01Z","updated_at":"2016-04-05T00:49:20Z","pushed_at":"2016-04-05T22:42:06Z","git_url":"git://github.com/lucaswiman/nonprimary-foreignkey.git","ssh_url":"git@github.com:lucaswiman/nonprimary-foreignkey.git","clone_url":"https://github.com/lucaswiman/nonprimary-foreignkey.git","svn_url":"https://github.com/lucaswiman/nonprimary-foreignkey","homepage":null,"size":22,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2"},"html":{"href":"https://github.com/lucaswiman/nonprimary-foreignkey/pull/2"},"issue":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/2"},"comments":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/issues/2/comments"},"review_comments":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2/comments"},"review_comment":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/pulls/2/commits"},"statuses":{"href":"https://api.github.com/repos/lucaswiman/nonprimary-foreignkey/statuses/f13caa0f144e7b5b6414d9af4bf7b0942808131c"}}}}