From b9afc95e70d7e78a8cff6e801fa9ed8fbcccd8a8 Mon Sep 17 00:00:00 2001 From: Ben Lopatin Date: Fri, 7 Jul 2017 21:31:37 -0400 Subject: [PATCH] Encapsulate organization.Organization model imports These should be made such that they are avoidable if explicit class arguments are provided to avoid unnecessarily importing concrete models which may not be installed. gh-131 --- organizations/backends/__init__.py | 4 ++-- organizations/backends/defaults.py | 29 ++++++++++++++------------ organizations/backends/forms.py | 27 ++++++++++++++---------- organizations/utils.py | 11 ++++++---- tests/test_backends.py | 33 ++++++++++++++++++++++-------- 5 files changed, 66 insertions(+), 38 deletions(-) diff --git a/organizations/backends/__init__.py b/organizations/backends/__init__.py index 81ad6270..8f000306 100644 --- a/organizations/backends/__init__.py +++ b/organizations/backends/__init__.py @@ -25,8 +25,8 @@ from importlib import import_module -from organizations.app_settings import (ORGS_INVITATION_BACKEND, - ORGS_REGISTRATION_BACKEND) +from organizations.app_settings import ORGS_INVITATION_BACKEND +from organizations.app_settings import ORGS_REGISTRATION_BACKEND def invitation_backend(): diff --git a/organizations/backends/defaults.py b/organizations/backends/defaults.py index d0c2f639..ceef671b 100644 --- a/organizations/backends/defaults.py +++ b/organizations/backends/defaults.py @@ -31,29 +31,32 @@ from django.conf import settings from django.conf.urls import url -from django.contrib.auth import authenticate, login, get_user_model -from django.core.urlresolvers import reverse +from django.contrib.auth import authenticate +from django.contrib.auth import get_user_model +from django.contrib.auth import login from django.core.mail import EmailMessage +from django.core.urlresolvers import reverse from django.http import Http404 -from django.shortcuts import render, redirect +from django.shortcuts import redirect +from django.shortcuts import render from django.template import loader from django.utils.translation import ugettext as _ -from ..utils import create_organization -from ..utils import model_field_attr -from .forms import UserRegistrationForm, OrganizationRegistrationForm -from .tokens import RegistrationTokenGenerator +from organizations.backends.forms import UserRegistrationForm +from organizations.backends.forms import org_registration_form +from organizations.backends.tokens import RegistrationTokenGenerator +from organizations.utils import create_organization +from organizations.utils import default_org_model +from organizations.utils import model_field_attr class BaseBackend(object): """ Base backend class for registering and inviting users to an organization """ - org_model = None - - def __init__(self, org_model=None, *args, **kwargs): + def __init__(self, org_model=None): self.user_model = get_user_model() - self.org_model = org_model + self.org_model = org_model or default_org_model() def get_urls(self): raise NotImplementedError @@ -92,7 +95,7 @@ def activate_organizations(self, user): relation_name = self.org_model().user_relation_name except TypeError: # No org_model specified, raises a TypeError because NoneType is - # not callable. Thiis the most sensible default + # not callable. This the most sensible default: relation_name = "organizations_organization" organization_set = getattr(user, relation_name) for org in organization_set.filter(is_active=False): @@ -215,7 +218,7 @@ def create_view(self, request): """ if request.user.is_authenticated(): return redirect("organization_add") - form = OrganizationRegistrationForm(request.POST or None) + form = org_registration_form(self.org_model)(request.POST or None) if form.is_valid(): try: user = self.user_model.objects.get(email=form.cleaned_data['email']) diff --git a/organizations/backends/forms.py b/organizations/backends/forms.py index 0d249bc5..aed6ac83 100644 --- a/organizations/backends/forms.py +++ b/organizations/backends/forms.py @@ -27,8 +27,6 @@ from django.contrib.auth import get_user_model from django.utils.translation import ugettext_lazy as _ -from ..models import Organization - class UserRegistrationForm(forms.ModelForm): """ @@ -61,14 +59,21 @@ class Meta: 'date_joined', 'groups', 'user_permissions') -class OrganizationRegistrationForm(forms.ModelForm): - """Form class for creating new organizations owned by new users.""" - email = forms.EmailField() +def org_registration_form(org_model): + """ + Generates a registration ModelForm for the given organization model class + """ + + class OrganizationRegistrationForm(forms.ModelForm): + """Form class for creating new organizations owned by new users.""" + email = forms.EmailField() + + class Meta: + model = org_model + exclude = ('is_active', 'users') - class Meta: - model = Organization - exclude = ('is_active', 'users') + def save(self, *args, **kwargs): + self.instance.is_active = False + super(OrganizationRegistrationForm, self).save(*args, **kwargs) - def save(self, *args, **kwargs): - self.instance.is_active = False - super(OrganizationRegistrationForm, self).save(*args, **kwargs) + return OrganizationRegistrationForm diff --git a/organizations/utils.py b/organizations/utils.py index 035c647b..2e5ce6a1 100644 --- a/organizations/utils.py +++ b/organizations/utils.py @@ -25,7 +25,11 @@ from itertools import chain -from .models import Organization + +def default_org_model(): + """Encapsulates importing the concrete model""" + from organizations.models import Organization + return Organization def model_field_names(model): @@ -41,8 +45,7 @@ def model_field_names(model): ))) -def create_organization(user, name, slug=None, is_active=None, - org_defaults=None, org_user_defaults=None, **kwargs): +def create_organization(user, name, slug=None, is_active=None, org_defaults=None, org_user_defaults=None, **kwargs): """ Returns a new organization, also creating an initial organization user who is the owner. @@ -56,7 +59,7 @@ def create_organization(user, name, slug=None, is_active=None, >>> create_account = partial(create_organization, model=Account) """ - org_model = kwargs.pop('model', None) or kwargs.pop('org_model', None) or Organization + org_model = kwargs.pop('model', None) or kwargs.pop('org_model', None) or default_org_model() kwargs.pop('org_user_model', None) # Discard deprecated argument org_owner_model = org_model.owner.related.related_model diff --git a/tests/test_backends.py b/tests/test_backends.py index 9163d58b..bd130b31 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -1,19 +1,20 @@ +from django.contrib.auth.models import User from django.core import mail from django.core.urlresolvers import reverse -from django.contrib.auth.models import User -from django.http import Http404, QueryDict +from django.http import Http404 +from django.http import QueryDict from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings -from test_vendors.models import Vendor -from test_abstract.models import CustomOrganization - -from organizations.backends.defaults import (BaseBackend, InvitationBackend, - RegistrationBackend) +from organizations.backends.defaults import BaseBackend +from organizations.backends.defaults import InvitationBackend +from organizations.backends.defaults import RegistrationBackend from organizations.backends.tokens import RegistrationTokenGenerator from organizations.models import Organization -from .utils import request_factory_login +from test_abstract.models import CustomOrganization +from test_vendors.models import Vendor +from tests.utils import request_factory_login @override_settings(USE_TZ=True) @@ -23,6 +24,22 @@ def test_generate_username(self): self.assertTrue(BaseBackend().get_username()) +@override_settings(INSTALLED_APPS=["django.contrib.auth", "django.contrib.contenttypes", + "django.contrib.sites", "test_accounts"], USE_TZ=True) +class TestSansOrganizations(TestCase): + + def test_verify_the_world(self): + """Verify the test environment is set up correctly""" + from django.conf import settings + self.assertFalse('organizations' in settings.INSTALLED_APPS) + + def test_generate_username(self): + self.assertTrue(BaseBackend().get_username()) + + def test_backend_urls(self): + self.assertTrue(InvitationBackend().get_urls()) + + @override_settings(USE_TZ=True) class InvitationTests(TestCase):