From 312c347769ac67878d06dc7306c9978cb2e574a3 Mon Sep 17 00:00:00 2001 From: John Tordoff Date: Mon, 18 May 2020 10:49:39 -0400 Subject: [PATCH] add refactor Django code for backward compatible syntax changes (#9300) ## Purpose Get backwards compatible code out of the way before we flip over to Django 2 ## Changes - Make Many-to-manys use direct assignment - add `on_delete` kwarg to foreign key fields - Get urls helpers from `django.urls` instead of `django.core.urlresolvers` which is depreciated - Change url namespaces for new Django 2 url format. ## QA Notes - No data migration - Low risk - No permissions code touched - No user facing changes ## Documentation Tech task, no new docs. ## Side Effects None that I know of. ## Ticket https://openscience.atlassian.net/browse/ENG-1392 --- addons/base/tests/models.py | 2 +- addons/base/tests/serializers.py | 4 +- addons/gitlab/models.py | 2 +- addons/onedrive/models.py | 2 +- admin/asset_files/views.py | 2 +- admin/banners/views.py | 2 +- admin/base/utils.py | 2 +- admin/collection_providers/views.py | 4 +- admin/common_auth/admin.py | 2 +- admin/common_auth/urls.py | 2 +- admin/common_auth/views.py | 2 +- admin/institutions/views.py | 2 +- admin/meetings/views.py | 4 +- admin/nodes/templatetags/node_extras.py | 2 +- admin/osf_groups/views.py | 2 +- admin/preprint_providers/views.py | 5 +- admin/preprints/views.py | 2 +- admin/registration_providers/views.py | 4 +- admin/subjects/views.py | 2 +- admin/users/templatetags/user_extras.py | 2 +- admin/users/views.py | 2 +- admin_tests/meetings/test_views.py | 2 +- admin_tests/nodes/test_views.py | 2 +- admin_tests/preprint_providers/test_views.py | 2 +- admin_tests/preprints/test_views.py | 2 +- admin_tests/subjects/test_views.py | 2 +- admin_tests/users/test_views.py | 2 +- api/base/authentication/drf.py | 2 +- api/base/pagination.py | 2 +- api/base/urls.py | 6 +- api/providers/urls.py | 76 ++++++++++--------- api_tests/providers/mixins.py | 4 +- .../views/test_preprint_provider_licenses.py | 4 +- ...1_add_reviews_notification_subscription.py | 4 +- osf/models/chronos.py | 6 +- osf/models/collection.py | 14 +++- osf/models/files.py | 4 +- osf/models/institution.py | 2 +- osf/models/node.py | 2 +- osf/models/preprint.py | 8 +- osf/models/registrations.py | 14 +++- osf/models/request.py | 6 +- osf/models/user.py | 6 +- osf_tests/factories.py | 6 +- osf_tests/test_elastic_search.py | 2 +- osf_tests/test_user.py | 4 +- requirements.txt | 4 +- tests/test_preprints.py | 2 +- 48 files changed, 137 insertions(+), 106 deletions(-) diff --git a/addons/base/tests/models.py b/addons/base/tests/models.py index 3fa4fe6441a..abfbde7da18 100644 --- a/addons/base/tests/models.py +++ b/addons/base/tests/models.py @@ -264,7 +264,7 @@ def test_folder_defaults_to_none(self): assert_is_none(node_settings.folder_id) def test_has_auth(self): - self.user.external_accounts = [] + self.user.external_accounts.clear() self.user_settings.reload() node = ProjectFactory() settings = self.NodeSettingsClass(user_settings=self.user_settings, owner=node) diff --git a/addons/base/tests/serializers.py b/addons/base/tests/serializers.py index d7becffcd41..9c331bca343 100644 --- a/addons/base/tests/serializers.py +++ b/addons/base/tests/serializers.py @@ -99,7 +99,7 @@ def test_user_is_owner_no_node_settings(self): assert_false(ser.user_is_owner) def test_user_is_owner_node_not_authorized_user_has_no_accounts(self): - self.user.external_accounts = [] + self.user.external_accounts.clear() assert_false(self.user_settings.external_accounts.count()) assert_false(self.ser.user_is_owner) @@ -111,7 +111,7 @@ def test_user_is_owner_node_authorized_user_is_not_owner(self): self.node_settings.external_account = self.ExternalAccountFactory() with mock.patch('addons.base.models.BaseOAuthUserSettings.verify_oauth_access', return_value=True): - self.user.external_accounts = [] + self.user.external_accounts.clear() assert_false(self.ser.user_is_owner) def test_user_is_owner_node_authorized_user_is_owner(self): diff --git a/addons/gitlab/models.py b/addons/gitlab/models.py index bdc83d9758e..64f121c63b2 100644 --- a/addons/gitlab/models.py +++ b/addons/gitlab/models.py @@ -76,7 +76,7 @@ class NodeSettings(BaseOAuthNodeSettings, BaseStorageAddon): repo_id = models.TextField(blank=True, null=True) hook_id = models.TextField(blank=True, null=True) hook_secret = models.TextField(blank=True, null=True) - user_settings = models.ForeignKey(UserSettings, null=True, blank=True) + user_settings = models.ForeignKey(UserSettings, null=True, blank=True, on_delete=models.CASCADE) @property def folder_id(self): diff --git a/addons/onedrive/models.py b/addons/onedrive/models.py index 152d6c77553..f6a72149f07 100644 --- a/addons/onedrive/models.py +++ b/addons/onedrive/models.py @@ -98,7 +98,7 @@ class NodeSettings(BaseOAuthNodeSettings, BaseStorageAddon): folder_id = models.TextField(null=True, blank=True) folder_path = models.TextField(null=True, blank=True) - user_settings = models.ForeignKey(UserSettings, null=True, blank=True) + user_settings = models.ForeignKey(UserSettings, null=True, blank=True, on_delete=models.CASCADE) _api = None diff --git a/admin/asset_files/views.py b/admin/asset_files/views.py index 7d1b88f76fd..6968348c1a3 100644 --- a/admin/asset_files/views.py +++ b/admin/asset_files/views.py @@ -1,5 +1,5 @@ from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.db.models import Case, CharField, Value, When from django.forms.models import model_to_dict from django.views.generic import ListView, DetailView, View, CreateView, DeleteView, UpdateView diff --git a/admin/banners/views.py b/admin/banners/views.py index 1c81b4be17c..916abcce984 100644 --- a/admin/banners/views.py +++ b/admin/banners/views.py @@ -5,7 +5,7 @@ from django.shortcuts import redirect from django.forms.models import model_to_dict -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.views.generic import ListView, DetailView, View, CreateView, UpdateView, DeleteView from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib import messages diff --git a/admin/base/utils.py b/admin/base/utils.py index b786aeb90f3..d9ecd67431a 100644 --- a/admin/base/utils.py +++ b/admin/base/utils.py @@ -4,7 +4,7 @@ from osf.models import Subject, NodeLicense from django.core.exceptions import ValidationError, PermissionDenied -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.validators import RegexValidator, _lazy_re_compile from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _ diff --git a/admin/collection_providers/views.py b/admin/collection_providers/views.py index cc4d3176ae5..5796982ce8f 100644 --- a/admin/collection_providers/views.py +++ b/admin/collection_providers/views.py @@ -3,7 +3,7 @@ from django.http import HttpResponse from django.core import serializers from django.core.exceptions import ValidationError -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.shortcuts import redirect from django.views.generic import View, CreateView, ListView, DetailView, UpdateView, DeleteView, TemplateView from django.contrib import messages @@ -317,7 +317,7 @@ def create_or_update_provider(self, provider_data): provider.primary_collection.program_area_choices = primary_collection['fields']['program_area_choices'] provider.primary_collection.save() if licenses: - provider.licenses_acceptable = licenses + provider.licenses_acceptable.add(*licenses) if default_license: provider.default_license = NodeLicense.objects.get(license_id=default_license) return provider diff --git a/admin/common_auth/admin.py b/admin/common_auth/admin.py index 7325a451876..b6d5ad12bd1 100644 --- a/admin/common_auth/admin.py +++ b/admin/common_auth/admin.py @@ -3,7 +3,7 @@ from django.contrib import admin from django.contrib.admin.models import DELETION from django.contrib.auth.models import Permission -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils.html import escape from osf.models import AdminLogEntry diff --git a/admin/common_auth/urls.py b/admin/common_auth/urls.py index c1ee7e760eb..b1fa39b9284 100644 --- a/admin/common_auth/urls.py +++ b/admin/common_auth/urls.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from django.conf.urls import url -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.contrib.auth.views import password_change, password_change_done from admin.common_auth import views diff --git a/admin/common_auth/views.py b/admin/common_auth/views.py index 04ac2731c0c..cda8fa7848d 100644 --- a/admin/common_auth/views.py +++ b/admin/common_auth/views.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -from django.core.urlresolvers import reverse, reverse_lazy +from django.urls import reverse, reverse_lazy from django.http import Http404 from django.shortcuts import redirect from django.utils.decorators import method_decorator diff --git a/admin/institutions/views.py b/admin/institutions/views.py index 90a54fcd855..bdcb1e17a2f 100644 --- a/admin/institutions/views.py +++ b/admin/institutions/views.py @@ -6,7 +6,7 @@ from django.core import serializers from django.shortcuts import redirect from django.forms.models import model_to_dict -from django.core.urlresolvers import reverse_lazy, reverse +from django.urls import reverse_lazy, reverse from django.http import HttpResponse, JsonResponse from django.views.generic import ListView, DetailView, View, CreateView, UpdateView, DeleteView, TemplateView from django.views.generic.edit import FormView diff --git a/admin/meetings/views.py b/admin/meetings/views.py index 2fc971c70cd..4e77d47645e 100644 --- a/admin/meetings/views.py +++ b/admin/meetings/views.py @@ -3,7 +3,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin from django.views.generic import ListView, FormView -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import Http404 from framework.auth.core import get_user @@ -113,7 +113,7 @@ def form_valid(self, form): **data ) new_conf.save() - new_conf.admins = admin_users + new_conf.admins.add(*admin_users) new_conf.field_names.update(custom_fields) new_conf.save() return super(MeetingCreateFormView, self).form_valid(form) diff --git a/admin/nodes/templatetags/node_extras.py b/admin/nodes/templatetags/node_extras.py index 161734421fc..b8b00e4aa20 100644 --- a/admin/nodes/templatetags/node_extras.py +++ b/admin/nodes/templatetags/node_extras.py @@ -1,5 +1,5 @@ from django import template -from django.core.urlresolvers import reverse +from django.urls import reverse from osf.models import Node, OSFUser diff --git a/admin/osf_groups/views.py b/admin/osf_groups/views.py index b430e130f13..6ca0204c330 100644 --- a/admin/osf_groups/views.py +++ b/admin/osf_groups/views.py @@ -1,5 +1,5 @@ from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.urlresolvers import reverse +from django.urls import reverse from django.views.generic import FormView, ListView from osf.models import OSFGroup diff --git a/admin/preprint_providers/views.py b/admin/preprint_providers/views.py index 02775eca023..a40dfb0227d 100644 --- a/admin/preprint_providers/views.py +++ b/admin/preprint_providers/views.py @@ -6,7 +6,7 @@ from django.http import Http404 from django.core import serializers from django.core.exceptions import ValidationError -from django.core.urlresolvers import reverse_lazy, reverse +from django.urls import reverse_lazy, reverse from django.http import HttpResponse, JsonResponse from django.views.generic import ListView, DetailView, View, CreateView, DeleteView, TemplateView, UpdateView from django.views.generic.edit import FormView @@ -331,7 +331,8 @@ def create_or_update_provider(self, provider_data): provider.save() if licenses: - provider.licenses_acceptable = licenses + provider.licenses_acceptable.clear() + provider.licenses_acceptable.add(*licenses) if default_license: provider.default_license = NodeLicense.objects.get(license_id=default_license) # Only adds the JSON taxonomy if there is no existing taxonomy data diff --git a/admin/preprints/views.py b/admin/preprints/views.py index 98a9b3a455a..70364d6765e 100644 --- a/admin/preprints/views.py +++ b/admin/preprints/views.py @@ -2,7 +2,7 @@ from django.views.generic import UpdateView, DeleteView, ListView from django.utils import timezone -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.contrib.auth.mixins import PermissionRequiredMixin from django.shortcuts import redirect from django.views.defaults import page_not_found diff --git a/admin/registration_providers/views.py b/admin/registration_providers/views.py index 0449413492d..7b3de622e96 100644 --- a/admin/registration_providers/views.py +++ b/admin/registration_providers/views.py @@ -4,7 +4,7 @@ from django.core import serializers from django.core.exceptions import ValidationError from django.core.management import call_command -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.shortcuts import redirect from django.views.generic import View, CreateView, ListView, DetailView, UpdateView, DeleteView, TemplateView from django.contrib.auth.mixins import PermissionRequiredMixin @@ -294,7 +294,7 @@ def create_or_update_provider(self, provider_data): provider.save() if licenses: - provider.licenses_acceptable = licenses + provider.licenses_acceptable.add(*licenses) if default_license: provider.default_license = NodeLicense.objects.get(license_id=default_license) diff --git a/admin/subjects/views.py b/admin/subjects/views.py index 9b4e3210f1b..89c254e714e 100644 --- a/admin/subjects/views.py +++ b/admin/subjects/views.py @@ -1,5 +1,5 @@ from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.views.generic import ListView, UpdateView from admin.subjects.forms import SubjectForm diff --git a/admin/users/templatetags/user_extras.py b/admin/users/templatetags/user_extras.py index b3eb842d9c3..210122be770 100644 --- a/admin/users/templatetags/user_extras.py +++ b/admin/users/templatetags/user_extras.py @@ -1,5 +1,5 @@ from django import template -from django.core.urlresolvers import reverse +from django.urls import reverse register = template.Library() diff --git a/admin/users/views.py b/admin/users/views.py index 1cc152cd3ef..34bedf8a2ee 100644 --- a/admin/users/views.py +++ b/admin/users/views.py @@ -9,7 +9,7 @@ from django.views.generic import FormView, DeleteView, ListView, TemplateView from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import PermissionDenied from django.core.mail import send_mail from django.http import Http404, HttpResponse diff --git a/admin_tests/meetings/test_views.py b/admin_tests/meetings/test_views.py index b10e17a6d9b..58f970a316f 100644 --- a/admin_tests/meetings/test_views.py +++ b/admin_tests/meetings/test_views.py @@ -2,7 +2,7 @@ from django.test import RequestFactory from django.http import Http404 -from django.core.urlresolvers import reverse +from django.urls import reverse from django.contrib.auth.models import Permission from django.core.exceptions import PermissionDenied diff --git a/admin_tests/nodes/test_views.py b/admin_tests/nodes/test_views.py index b9dc77ff1ef..2441ce6af33 100644 --- a/admin_tests/nodes/test_views.py +++ b/admin_tests/nodes/test_views.py @@ -24,7 +24,7 @@ from nose import tools as nt from django.utils import timezone from django.test import RequestFactory -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import PermissionDenied from django.contrib.auth.models import Permission from framework.auth.core import Auth diff --git a/admin_tests/preprint_providers/test_views.py b/admin_tests/preprint_providers/test_views.py index 05e14fdd9e9..7e342e1bea7 100644 --- a/admin_tests/preprint_providers/test_views.py +++ b/admin_tests/preprint_providers/test_views.py @@ -177,7 +177,7 @@ def setUp(self): self.import_view = views.ImportPreprintProvider() self.import_view = setup_user_view(self.import_view, self.import_request, user=self.user) - self.preprint_provider.licenses_acceptable = [NodeLicense.objects.get(license_id='NONE')] + self.preprint_provider.licenses_acceptable.add(*[NodeLicense.objects.get(license_id='NONE')]) self.subject = SubjectFactory(provider=self.preprint_provider) def test_post(self): diff --git a/admin_tests/preprints/test_views.py b/admin_tests/preprints/test_views.py index 888c9f6bd0d..469fc5bbd07 100644 --- a/admin_tests/preprints/test_views.py +++ b/admin_tests/preprints/test_views.py @@ -2,7 +2,7 @@ import mock from django.test import RequestFactory -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import PermissionDenied from django.contrib.auth.models import Permission, Group, AnonymousUser from django.contrib.messages.storage.fallback import FallbackStorage diff --git a/admin_tests/subjects/test_views.py b/admin_tests/subjects/test_views.py index 8e01a2f2857..5adb0780f3f 100644 --- a/admin_tests/subjects/test_views.py +++ b/admin_tests/subjects/test_views.py @@ -1,6 +1,6 @@ from nose import tools as nt from django.test import RequestFactory -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import PermissionDenied from django.contrib.auth.models import Permission diff --git a/admin_tests/users/test_views.py b/admin_tests/users/test_views.py index c6023599aba..e4417301125 100644 --- a/admin_tests/users/test_views.py +++ b/admin_tests/users/test_views.py @@ -9,7 +9,7 @@ from django.test import RequestFactory from django.http import Http404 from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import PermissionDenied from django.contrib.auth.models import Permission from django.contrib.messages.storage.fallback import FallbackStorage diff --git a/api/base/authentication/drf.py b/api/base/authentication/drf.py index 94afc468a94..72b501193be 100644 --- a/api/base/authentication/drf.py +++ b/api/base/authentication/drf.py @@ -30,7 +30,7 @@ def get_session_from_cookie(cookie_val): """ try: - session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie_val) + session_id = itsdangerous.Signer(settings.SECRET_KEY).unsign(cookie_val).decode() except itsdangerous.BadSignature: return None try: diff --git a/api/base/pagination.py b/api/base/pagination.py index 4860871b557..920f4e39e5c 100644 --- a/api/base/pagination.py +++ b/api/base/pagination.py @@ -1,6 +1,6 @@ from django.utils import six from collections import OrderedDict -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.paginator import InvalidPage, Paginator as DjangoPaginator from django.db.models import QuerySet diff --git a/api/base/urls.py b/api/base/urls.py index 9de0c563198..4177a16edbd 100644 --- a/api/base/urls.py +++ b/api/base/urls.py @@ -33,7 +33,7 @@ url(r'^status/', views.status_check, name='status_check'), url(r'^actions/', include('api.actions.urls', namespace='actions')), url(r'^addons/', include('api.addons.urls', namespace='addons')), - url(r'^alerts/', include('api.alerts.urls', namespace='alerts')), + url(r'^alerts/', include(('api.alerts.urls', 'alerts'), namespace='alerts')), url(r'^applications/', include('api.applications.urls', namespace='applications')), url(r'^citations/', include('api.citations.urls', namespace='citations')), url(r'^collections/', include('api.collections.urls', namespace='collections')), @@ -56,7 +56,7 @@ url(r'^regions/', include('api.regions.urls', namespace='regions')), url(r'^providers/', include('api.providers.urls', namespace='providers')), url(r'^registrations/', include('api.registrations.urls', namespace='registrations')), - url(r'^requests/', include('api.requests.urls', namespace='requests')), + url(r'^requests/', include(('api.requests.urls', 'requests'), namespace='requests')), url(r'^scopes/', include('api.scopes.urls', namespace='scopes')), url(r'^search/', include('api.search.urls', namespace='search')), url(r'^sparse/', include('api.sparse.urls', namespace='sparse')), @@ -68,7 +68,7 @@ url(r'^users/', include('api.users.urls', namespace='users')), url(r'^view_only_links/', include('api.view_only_links.urls', namespace='view-only-links')), url(r'^wikis/', include('api.wikis.urls', namespace='wikis')), - url(r'^_waffle/', include('api.waffle.urls', namespace='waffle')), + url(r'^_waffle/', include(('api.waffle.urls', 'waffle'), namespace='waffle')), ], ), ), diff --git a/api/providers/urls.py b/api/providers/urls.py index 59bc4f4ce47..ab3a6104f72 100644 --- a/api/providers/urls.py +++ b/api/providers/urls.py @@ -7,50 +7,58 @@ urlpatterns = [ url( r'^preprints/', include( - [ - url(r'^$', views.PreprintProviderList.as_view(), name=views.PreprintProviderList.view_name), - url(r'^(?P\w+)/$', views.PreprintProviderDetail.as_view(), name=views.PreprintProviderDetail.view_name), - url(r'^(?P\w+)/licenses/$', views.PreprintProviderLicenseList.as_view(), name=views.PreprintProviderLicenseList.view_name), - url(r'^(?P\w+)/preprints/$', views.PreprintProviderPreprintList.as_view(), name=views.PreprintProviderPreprintList.view_name), - url(r'^(?P\w+)/subjects/$', views.PreprintProviderSubjects.as_view(), name=views.PreprintProviderSubjects.view_name), - url(r'^(?P\w+)/subjects/highlighted/$', views.PreprintProviderHighlightedSubjectList.as_view(), name=views.PreprintProviderHighlightedSubjectList.view_name), - url(r'^(?P\w+)/taxonomies/$', views.PreprintProviderTaxonomies.as_view(), name=views.PreprintProviderTaxonomies.view_name), - url(r'^(?P\w+)/taxonomies/highlighted/$', views.PreprintProviderHighlightedTaxonomyList.as_view(), name=views.PreprintProviderHighlightedTaxonomyList.view_name), - url(r'^(?P\w+)/withdraw_requests/$', views.PreprintProviderWithdrawRequestList.as_view(), name=views.PreprintProviderWithdrawRequestList.view_name), - url(r'^(?P\w+)/moderators/$', views.PreprintProviderModeratorsList.as_view(), name=views.PreprintProviderModeratorsList.view_name), - url(r'^(?P\w+)/moderators/(?P\w+)/$', views.PreprintProviderModeratorsDetail.as_view(), name=views.PreprintProviderModeratorsDetail.view_name), - ], namespace='preprint-providers', + ( + [ + url(r'^$', views.PreprintProviderList.as_view(), name=views.PreprintProviderList.view_name), + url(r'^(?P\w+)/$', views.PreprintProviderDetail.as_view(), name=views.PreprintProviderDetail.view_name), + url(r'^(?P\w+)/licenses/$', views.PreprintProviderLicenseList.as_view(), name=views.PreprintProviderLicenseList.view_name), + url(r'^(?P\w+)/preprints/$', views.PreprintProviderPreprintList.as_view(), name=views.PreprintProviderPreprintList.view_name), + url(r'^(?P\w+)/subjects/$', views.PreprintProviderSubjects.as_view(), name=views.PreprintProviderSubjects.view_name), + url(r'^(?P\w+)/subjects/highlighted/$', views.PreprintProviderHighlightedSubjectList.as_view(), name=views.PreprintProviderHighlightedSubjectList.view_name), + url(r'^(?P\w+)/taxonomies/$', views.PreprintProviderTaxonomies.as_view(), name=views.PreprintProviderTaxonomies.view_name), + url(r'^(?P\w+)/taxonomies/highlighted/$', views.PreprintProviderHighlightedTaxonomyList.as_view(), name=views.PreprintProviderHighlightedTaxonomyList.view_name), + url(r'^(?P\w+)/withdraw_requests/$', views.PreprintProviderWithdrawRequestList.as_view(), name=views.PreprintProviderWithdrawRequestList.view_name), + url(r'^(?P\w+)/moderators/$', views.PreprintProviderModeratorsList.as_view(), name=views.PreprintProviderModeratorsList.view_name), + url(r'^(?P\w+)/moderators/(?P\w+)/$', views.PreprintProviderModeratorsDetail.as_view(), name=views.PreprintProviderModeratorsDetail.view_name), + ], 'preprints', + ), + namespace='preprint-providers', ), ), url( r'^collections/', include( - [ - url(r'^$', views.CollectionProviderList.as_view(), name=views.CollectionProviderList.view_name), - url(r'^(?P\w+)/$', views.CollectionProviderDetail.as_view(), name=views.CollectionProviderDetail.view_name), - url(r'^(?P\w+)/licenses/$', views.CollectionProviderLicenseList.as_view(), name=views.CollectionProviderLicenseList.view_name), - url(r'^(?P\w+)/submissions/$', views.CollectionProviderSubmissionList.as_view(), name=views.CollectionProviderSubmissionList.view_name), - url(r'^(?P\w+)/subjects/$', views.CollectionProviderSubjects.as_view(), name=views.CollectionProviderSubjects.view_name), - url(r'^(?P\w+)/subjects/highlighted/$', views.CollectionProviderHighlightedSubjectList.as_view(), name=views.CollectionProviderHighlightedSubjectList.view_name), - url(r'^(?P\w+)/taxonomies/$', views.CollectionProviderTaxonomies.as_view(), name=views.CollectionProviderTaxonomies.view_name), - url(r'^(?P\w+)/taxonomies/highlighted/$', views.CollectionProviderHighlightedTaxonomyList.as_view(), name=views.CollectionProviderHighlightedTaxonomyList.view_name), - ], namespace='collection-providers', + ( + [ + url(r'^$', views.CollectionProviderList.as_view(), name=views.CollectionProviderList.view_name), + url(r'^(?P\w+)/$', views.CollectionProviderDetail.as_view(), name=views.CollectionProviderDetail.view_name), + url(r'^(?P\w+)/licenses/$', views.CollectionProviderLicenseList.as_view(), name=views.CollectionProviderLicenseList.view_name), + url(r'^(?P\w+)/submissions/$', views.CollectionProviderSubmissionList.as_view(), name=views.CollectionProviderSubmissionList.view_name), + url(r'^(?P\w+)/subjects/$', views.CollectionProviderSubjects.as_view(), name=views.CollectionProviderSubjects.view_name), + url(r'^(?P\w+)/subjects/highlighted/$', views.CollectionProviderHighlightedSubjectList.as_view(), name=views.CollectionProviderHighlightedSubjectList.view_name), + url(r'^(?P\w+)/taxonomies/$', views.CollectionProviderTaxonomies.as_view(), name=views.CollectionProviderTaxonomies.view_name), + url(r'^(?P\w+)/taxonomies/highlighted/$', views.CollectionProviderHighlightedTaxonomyList.as_view(), name=views.CollectionProviderHighlightedTaxonomyList.view_name), + ], 'collections', + ), + namespace='collection-providers', ), ), url( r'^registrations/', include( - [ - url(r'^$', views.RegistrationProviderList.as_view(), name=views.RegistrationProviderList.view_name), - url(r'^(?P\w+)/$', views.RegistrationProviderDetail.as_view(), name=views.RegistrationProviderDetail.view_name), - url(r'^(?P\w+)/licenses/$', views.RegistrationProviderLicenseList.as_view(), name=views.RegistrationProviderLicenseList.view_name), - url(r'^(?P\w+)/submissions/$', views.RegistrationProviderSubmissionList.as_view(), name=views.RegistrationProviderSubmissionList.view_name), - url(r'^(?P\w+)/subjects/$', views.RegistrationProviderSubjects.as_view(), name=views.RegistrationProviderSubjects.view_name), - url(r'^(?P\w+)/subjects/highlighted/$', views.RegistrationProviderHighlightedSubjectList.as_view(), name=views.RegistrationProviderHighlightedSubjectList.view_name), - url(r'^(?P\w+)/taxonomies/$', views.RegistrationProviderTaxonomies.as_view(), name=views.RegistrationProviderTaxonomies.view_name), - url(r'^(?P\w+)/taxonomies/highlighted/$', views.RegistrationProviderHighlightedTaxonomyList.as_view(), name=views.RegistrationProviderHighlightedTaxonomyList.view_name), - ], namespace='registration-providers', + ( + [ + url(r'^$', views.RegistrationProviderList.as_view(), name=views.RegistrationProviderList.view_name), + url(r'^(?P\w+)/$', views.RegistrationProviderDetail.as_view(), name=views.RegistrationProviderDetail.view_name), + url(r'^(?P\w+)/licenses/$', views.RegistrationProviderLicenseList.as_view(), name=views.RegistrationProviderLicenseList.view_name), + url(r'^(?P\w+)/submissions/$', views.RegistrationProviderSubmissionList.as_view(), name=views.RegistrationProviderSubmissionList.view_name), + url(r'^(?P\w+)/subjects/$', views.RegistrationProviderSubjects.as_view(), name=views.RegistrationProviderSubjects.view_name), + url(r'^(?P\w+)/subjects/highlighted/$', views.RegistrationProviderHighlightedSubjectList.as_view(), name=views.RegistrationProviderHighlightedSubjectList.view_name), + url(r'^(?P\w+)/taxonomies/$', views.RegistrationProviderTaxonomies.as_view(), name=views.RegistrationProviderTaxonomies.view_name), + url(r'^(?P\w+)/taxonomies/highlighted/$', views.RegistrationProviderHighlightedTaxonomyList.as_view(), name=views.RegistrationProviderHighlightedTaxonomyList.view_name), + ], 'registrations', + ), + namespace='registration-providers', ), ), - ] diff --git a/api_tests/providers/mixins.py b/api_tests/providers/mixins.py index 2253377469f..3febff802ce 100644 --- a/api_tests/providers/mixins.py +++ b/api_tests/providers/mixins.py @@ -975,7 +975,7 @@ def license_three(self, licenses): return licenses[2] def test_provider_has_no_acceptable_licenses_and_no_default(self, app, provider, licenses, url, license_one): - provider.licenses_acceptable = [] + provider.licenses_acceptable.clear() provider.default_license = None provider.save() res = app.get(url) @@ -989,7 +989,7 @@ def test_provider_has_no_acceptable_licenses_and_no_default(self, app, provider, assert res.json['data'][0]['id'] == license_one._id def test_provider_has_a_default_license_but_no_acceptable_licenses(self, app, provider, licenses, license_two, url): - provider.licenses_acceptable = [] + provider.licenses_acceptable.clear() provider.default_license = license_two provider.save() res = app.get(url) diff --git a/api_tests/providers/preprints/views/test_preprint_provider_licenses.py b/api_tests/providers/preprints/views/test_preprint_provider_licenses.py index e4ce5d39ad9..4dd3465dfc4 100644 --- a/api_tests/providers/preprints/views/test_preprint_provider_licenses.py +++ b/api_tests/providers/preprints/views/test_preprint_provider_licenses.py @@ -35,7 +35,7 @@ def url(self, provider, request): API_BASE, provider._id) def test_preprint_provider_has_no_acceptable_licenses_and_no_default(self, app, provider, licenses, url): - provider.licenses_acceptable = [] + provider.licenses_acceptable.clear() provider.default_license = None provider.save() res = app.get(url) @@ -44,7 +44,7 @@ def test_preprint_provider_has_no_acceptable_licenses_and_no_default(self, app, assert res.json['links']['meta']['total'] == len(licenses) def test_preprint_provider_has_a_default_license_but_no_acceptable_licenses(self, app, provider, licenses, license_two, url): - provider.licenses_acceptable = [] + provider.licenses_acceptable.clear() provider.default_license = license_two provider.save() res = app.get(url) diff --git a/osf/migrations/0061_add_reviews_notification_subscription.py b/osf/migrations/0061_add_reviews_notification_subscription.py index d40df94e4c9..ae9c691c865 100644 --- a/osf/migrations/0061_add_reviews_notification_subscription.py +++ b/osf/migrations/0061_add_reviews_notification_subscription.py @@ -3,11 +3,11 @@ from __future__ import unicode_literals from django.db import migrations -from django.core.management import call_command +from osf.management.commands.add_notification_subscription import add_reviews_notification_setting def add_reviews_notification_subscription(state, schema_editor): - call_command('add_notification_subscription', '--notification=global_reviews', state=state) + add_reviews_notification_setting('global_reviews', state=state) class Migration(migrations.Migration): diff --git a/osf/models/chronos.py b/osf/models/chronos.py index 225c3ccf852..355abc8366d 100644 --- a/osf/models/chronos.py +++ b/osf/models/chronos.py @@ -25,10 +25,10 @@ def __repr__(self): class ChronosSubmission(BaseModel): publication_id = models.TextField(null=False, blank=False, unique=True) - journal = models.ForeignKey(ChronosJournal, null=False, blank=False) - preprint = models.ForeignKey('osf.Preprint', null=False, blank=False) + journal = models.ForeignKey(ChronosJournal, null=False, blank=False, on_delete=models.CASCADE) + preprint = models.ForeignKey('osf.Preprint', null=False, blank=False, on_delete=models.CASCADE) - submitter = models.ForeignKey('osf.OSFUser', null=False, blank=False) + submitter = models.ForeignKey('osf.OSFUser', null=False, blank=False, on_delete=models.CASCADE) status = models.IntegerField(null=True, blank=True, default=None, choices=ChronosSubmissionStatus.choices()) diff --git a/osf/models/collection.py b/osf/models/collection.py index 53638c39178..3645090fabb 100644 --- a/osf/models/collection.py +++ b/osf/models/collection.py @@ -32,7 +32,7 @@ class Meta: collection = models.ForeignKey('Collection', on_delete=models.CASCADE) guid = models.ForeignKey('Guid', on_delete=models.CASCADE) - creator = models.ForeignKey('OSFUser') + creator = models.ForeignKey('OSFUser', on_delete=models.CASCADE) collected_type = models.CharField(blank=True, max_length=127) status = models.CharField(blank=True, max_length=127) volume = models.CharField(blank=True, max_length=127) @@ -103,7 +103,7 @@ class Meta: ) provider = models.ForeignKey('AbstractProvider', blank=True, null=True, on_delete=models.CASCADE) - creator = models.ForeignKey('OSFUser') + creator = models.ForeignKey('OSFUser', on_delete=models.CASCADE) guid_links = models.ManyToManyField('Guid', through=CollectionSubmission, related_name='collections') collected_types = models.ManyToManyField( 'contenttypes.ContentType', @@ -181,8 +181,14 @@ def save(self, *args, **kwargs): if first_save: # Set defaults for M2M - self.collected_types = ContentType.objects.filter(app_label='osf', model__in=['abstractnode', 'collection', 'preprint']) - # Set up initial permissions + content_type = ContentType.objects.filter( + app_label='osf', + model__in=['abstractnode', 'collection', 'preprint'] + ) + + self.collected_types.add(*content_type) + + # Set up initial permissions self.update_group_permissions() self.get_group(ADMIN).user_set.add(self.creator) diff --git a/osf/models/files.py b/osf/models/files.py index a1c9efca18f..3cb9cfd399f 100644 --- a/osf/models/files.py +++ b/osf/models/files.py @@ -839,8 +839,8 @@ class Meta: class BaseFileVersionsThrough(models.Model): - basefilenode = models.ForeignKey(BaseFileNode, db_index=True) - fileversion = models.ForeignKey(FileVersion, db_index=True) + basefilenode = models.ForeignKey(BaseFileNode, db_index=True, on_delete=models.CASCADE) + fileversion = models.ForeignKey(FileVersion, db_index=True, on_delete=models.CASCADE) version_name = models.TextField(blank=True) class Meta: diff --git a/osf/models/institution.py b/osf/models/institution.py index c5afd679033..5db8c0033c5 100644 --- a/osf/models/institution.py +++ b/osf/models/institution.py @@ -5,7 +5,7 @@ from django.conf import settings from django.contrib.postgres import fields -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver diff --git a/osf/models/node.py b/osf/models/node.py index 72237313b4c..ddfbf3d27db 100644 --- a/osf/models/node.py +++ b/osf/models/node.py @@ -15,7 +15,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.fields import GenericRelation from django.core.paginator import Paginator -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models, connection from django.db.models.signals import post_save from django.dispatch import receiver diff --git a/osf/models/preprint.py b/osf/models/preprint.py index 8616b0f0f69..458723177ca 100644 --- a/osf/models/preprint.py +++ b/osf/models/preprint.py @@ -169,7 +169,13 @@ class Preprint(DirtyFieldsMixin, GuidMixin, IdentifierMixin, ReviewableMixin, Ba validators=[validate_doi], null=True, blank=True) files = GenericRelation('osf.OsfStorageFile', object_id_field='target_object_id', content_type_field='target_content_type') - primary_file = models.ForeignKey('osf.OsfStorageFile', null=True, blank=True, related_name='preprint') + primary_file = models.ForeignKey( + 'osf.OsfStorageFile', + null=True, + blank=True, + related_name='preprint', + on_delete=models.CASCADE + ) # (for legacy preprints), pull off of node is_public = models.BooleanField(default=True, db_index=True) # Datetime when old node was deleted (for legacy preprints) diff --git a/osf/models/registrations.py b/osf/models/registrations.py index 73a854f3214..3e6a9dd7668 100644 --- a/osf/models/registrations.py +++ b/osf/models/registrations.py @@ -61,7 +61,12 @@ class Registration(AbstractNode): 'node_license', 'category', ] - provider = models.ForeignKey('RegistrationProvider', related_name='registrations', null=True) + provider = models.ForeignKey( + 'RegistrationProvider', + related_name='registrations', + null=True, + on_delete=models.SET_NULL + ) registered_date = NonNaiveDateTimeField(db_index=True, null=True, blank=True) # This is a NullBooleanField because of inheritance issues with using a BooleanField @@ -581,7 +586,12 @@ class DraftRegistration(ObjectIDMixin, RegistrationResponseMixin, DirtyFieldsMix null=True, on_delete=models.CASCADE) initiator = models.ForeignKey('OSFUser', null=True, on_delete=models.CASCADE) - provider = models.ForeignKey('RegistrationProvider', related_name='draft_registrations', null=True) + provider = models.ForeignKey( + 'RegistrationProvider', + related_name='draft_registrations', + null=True, + on_delete=models.CASCADE, + ) # Dictionary field mapping question id to a question's comments and answer # { diff --git a/osf/models/request.py b/osf/models/request.py index a5f4039a47d..7914c8b2a3f 100644 --- a/osf/models/request.py +++ b/osf/models/request.py @@ -16,7 +16,7 @@ class Meta: objects = IncludeManager() request_type = models.CharField(max_length=31, choices=RequestTypes.choices()) - creator = models.ForeignKey('OSFUser', related_name='submitted_%(class)s') + creator = models.ForeignKey('OSFUser', related_name='submitted_%(class)s', on_delete=models.CASCADE) comment = models.TextField(null=True, blank=True) @property @@ -27,10 +27,10 @@ def target(self): class NodeRequest(AbstractRequest, NodeRequestableMixin): """ Request for Node Access """ - target = models.ForeignKey('AbstractNode', related_name='requests') + target = models.ForeignKey('AbstractNode', related_name='requests', on_delete=models.CASCADE) class PreprintRequest(AbstractRequest, PreprintRequestableMixin): """ Request for Preprint Withdrawal """ - target = models.ForeignKey('Preprint', related_name='requests') + target = models.ForeignKey('Preprint', related_name='requests', on_delete=models.CASCADE) diff --git a/osf/models/user.py b/osf/models/user.py index 0d86dc0b6b1..c47ca53739f 100644 --- a/osf/models/user.py +++ b/osf/models/user.py @@ -229,7 +229,7 @@ class OSFUser(DirtyFieldsMixin, GuidMixin, BaseModel, AbstractBaseUser, Permissi group_connected_email_records = DateTimeAwareJSONField(default=dict, blank=True) # The user into which this account was merged - merged_by = models.ForeignKey('self', null=True, blank=True, related_name='merger') + merged_by = models.ForeignKey('self', null=True, blank=True, related_name='merger', on_delete=models.CASCADE) # verification key v1: only the token string, no expiration time # used for cas login with username and verification key @@ -561,8 +561,8 @@ def csl_name(self, node_id=None): def osfstorage_region(self): from addons.osfstorage.models import Region osfs_settings = self._settings_model('osfstorage') - default_region_subquery = osfs_settings.objects.filter(owner=self.id).values('default_region_id') - return Region.objects.get(id=default_region_subquery) + region_subquery = osfs_settings.objects.get(owner=self.id).default_region_id + return Region.objects.get(id=region_subquery) @property def contributor_to(self): diff --git a/osf_tests/factories.py b/osf_tests/factories.py index 4c6cdb2b222..cf12ec9160e 100644 --- a/osf_tests/factories.py +++ b/osf_tests/factories.py @@ -307,7 +307,7 @@ def _create(cls, *args, **kwargs): obj = cls._build(*args, **kwargs) obj.save() # M2M, requires initial save - obj.collected_types = collected_types + obj.collected_types.add(*collected_types) return obj class BookmarkCollectionFactory(CollectionFactory): @@ -870,7 +870,7 @@ class Meta: @factory.post_generation def admins(self, create, extracted, **kwargs): - self.admins = extracted or [UserFactory()] + self.admins.add(*(extracted or [UserFactory()])) class SessionFactory(DjangoModelFactory): @@ -1017,7 +1017,7 @@ class Meta: def _create(cls, target_class, *args, **kwargs): providers = kwargs.pop('providers', []) instance = super(ProviderAssetFileFactory, cls)._create(target_class, *args, **kwargs) - instance.providers = providers + instance.providers.add(*providers) instance.save() return instance diff --git a/osf_tests/test_elastic_search.py b/osf_tests/test_elastic_search.py index 6947e2880c5..e895df288dc 100644 --- a/osf_tests/test_elastic_search.py +++ b/osf_tests/test_elastic_search.py @@ -841,7 +841,7 @@ def setUp(self): creator=self.user, is_public=True, ) - self.registration.archive_job.target_addons = [] + self.registration.archive_job.target_addons.clear() self.registration.archive_job.status = 'SUCCESS' self.registration.archive_job.save() diff --git a/osf_tests/test_user.py b/osf_tests/test_user.py index c1066bf58c3..d7db43e47c3 100644 --- a/osf_tests/test_user.py +++ b/osf_tests/test_user.py @@ -1900,8 +1900,8 @@ def is_mrm_field(value): self.user.notifications_configured = {'abc12': True} other_user.notifications_configured = {'123ab': True} - self.user.external_accounts = [ExternalAccountFactory()] - other_user.external_accounts = [ExternalAccountFactory()] + self.user.external_accounts.add(ExternalAccountFactory()) + other_user.external_accounts.add(ExternalAccountFactory()) self.user.mailchimp_mailing_lists = { 'user': True, diff --git a/requirements.txt b/requirements.txt index 39d0e5e45f6..6defbe442a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,10 +62,10 @@ requests==2.20.0 urllib3==1.22 oauthlib==2.0.6 requests-oauthlib==0.8.0 -raven==5.32.0 +raven==6.4.0 # API requirements -Django==1.11.15 # pyup: <2.0 # Remove this when we're on Py3 +Django==1.11.28 djangorestframework==3.8.2 django-cors-headers==2.1.0 djangorestframework-bulk==0.2.1 diff --git a/tests/test_preprints.py b/tests/test_preprints.py index 5224d518c1b..0bf971003c7 100644 --- a/tests/test_preprints.py +++ b/tests/test_preprints.py @@ -2059,7 +2059,7 @@ def test_format_preprint_date_modified_node_updated(self): assert preprint['date_updated'] == self.preprint.modified.isoformat() def test_format_preprint_nones(self): - self.preprint.tags = [] + self.preprint.tags.clear() self.preprint.date_published = None self.preprint.article_doi = None self.preprint.set_subjects([], auth=Auth(self.preprint.creator))