From 99bee08fc5e0c0905d3595da24d30eb103e957df Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 10 Jan 2013 06:37:38 +0900 Subject: [PATCH 1/2] Introduce backends Add backends to select user. UsernameBackend is default backend that select users with username in `django.contrib.auth.models.User`. ProfilenameBackend is new backend that select users with field name setting in `AUTH_PROFILE_MODULE` model. --- django_messages/__init__.py | 31 +++++++++++++- django_messages/backends/__init__.py | 12 ++++++ django_messages/backends/profilename.py | 54 +++++++++++++++++++++++++ django_messages/backends/username.py | 13 ++++++ django_messages/fields.py | 10 ++--- django_messages/views.py | 10 +++-- 6 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 django_messages/backends/__init__.py create mode 100644 django_messages/backends/profilename.py create mode 100644 django_messages/backends/username.py diff --git a/django_messages/__init__.py b/django_messages/__init__.py index f7c0e87..7447306 100644 --- a/django_messages/__init__.py +++ b/django_messages/__init__.py @@ -1,2 +1,31 @@ +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.utils import importlib + VERSION = (0, 5, 0, 'pre') -__version__ = '.'.join(map(str, VERSION)) \ No newline at end of file +__version__ = '.'.join(map(str, VERSION)) + +BACKEND = getattr(settings, 'MESSAGES_BACKEND', + 'django_messages.backends.username.UsernameBackend') + +def _get_backend(full_backend_path): + from django_messages.backends import BaseMessageBackend + module, attr = full_backend_path.rsplit('.', 1) + try: + mod = importlib.import_module(module) + except ImportError, e: + raise ImproperlyConfigured('Error importing module %s: "%s"' % + (module, e)) + try: + Backend = getattr(mod, attr) + except AttributeError, e: + raise ImproperlyConfigured('Module "%s" does not define a "%s" ' + 'class.' % (module, attr)) + + if not issubclass(Backend, BaseMessageBackend): + raise ImproperlyConfigured('Backend "%s" is not a subclass of "%s"' % + (Backend, BaseMessageBackend)) + + return Backend() + +backend = _get_backend(BACKEND) \ No newline at end of file diff --git a/django_messages/backends/__init__.py b/django_messages/backends/__init__.py new file mode 100644 index 0000000..785428b --- /dev/null +++ b/django_messages/backends/__init__.py @@ -0,0 +1,12 @@ + +class BaseMessageBackend(object): + """ + Abstract message backend base class. + """ + def get_name(self, user): + """Get name from user object.""" + raise NotImplementedError + + def filter_users(self, names): + """Filter users with given names.""" + raise NotImplementedError diff --git a/django_messages/backends/profilename.py b/django_messages/backends/profilename.py new file mode 100644 index 0000000..be53823 --- /dev/null +++ b/django_messages/backends/profilename.py @@ -0,0 +1,54 @@ +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.utils import importlib +from django_messages.backends import BaseMessageBackend + +class ProfilenameBackend(BaseMessageBackend): + """ + Select user with configured fieldname of AUTH_PROFILE_MODULE. + The MESSAGES_PROFILENAME_FIELDNAME setting is required. + """ + def __init__(self): + if not getattr(settings, 'AUTH_PROFILE_MODULE', False): + raise ImproperlyConfigured( + 'You need to set AUTH_PROFILE_MODULE in your project settings') + try: + app, attr = settings.AUTH_PROFILE_MODULE.split('.') + module = app + '.models' + except ValueError: + raise ImproperlyConfigured( + 'app_label and model_name should be separated by a dot in the ' + 'AUTH_PROFILE_MODULE setting') + + try: + mod = importlib.import_module(module) + except ImportError, e: + raise ImproperlyConfigured('Error importing module %s: "%s"' % + (module, e)) + try: + Profile = getattr(mod, attr) + except AttributeError, e: + raise ImproperlyConfigured('Module "%s" does not define a "%s" ' + 'class.' % (module, attr)) + + self.Profile = Profile + + if not getattr(settings, 'MESSAGES_PROFILENAME_FIELDNAME', False): + raise ImproperlyConfigured( + 'The MESSAGES_PROFILENAME_FIELDNAME setting is required for Pro' + 'filename backend.') + self.fieldname = settings.MESSAGES_PROFILENAME_FIELDNAME + + #if not hasattr(self.Profile, self.fieldname): # any idea? + # raise ImproperlyConfigured( + # 'Profile class "%s" does not define a "%s" field' % + # (attr, self.fieldname)) + + def get_name(self, user): + return getattr(user.get_profile(), self.fieldname) + + def filter_users(self, names_set): + filterdict = {self.fieldname + '__in': names_set} + query = self.Profile.objects.select_related().filter(**filterdict) + users = [profile.user for profile in query] + return users diff --git a/django_messages/backends/username.py b/django_messages/backends/username.py new file mode 100644 index 0000000..45640d3 --- /dev/null +++ b/django_messages/backends/username.py @@ -0,0 +1,13 @@ + +from django_messages.backends import BaseMessageBackend +from django.contrib.auth.models import User + +class UsernameBackend(BaseMessageBackend): + """ + Select user with username of `django.contrib.auth` User. + """ + def get_name(self, user): + return user.username + + def filter_users(self, names_set): + return list(User.objects.filter(username__in=names_set)) diff --git a/django_messages/fields.py b/django_messages/fields.py index 41e3b47..2eb06bb 100644 --- a/django_messages/fields.py +++ b/django_messages/fields.py @@ -5,8 +5,8 @@ from django import forms from django.forms import widgets -from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ +from django_messages import backend class CommaSeparatedUserInput(widgets.Input): @@ -16,7 +16,7 @@ def render(self, name, value, attrs=None): if value is None: value = '' elif isinstance(value, (list, tuple)): - value = (', '.join([user.username for user in value])) + value = (', '.join([backend.get_name(user) for user in value])) return super(CommaSeparatedUserInput, self).render(name, value, attrs) @@ -38,8 +38,8 @@ def clean(self, value): names = set(value.split(',')) names_set = set([name.strip() for name in names if name.strip()]) - users = list(User.objects.filter(username__in=names_set)) - unknown_names = names_set ^ set([user.username for user in users]) + users = backend.filter_users(names_set) + unknown_names = names_set ^ set([backend.get_name(user) for user in users]) recipient_filter = self._recipient_filter invalid_users = [] @@ -47,7 +47,7 @@ def clean(self, value): for r in users: if recipient_filter(r) is False: users.remove(r) - invalid_users.append(r.username) + invalid_users.append(backend.get_name(r)) if unknown_names or invalid_users: raise forms.ValidationError(_(u"The following usernames are incorrect: %(users)s") % {'users': ', '.join(list(unknown_names)+invalid_users)}) diff --git a/django_messages/views.py b/django_messages/views.py index 57df275..d61252a 100644 --- a/django_messages/views.py +++ b/django_messages/views.py @@ -13,6 +13,7 @@ from django_messages.models import Message from django_messages.forms import ComposeForm from django_messages.utils import format_quote +from django_messages import backend if "notification" in settings.INSTALLED_APPS: from notification import models as notification @@ -63,9 +64,9 @@ def compose(request, recipient=None, form_class=ComposeForm, Displays and handles the ``form_class`` form to compose new messages. Required Arguments: None Optional Arguments: - ``recipient``: username of a `django.contrib.auth` User, who should - receive the message, optionally multiple usernames - could be separated by a '+' + ``recipient``: name of a user - username of `django.contrib.auth` User + at default, who should receive the message, optionally + multiple names could be separated by a '+' ``form_class``: the form-class to use ``template_name``: the template to use ``success_url``: where to redirect after successfull submission @@ -84,7 +85,8 @@ def compose(request, recipient=None, form_class=ComposeForm, else: form = form_class() if recipient is not None: - recipients = [u for u in User.objects.filter(username__in=[r.strip() for r in recipient.split('+')])] + names = [r.strip() for r in recipient.split('+')] + recipients = backend.filter_users(names) form.fields['recipient'].initial = recipients return render_to_response(template_name, { 'form': form, From 0e6965c49b6bcb81c6f51dfc19af98387dd637f4 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 29 Jan 2013 07:38:49 +0900 Subject: [PATCH 2/2] Resolve dependency cycle when install --- django_messages/__init__.py | 10 ++++++++-- django_messages/{tests.py => tests/__init__.py} | 0 django_messages/tests/settings.py | 0 django_messages/version.txt | 1 + setup.py | 8 +++++++- 5 files changed, 16 insertions(+), 3 deletions(-) rename django_messages/{tests.py => tests/__init__.py} (100%) create mode 100644 django_messages/tests/settings.py create mode 100644 django_messages/version.txt diff --git a/django_messages/__init__.py b/django_messages/__init__.py index 7447306..99cc342 100644 --- a/django_messages/__init__.py +++ b/django_messages/__init__.py @@ -1,9 +1,15 @@ +import pkg_resources from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils import importlib -VERSION = (0, 5, 0, 'pre') -__version__ = '.'.join(map(str, VERSION)) +__version__ = pkg_resources.resource_string('django_messages', 'version.txt').strip() + +def _version_tuple(__version__): + versions = __version__.split('-') + numversions = map(int, versions[0].split('.')) + return tuple(numversions + versions[1:]) +VERSION = _version_tuple(__version__) BACKEND = getattr(settings, 'MESSAGES_BACKEND', 'django_messages.backends.username.UsernameBackend') diff --git a/django_messages/tests.py b/django_messages/tests/__init__.py similarity index 100% rename from django_messages/tests.py rename to django_messages/tests/__init__.py diff --git a/django_messages/tests/settings.py b/django_messages/tests/settings.py new file mode 100644 index 0000000..e69de29 diff --git a/django_messages/version.txt b/django_messages/version.txt new file mode 100644 index 0000000..34b1e02 --- /dev/null +++ b/django_messages/version.txt @@ -0,0 +1 @@ +0.5.0-pre \ No newline at end of file diff --git a/setup.py b/setup.py index 7182843..74047e7 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,12 @@ from distutils.core import setup +def get_version(): + with open('django_messages/version.txt') as f: + return f.read().strip() + setup( name='django-messages', - version=__import__('django_messages').__version__, + version=get_version(), description='User-to-user messaging system for Django', long_description=open('README.rst').read(), author='Arne Brodowski', @@ -12,9 +16,11 @@ packages=( 'django_messages', 'django_messages.templatetags', + 'django_messages.backends', ), package_data={ 'django_messages': [ + 'version.txt', 'templates/django_messages/*', 'templates/notification/*/*', 'locale/*/LC_MESSAGES/*',