{"action":"created","comment":{"url":"https://api.github.com/repos/django/django/pulls/comments/66839358","id":66839358,"diff_hunk":"@@ -0,0 +1,134 @@\n+from __future__ import unicode_literals\n+\n+import hashlib\n+\n+from django.utils.encoding import force_bytes\n+\n+__all__ = ['Index']\n+\n+# Max length restriction on names imposed by Oracle\n+MAX_NAME_LENGTH = 30\n+\n+\n+class BaseIndex(object):\n+\n+    # The type of index - BTree, Hash, RTree, etc.\n+    # Used in deconstruction to differentiate indexes of different types with\n+    # the same set of fields.\n+    index_type = None\n+\n+    def __init__(self, *fields, **kwargs):\n+        if not fields:\n+            raise ValueError(\"Minimum one field required to define an index.\")\n+        self.fields = fields\n+        self._name = kwargs.get('name')\n+        if 'model' in kwargs:\n+            self.model = kwargs['model']\n+\n+    @property\n+    def name(self):\n+        if not self._name:\n+            self._name = self.get_name()\n+        return self._name\n+\n+    def create_sql(self, schema_editor, suffix=''):\n+        raise NotImplementedError('subclasses of BaseIndex require a create_sql() method')\n+\n+    def remove_sql(self, schema_editor):\n+        raise NotImplementedError('subclasses of BaseIndex require a remove_sql() method')\n+\n+    def _hash_generator(*args):\n+        \"\"\"\n+        Generate a 32-bit digest of a set of arguments that can be used to\n+        shorten identifying names.\n+        \"\"\"\n+        h = hashlib.md5()\n+        for arg in args:\n+            h.update(force_bytes(arg))\n+        return h.hexdigest()[:8]\n+\n+    def get_name(self, suffix=None):\n+        \"\"\"\n+        Generate a unique name for the index.\n+        \"\"\"\n+        if suffix is None:\n+            suffix = '_%s' % self.index_type\n+        column_names = self.fields\n+        # If there is just one column in the index, use a default algorithm from Django\n+        table_name = self.model._meta.db_table\n+        index_unique_hash = self._hash_generator(column_names)\n+        index_name = ('%s_%s_%s%s' % (\n+            table_name, column_names[0], index_unique_hash, suffix\n+        )).replace('\"', '').replace('.', '_')\n+        # If the name is too long chop off characters from the prefix_part of the name\n+        if len(index_name) > MAX_NAME_LENGTH:\n+            # If table name is too long keep it in the prefix_part\n+            prefix_part = table_name\n+            suffix_part = ('_%s_%s%s' % (column_names[0], index_unique_hash, suffix))\n+            if len(suffix_part) > MAX_NAME_LENGTH:  # When column_names[0] is too long\n+                # If column name is too long keep it in the prefix_part\n+                prefix_part = ('%s_%s' % (table_name, column_names[0]))\n+                suffix_part = ('_%s%s' % (index_unique_hash, suffix))\n+            index_name = '%s%s' % (\n+                prefix_part[:(MAX_NAME_LENGTH - len(suffix_part))], suffix_part\n+            )\n+        # It shouldn't start with an underscore (Oracle hates this)\n+        if index_name[0] == \"_\":\n+            index_name = index_name[1:]\n+        # It can't start with a number on Oracle, so prepend D if we need to\n+        if index_name[0].isdigit():\n+            index_name = \"D%s\" % index_name[1:]\n+        return index_name\n+\n+    def deconstruct(self):\n+        \"\"\"\n+        Returns a 3-tuple of class import path, positional arguments, and keyword\n+        arguments.\n+        \"\"\"\n+        kwargs = {\n+            'type': self.index_type,\n+        }\n+        return (self.__class__.__name__, self.fields, kwargs)\n+\n+    def __repr__(self):\n+        return '<%s: fields=\"%s\">' % (self.__class__.__name__, ', '.join(self.fields))\n+\n+    def __eq__(self, other):\n+        return (self.__class__ == other.__class__) and (self.deconstruct() == other.deconstruct())\n+\n+    def __ne__(self, other):\n+        return not (self == other)\n+\n+\n+class Index(BaseIndex):\n+\n+    index_type = 'idx'\n+\n+    def create_sql(self, schema_editor, suffix=''):","path":"django/db/models/indexes.py","position":107,"original_position":107,"commit_id":"f31d876fdbdd205e9e49d1af6afcd9f727aecdbd","original_commit_id":"f31d876fdbdd205e9e49d1af6afcd9f727aecdbd","user":{"login":"akki","id":4801628,"avatar_url":"https://avatars.githubusercontent.com/u/4801628?v=3","gravatar_id":"","url":"https://api.github.com/users/akki","html_url":"https://github.com/akki","followers_url":"https://api.github.com/users/akki/followers","following_url":"https://api.github.com/users/akki/following{/other_user}","gists_url":"https://api.github.com/users/akki/gists{/gist_id}","starred_url":"https://api.github.com/users/akki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akki/subscriptions","organizations_url":"https://api.github.com/users/akki/orgs","repos_url":"https://api.github.com/users/akki/repos","events_url":"https://api.github.com/users/akki/events{/privacy}","received_events_url":"https://api.github.com/users/akki/received_events","type":"User","site_admin":false},"body":"`create_index_name` is a method of `BaseDatabaseSchemaEditor` because it's schema dependent whereas in `get_name` I have made it independent of schema so that we don't have different names for the same index for users using different schemas (as we want `name` to be a part of the deconstructed index class in the migrations).\r\nAbout `_hash_generator` it is function having nothing to do with `SchemaEditor` nor with `Index`. It should be made a separate function instead of methods of these two classes IMO.\r\n","created_at":"2016-06-13T18:08:25Z","updated_at":"2016-06-13T18:08:25Z","html_url":"https://github.com/django/django/pull/6726#discussion_r66839358","pull_request_url":"https://api.github.com/repos/django/django/pulls/6726","_links":{"self":{"href":"https://api.github.com/repos/django/django/pulls/comments/66839358"},"html":{"href":"https://github.com/django/django/pull/6726#discussion_r66839358"},"pull_request":{"href":"https://api.github.com/repos/django/django/pulls/6726"}}},"pull_request":{"url":"https://api.github.com/repos/django/django/pulls/6726","id":72630732,"html_url":"https://github.com/django/django/pull/6726","diff_url":"https://github.com/django/django/pull/6726.diff","patch_url":"https://github.com/django/django/pull/6726.patch","issue_url":"https://api.github.com/repos/django/django/issues/6726","number":6726,"state":"open","locked":false,"title":"Fixed #26709 -- Add class based indexes and CreateIndex operation","user":{"login":"akki","id":4801628,"avatar_url":"https://avatars.githubusercontent.com/u/4801628?v=3","gravatar_id":"","url":"https://api.github.com/users/akki","html_url":"https://github.com/akki","followers_url":"https://api.github.com/users/akki/followers","following_url":"https://api.github.com/users/akki/following{/other_user}","gists_url":"https://api.github.com/users/akki/gists{/gist_id}","starred_url":"https://api.github.com/users/akki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akki/subscriptions","organizations_url":"https://api.github.com/users/akki/orgs","repos_url":"https://api.github.com/users/akki/repos","events_url":"https://api.github.com/users/akki/events{/privacy}","received_events_url":"https://api.github.com/users/akki/received_events","type":"User","site_admin":false},"body":"Ticket [#26709](https://code.djangoproject.com/ticket/26709).","created_at":"2016-06-05T14:29:12Z","updated_at":"2016-06-13T18:08:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1eff59ea0aff5601a3aa0ed8e5b11d008a2f7b6d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/django/django/pulls/6726/commits","review_comments_url":"https://api.github.com/repos/django/django/pulls/6726/comments","review_comment_url":"https://api.github.com/repos/django/django/pulls/comments{/number}","comments_url":"https://api.github.com/repos/django/django/issues/6726/comments","statuses_url":"https://api.github.com/repos/django/django/statuses/f31d876fdbdd205e9e49d1af6afcd9f727aecdbd","head":{"label":"akki:gsoc-indexes","ref":"gsoc-indexes","sha":"f31d876fdbdd205e9e49d1af6afcd9f727aecdbd","user":{"login":"akki","id":4801628,"avatar_url":"https://avatars.githubusercontent.com/u/4801628?v=3","gravatar_id":"","url":"https://api.github.com/users/akki","html_url":"https://github.com/akki","followers_url":"https://api.github.com/users/akki/followers","following_url":"https://api.github.com/users/akki/following{/other_user}","gists_url":"https://api.github.com/users/akki/gists{/gist_id}","starred_url":"https://api.github.com/users/akki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akki/subscriptions","organizations_url":"https://api.github.com/users/akki/orgs","repos_url":"https://api.github.com/users/akki/repos","events_url":"https://api.github.com/users/akki/events{/privacy}","received_events_url":"https://api.github.com/users/akki/received_events","type":"User","site_admin":false},"repo":{"id":48555733,"name":"django","full_name":"akki/django","owner":{"login":"akki","id":4801628,"avatar_url":"https://avatars.githubusercontent.com/u/4801628?v=3","gravatar_id":"","url":"https://api.github.com/users/akki","html_url":"https://github.com/akki","followers_url":"https://api.github.com/users/akki/followers","following_url":"https://api.github.com/users/akki/following{/other_user}","gists_url":"https://api.github.com/users/akki/gists{/gist_id}","starred_url":"https://api.github.com/users/akki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akki/subscriptions","organizations_url":"https://api.github.com/users/akki/orgs","repos_url":"https://api.github.com/users/akki/repos","events_url":"https://api.github.com/users/akki/events{/privacy}","received_events_url":"https://api.github.com/users/akki/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/akki/django","description":"The Web framework for perfectionists with deadlines.","fork":true,"url":"https://api.github.com/repos/akki/django","forks_url":"https://api.github.com/repos/akki/django/forks","keys_url":"https://api.github.com/repos/akki/django/keys{/key_id}","collaborators_url":"https://api.github.com/repos/akki/django/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/akki/django/teams","hooks_url":"https://api.github.com/repos/akki/django/hooks","issue_events_url":"https://api.github.com/repos/akki/django/issues/events{/number}","events_url":"https://api.github.com/repos/akki/django/events","assignees_url":"https://api.github.com/repos/akki/django/assignees{/user}","branches_url":"https://api.github.com/repos/akki/django/branches{/branch}","tags_url":"https://api.github.com/repos/akki/django/tags","blobs_url":"https://api.github.com/repos/akki/django/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/akki/django/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/akki/django/git/refs{/sha}","trees_url":"https://api.github.com/repos/akki/django/git/trees{/sha}","statuses_url":"https://api.github.com/repos/akki/django/statuses/{sha}","languages_url":"https://api.github.com/repos/akki/django/languages","stargazers_url":"https://api.github.com/repos/akki/django/stargazers","contributors_url":"https://api.github.com/repos/akki/django/contributors","subscribers_url":"https://api.github.com/repos/akki/django/subscribers","subscription_url":"https://api.github.com/repos/akki/django/subscription","commits_url":"https://api.github.com/repos/akki/django/commits{/sha}","git_commits_url":"https://api.github.com/repos/akki/django/git/commits{/sha}","comments_url":"https://api.github.com/repos/akki/django/comments{/number}","issue_comment_url":"https://api.github.com/repos/akki/django/issues/comments{/number}","contents_url":"https://api.github.com/repos/akki/django/contents/{+path}","compare_url":"https://api.github.com/repos/akki/django/compare/{base}...{head}","merges_url":"https://api.github.com/repos/akki/django/merges","archive_url":"https://api.github.com/repos/akki/django/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/akki/django/downloads","issues_url":"https://api.github.com/repos/akki/django/issues{/number}","pulls_url":"https://api.github.com/repos/akki/django/pulls{/number}","milestones_url":"https://api.github.com/repos/akki/django/milestones{/number}","notifications_url":"https://api.github.com/repos/akki/django/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/akki/django/labels{/name}","releases_url":"https://api.github.com/repos/akki/django/releases{/id}","deployments_url":"https://api.github.com/repos/akki/django/deployments","created_at":"2015-12-24T20:15:07Z","updated_at":"2016-02-05T14:52:35Z","pushed_at":"2016-06-11T17:06:11Z","git_url":"git://github.com/akki/django.git","ssh_url":"git@github.com:akki/django.git","clone_url":"https://github.com/akki/django.git","svn_url":"https://github.com/akki/django","homepage":"https://www.djangoproject.com/","size":138470,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"django:master","ref":"master","sha":"faeeb84edfebecf5a5f40df9ef816e5f1cd457c6","user":{"login":"django","id":27804,"avatar_url":"https://avatars.githubusercontent.com/u/27804?v=3","gravatar_id":"","url":"https://api.github.com/users/django","html_url":"https://github.com/django","followers_url":"https://api.github.com/users/django/followers","following_url":"https://api.github.com/users/django/following{/other_user}","gists_url":"https://api.github.com/users/django/gists{/gist_id}","starred_url":"https://api.github.com/users/django/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/django/subscriptions","organizations_url":"https://api.github.com/users/django/orgs","repos_url":"https://api.github.com/users/django/repos","events_url":"https://api.github.com/users/django/events{/privacy}","received_events_url":"https://api.github.com/users/django/received_events","type":"Organization","site_admin":false},"repo":{"id":4164482,"name":"django","full_name":"django/django","owner":{"login":"django","id":27804,"avatar_url":"https://avatars.githubusercontent.com/u/27804?v=3","gravatar_id":"","url":"https://api.github.com/users/django","html_url":"https://github.com/django","followers_url":"https://api.github.com/users/django/followers","following_url":"https://api.github.com/users/django/following{/other_user}","gists_url":"https://api.github.com/users/django/gists{/gist_id}","starred_url":"https://api.github.com/users/django/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/django/subscriptions","organizations_url":"https://api.github.com/users/django/orgs","repos_url":"https://api.github.com/users/django/repos","events_url":"https://api.github.com/users/django/events{/privacy}","received_events_url":"https://api.github.com/users/django/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/django/django","description":"The Web framework for perfectionists with deadlines.","fork":false,"url":"https://api.github.com/repos/django/django","forks_url":"https://api.github.com/repos/django/django/forks","keys_url":"https://api.github.com/repos/django/django/keys{/key_id}","collaborators_url":"https://api.github.com/repos/django/django/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/django/django/teams","hooks_url":"https://api.github.com/repos/django/django/hooks","issue_events_url":"https://api.github.com/repos/django/django/issues/events{/number}","events_url":"https://api.github.com/repos/django/django/events","assignees_url":"https://api.github.com/repos/django/django/assignees{/user}","branches_url":"https://api.github.com/repos/django/django/branches{/branch}","tags_url":"https://api.github.com/repos/django/django/tags","blobs_url":"https://api.github.com/repos/django/django/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/django/django/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/django/django/git/refs{/sha}","trees_url":"https://api.github.com/repos/django/django/git/trees{/sha}","statuses_url":"https://api.github.com/repos/django/django/statuses/{sha}","languages_url":"https://api.github.com/repos/django/django/languages","stargazers_url":"https://api.github.com/repos/django/django/stargazers","contributors_url":"https://api.github.com/repos/django/django/contributors","subscribers_url":"https://api.github.com/repos/django/django/subscribers","subscription_url":"https://api.github.com/repos/django/django/subscription","commits_url":"https://api.github.com/repos/django/django/commits{/sha}","git_commits_url":"https://api.github.com/repos/django/django/git/commits{/sha}","comments_url":"https://api.github.com/repos/django/django/comments{/number}","issue_comment_url":"https://api.github.com/repos/django/django/issues/comments{/number}","contents_url":"https://api.github.com/repos/django/django/contents/{+path}","compare_url":"https://api.github.com/repos/django/django/compare/{base}...{head}","merges_url":"https://api.github.com/repos/django/django/merges","archive_url":"https://api.github.com/repos/django/django/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/django/django/downloads","issues_url":"https://api.github.com/repos/django/django/issues{/number}","pulls_url":"https://api.github.com/repos/django/django/pulls{/number}","milestones_url":"https://api.github.com/repos/django/django/milestones{/number}","notifications_url":"https://api.github.com/repos/django/django/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/django/django/labels{/name}","releases_url":"https://api.github.com/repos/django/django/releases{/id}","deployments_url":"https://api.github.com/repos/django/django/deployments","created_at":"2012-04-28T02:47:18Z","updated_at":"2016-06-13T15:32:30Z","pushed_at":"2016-06-13T17:22:26Z","git_url":"git://github.com/django/django.git","ssh_url":"git@github.com:django/django.git","clone_url":"https://github.com/django/django.git","svn_url":"https://github.com/django/django","homepage":"https://www.djangoproject.com/","size":197034,"stargazers_count":19863,"watchers_count":19863,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":8009,"mirror_url":null,"open_issues_count":89,"forks":8009,"open_issues":89,"watchers":19863,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/django/django/pulls/6726"},"html":{"href":"https://github.com/django/django/pull/6726"},"issue":{"href":"https://api.github.com/repos/django/django/issues/6726"},"comments":{"href":"https://api.github.com/repos/django/django/issues/6726/comments"},"review_comments":{"href":"https://api.github.com/repos/django/django/pulls/6726/comments"},"review_comment":{"href":"https://api.github.com/repos/django/django/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/django/django/pulls/6726/commits"},"statuses":{"href":"https://api.github.com/repos/django/django/statuses/f31d876fdbdd205e9e49d1af6afcd9f727aecdbd"}}}}
{"actor":{"display_login":"akki"}}