Merge pull request #973 from seglberg/feature/#958-django-split
Removed Django Support from MongoEngine +1 @thedrow @MRigal @DavidBord @rozza
This commit is contained in:
		
							
								
								
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -8,24 +8,18 @@ python: | |||||||
| - pypy | - pypy | ||||||
| - pypy3 | - pypy3 | ||||||
| env: | env: | ||||||
| - PYMONGO=2.7.2 DJANGO=dev | - PYMONGO=2.7.2 | ||||||
| - PYMONGO=2.7.2 DJANGO=1.7.1 | - PYMONGO=2.8 | ||||||
| - PYMONGO=2.7.2 DJANGO=1.6.8 |  | ||||||
| - PYMONGO=2.7.2 DJANGO=1.5.11 |  | ||||||
| - PYMONGO=2.8 DJANGO=dev |  | ||||||
| - PYMONGO=2.8 DJANGO=1.7.1 |  | ||||||
| - PYMONGO=2.8 DJANGO=1.6.8 |  | ||||||
| - PYMONGO=2.8 DJANGO=1.5.11 |  | ||||||
| matrix: | matrix: | ||||||
|   exclude: |   exclude: | ||||||
|   - python: '2.6' |   - python: '2.6' | ||||||
|     env: PYMONGO=2.7.2 DJANGO=dev |     env: PYMONGO=2.7.2  | ||||||
|   - python: '2.6' |   - python: '2.6' | ||||||
|     env: PYMONGO=2.8 DJANGO=dev |     env: PYMONGO=2.8 | ||||||
|   - python: '2.6' |   - python: '2.6' | ||||||
|     env: PYMONGO=2.7.2 DJANGO=1.7.1 |     env: PYMONGO=2.7.2 | ||||||
|   - python: '2.6' |   - python: '2.6' | ||||||
|     env: PYMONGO=2.8 DJANGO=1.7.1 |     env: PYMONGO=2.8 | ||||||
|   allow_failures: |   allow_failures: | ||||||
|   - python: pypy3 |   - python: pypy3 | ||||||
|   fast_finish: true |   fast_finish: true | ||||||
| @@ -43,9 +37,6 @@ install: | |||||||
|   true; fi |   true; fi | ||||||
| - if [[ $PYMONGO != 'dev' ]]; then travis_retry pip install pymongo==$PYMONGO; true; | - if [[ $PYMONGO != 'dev' ]]; then travis_retry pip install pymongo==$PYMONGO; true; | ||||||
|   fi |   fi | ||||||
| - if [[ $DJANGO == 'dev' ]]; then travis_retry pip install git+https://github.com/django/django.git; |  | ||||||
|   fi |  | ||||||
| - if [[ $DJANGO != 'dev' ]]; then travis_retry pip install Django==$DJANGO; fi |  | ||||||
| - travis_retry pip install https://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.1.tar.gz#md5=1534bb15cf311f07afaa3aacba1c028b | - travis_retry pip install https://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.1.tar.gz#md5=1534bb15cf311f07afaa3aacba1c028b | ||||||
| - travis_retry pip install coveralls | - travis_retry pip install coveralls | ||||||
| - travis_retry python setup.py install | - travis_retry python setup.py install | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ Changes in 0.9.X - DEV | |||||||
| - Use sets for populating dbrefs to dereference | - Use sets for populating dbrefs to dereference | ||||||
| - Fixed unpickled documents replacing the global field's list. #888 | - Fixed unpickled documents replacing the global field's list. #888 | ||||||
| - Fixed storage of microseconds in ComplexDateTimeField and unused separator option. #910 | - Fixed storage of microseconds in ComplexDateTimeField and unused separator option. #910 | ||||||
|  | - Django support was removed and will be available as a separate extension. #958 | ||||||
|  |  | ||||||
| Changes in 0.9.0 | Changes in 0.9.0 | ||||||
| ================ | ================ | ||||||
|   | |||||||
							
								
								
									
										180
									
								
								docs/django.rst
									
									
									
									
									
								
							
							
						
						
									
										180
									
								
								docs/django.rst
									
									
									
									
									
								
							| @@ -2,176 +2,18 @@ | |||||||
| Django Support | Django Support | ||||||
| ============== | ============== | ||||||
|  |  | ||||||
| .. note:: Updated to support Django 1.5 | .. note:: Django support has been split from the main MongoEngine | ||||||
|  |     repository. The *legacy* Django extension may be found bundled with the | ||||||
| Connecting |     0.9 release of MongoEngine. | ||||||
| ========== |  | ||||||
| In your **settings.py** file, ignore the standard database settings (unless you |  | ||||||
| also plan to use the ORM in your project), and instead call |  | ||||||
| :func:`~mongoengine.connect` somewhere in the settings module. |  | ||||||
|  |  | ||||||
| .. note:: |  | ||||||
|    If you are not using another Database backend you may need to add a dummy |  | ||||||
|    database backend to ``settings.py`` eg:: |  | ||||||
|  |  | ||||||
|         DATABASES = { |  | ||||||
|             'default': { |  | ||||||
|                 'ENGINE': 'django.db.backends.dummy' |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
| Authentication |  | ||||||
| ============== |  | ||||||
| MongoEngine includes a Django authentication backend, which uses MongoDB. The |  | ||||||
| :class:`~mongoengine.django.auth.User` model is a MongoEngine |  | ||||||
| :class:`~mongoengine.Document`, but implements most of the methods and |  | ||||||
| attributes that the standard Django :class:`User` model does - so the two are |  | ||||||
| moderately compatible. Using this backend will allow you to store users in |  | ||||||
| MongoDB but still use many of the Django authentication infrastructure (such as |  | ||||||
| the :func:`login_required` decorator and the :func:`authenticate` function). To |  | ||||||
| enable the MongoEngine auth backend, add the following to your **settings.py** |  | ||||||
| file:: |  | ||||||
|  |  | ||||||
|     AUTHENTICATION_BACKENDS = ( |  | ||||||
|         'mongoengine.django.auth.MongoEngineBackend', |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
| The :mod:`~mongoengine.django.auth` module also contains a |  | ||||||
| :func:`~mongoengine.django.auth.get_user` helper function, that takes a user's |  | ||||||
| :attr:`id` and returns a :class:`~mongoengine.django.auth.User` object. |  | ||||||
|  |  | ||||||
| .. versionadded:: 0.1.3 |  | ||||||
|  |  | ||||||
| Custom User model |  | ||||||
| ================= |  | ||||||
| Django 1.5 introduced `Custom user Models |  | ||||||
| <https://docs.djangoproject.com/en/dev/topics/auth/customizing/#auth-custom-user>`_ |  | ||||||
| which can be used as an alternative to the MongoEngine authentication backend. |  | ||||||
|  |  | ||||||
| The main advantage of this option is that other components relying on |  | ||||||
| :mod:`django.contrib.auth` and supporting the new swappable user model are more |  | ||||||
| likely to work. For example, you can use the ``createsuperuser`` management |  | ||||||
| command as usual. |  | ||||||
|  |  | ||||||
| To enable the custom User model in Django, add ``'mongoengine.django.mongo_auth'`` |  | ||||||
| in your ``INSTALLED_APPS`` and set ``'mongo_auth.MongoUser'`` as the custom user |  | ||||||
| user model to use. In your **settings.py** file you will have:: |  | ||||||
|  |  | ||||||
|     INSTALLED_APPS = ( |  | ||||||
|         ... |  | ||||||
|         'django.contrib.auth', |  | ||||||
|         'mongoengine.django.mongo_auth', |  | ||||||
|         ... |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     AUTH_USER_MODEL = 'mongo_auth.MongoUser' |  | ||||||
|  |  | ||||||
| An additional ``MONGOENGINE_USER_DOCUMENT`` setting enables you to replace the |  | ||||||
| :class:`~mongoengine.django.auth.User` class with another class of your choice:: |  | ||||||
|  |  | ||||||
|     MONGOENGINE_USER_DOCUMENT = 'mongoengine.django.auth.User' |  | ||||||
|  |  | ||||||
| The custom :class:`User` must be a :class:`~mongoengine.Document` class, but |  | ||||||
| otherwise has the same requirements as a standard custom user model, |  | ||||||
| as specified in the `Django Documentation |  | ||||||
| <https://docs.djangoproject.com/en/dev/topics/auth/customizing/>`_. |  | ||||||
| In particular, the custom class must define :attr:`USERNAME_FIELD` and |  | ||||||
| :attr:`REQUIRED_FIELDS` attributes. |  | ||||||
|  |  | ||||||
| Sessions |  | ||||||
| ======== |  | ||||||
| Django allows the use of different backend stores for its sessions. MongoEngine |  | ||||||
| provides a MongoDB-based session backend for Django, which allows you to use |  | ||||||
| sessions in your Django application with just MongoDB. To enable the MongoEngine |  | ||||||
| session backend, ensure that your settings module has |  | ||||||
| ``'django.contrib.sessions.middleware.SessionMiddleware'`` in the |  | ||||||
| ``MIDDLEWARE_CLASSES`` field  and ``'django.contrib.sessions'`` in your |  | ||||||
| ``INSTALLED_APPS``. From there, all you need to do is add the following line |  | ||||||
| into your settings module:: |  | ||||||
|  |  | ||||||
|     SESSION_ENGINE = 'mongoengine.django.sessions' |  | ||||||
|     SESSION_SERIALIZER = 'mongoengine.django.sessions.BSONSerializer' |  | ||||||
|  |  | ||||||
| Django provides session cookie, which expires after ```SESSION_COOKIE_AGE``` seconds, but doesn't delete cookie at sessions backend, so ``'mongoengine.django.sessions'`` supports  `mongodb TTL |  | ||||||
| <http://docs.mongodb.org/manual/tutorial/expire-data/>`_. |  | ||||||
|  |  | ||||||
| .. note:: ``SESSION_SERIALIZER`` is only necessary in Django 1.6 as the default |  | ||||||
|    serializer is based around JSON and doesn't know how to convert |  | ||||||
|    ``bson.objectid.ObjectId`` instances to strings. |  | ||||||
|  |  | ||||||
| .. versionadded:: 0.2.1 |  | ||||||
|  |  | ||||||
| Storage |  | ||||||
| ======= |  | ||||||
| With MongoEngine's support for GridFS via the :class:`~mongoengine.fields.FileField`, |  | ||||||
| it is useful to have a Django file storage backend that wraps this. The new |  | ||||||
| storage module is called :class:`~mongoengine.django.storage.GridFSStorage`. |  | ||||||
| Using it is very similar to using the default FileSystemStorage.:: |  | ||||||
|  |  | ||||||
|     from mongoengine.django.storage import GridFSStorage |  | ||||||
|     fs = GridFSStorage() |  | ||||||
|  |  | ||||||
|     filename = fs.save('hello.txt', 'Hello, World!') |  | ||||||
|  |  | ||||||
| All of the `Django Storage API methods |  | ||||||
| <http://docs.djangoproject.com/en/dev/ref/files/storage/>`_ have been |  | ||||||
| implemented except :func:`path`. If the filename provided already exists, an |  | ||||||
| underscore and a number (before # the file extension, if one exists) will be |  | ||||||
| appended to the filename until the generated filename doesn't exist. The |  | ||||||
| :func:`save` method will return the new filename.:: |  | ||||||
|  |  | ||||||
|     >>> fs.exists('hello.txt') |  | ||||||
|     True |  | ||||||
|     >>> fs.open('hello.txt').read() |  | ||||||
|     'Hello, World!' |  | ||||||
|     >>> fs.size('hello.txt') |  | ||||||
|     13 |  | ||||||
|     >>> fs.url('hello.txt') |  | ||||||
|     'http://your_media_url/hello.txt' |  | ||||||
|     >>> fs.open('hello.txt').name |  | ||||||
|     'hello.txt' |  | ||||||
|     >>> fs.listdir() |  | ||||||
|     ([], [u'hello.txt']) |  | ||||||
|  |  | ||||||
| All files will be saved and retrieved in GridFS via the :class:`FileDocument` |  | ||||||
| document, allowing easy access to the files without the GridFSStorage |  | ||||||
| backend.:: |  | ||||||
|  |  | ||||||
|     >>> from mongoengine.django.storage import FileDocument |  | ||||||
|     >>> FileDocument.objects() |  | ||||||
|     [<FileDocument: FileDocument object>] |  | ||||||
|  |  | ||||||
| .. versionadded:: 0.4 |  | ||||||
|  |  | ||||||
| Shortcuts |  | ||||||
| ========= |  | ||||||
| Inspired by the `Django shortcut get_object_or_404 |  | ||||||
| <https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#get-object-or-404>`_, |  | ||||||
| the :func:`~mongoengine.django.shortcuts.get_document_or_404` method returns  |  | ||||||
| a document or raises an Http404 exception if the document does not exist:: |  | ||||||
|  |  | ||||||
|     from mongoengine.django.shortcuts import get_document_or_404 |  | ||||||
|      |  | ||||||
|     admin_user = get_document_or_404(User, username='root') |  | ||||||
|  |  | ||||||
| The first argument may be a Document or QuerySet object. All other passed arguments |  | ||||||
| and keyword arguments are used in the query:: |  | ||||||
|  |  | ||||||
|     foo_email = get_document_or_404(User.objects.only('email'), username='foo', is_active=True).email |  | ||||||
|  |  | ||||||
| .. note:: Like with :func:`get`, a MultipleObjectsReturned will be raised if more than one |  | ||||||
|     object is found. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Also inspired by the `Django shortcut get_list_or_404 |  | ||||||
| <https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#get-list-or-404>`_, |  | ||||||
| the :func:`~mongoengine.django.shortcuts.get_list_or_404` method returns a list of |  | ||||||
| documents or raises an Http404 exception if the list is empty:: |  | ||||||
|  |  | ||||||
|     from mongoengine.django.shortcuts import get_list_or_404 | Help Wanted! | ||||||
|      | ------------ | ||||||
|     active_users = get_list_or_404(User, is_active=True) |  | ||||||
|  |  | ||||||
| The first argument may be a Document or QuerySet object. All other passed |  | ||||||
| arguments and keyword arguments are used to filter the query. |  | ||||||
|  |  | ||||||
|  | The MongoEngine team is looking for help contributing and maintaining a new | ||||||
|  | Django extension for MongoEngine! If you have Django experience and would like | ||||||
|  | to help contribute to the project, please get in touch on the  | ||||||
|  | `mailing list <http://groups.google.com/group/mongoengine-users>`_ or by  | ||||||
|  | simpily contributing on  | ||||||
|  | `GitHub <https://github.com/MongoEngine/django-mongoengine>`_. | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ import signals | |||||||
| from signals import * | from signals import * | ||||||
| from errors import * | from errors import * | ||||||
| import errors | import errors | ||||||
| import django |  | ||||||
|  |  | ||||||
| __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ + | __all__ = (list(document.__all__) + fields.__all__ + connection.__all__ + | ||||||
|            list(queryset.__all__) + signals.__all__ + list(errors.__all__)) |            list(queryset.__all__) + signals.__all__ + list(errors.__all__)) | ||||||
|   | |||||||
| @@ -1,412 +0,0 @@ | |||||||
| from mongoengine import * |  | ||||||
|  |  | ||||||
| from django.utils.encoding import smart_str |  | ||||||
| from django.contrib.auth.models import _user_has_perm, _user_get_all_permissions, _user_has_module_perms |  | ||||||
| from django.db import models |  | ||||||
| from django.contrib.contenttypes.models import ContentTypeManager |  | ||||||
| from django.contrib import auth |  | ||||||
| from django.contrib.auth.models import AnonymousUser |  | ||||||
| from django.utils.translation import ugettext_lazy as _ |  | ||||||
|  |  | ||||||
| from .utils import datetime_now |  | ||||||
|  |  | ||||||
| REDIRECT_FIELD_NAME = 'next' |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     from django.contrib.auth.hashers import check_password, make_password |  | ||||||
| except ImportError: |  | ||||||
|     """Handle older versions of Django""" |  | ||||||
|     from django.utils.hashcompat import md5_constructor, sha_constructor |  | ||||||
|  |  | ||||||
|     def get_hexdigest(algorithm, salt, raw_password): |  | ||||||
|         raw_password, salt = smart_str(raw_password), smart_str(salt) |  | ||||||
|         if algorithm == 'md5': |  | ||||||
|             return md5_constructor(salt + raw_password).hexdigest() |  | ||||||
|         elif algorithm == 'sha1': |  | ||||||
|             return sha_constructor(salt + raw_password).hexdigest() |  | ||||||
|         raise ValueError('Got unknown password algorithm type in password') |  | ||||||
|  |  | ||||||
|     def check_password(raw_password, password): |  | ||||||
|         algo, salt, hash = password.split('$') |  | ||||||
|         return hash == get_hexdigest(algo, salt, raw_password) |  | ||||||
|  |  | ||||||
|     def make_password(raw_password): |  | ||||||
|         from random import random |  | ||||||
|         algo = 'sha1' |  | ||||||
|         salt = get_hexdigest(algo, str(random()), str(random()))[:5] |  | ||||||
|         hash = get_hexdigest(algo, salt, raw_password) |  | ||||||
|         return '%s$%s$%s' % (algo, salt, hash) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ContentType(Document): |  | ||||||
|     name = StringField(max_length=100) |  | ||||||
|     app_label = StringField(max_length=100) |  | ||||||
|     model = StringField(max_length=100, verbose_name=_('python model class name'), |  | ||||||
|                         unique_with='app_label') |  | ||||||
|     objects = ContentTypeManager() |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|         verbose_name = _('content type') |  | ||||||
|         verbose_name_plural = _('content types') |  | ||||||
|         # db_table = 'django_content_type' |  | ||||||
|         # ordering = ('name',) |  | ||||||
|         # unique_together = (('app_label', 'model'),) |  | ||||||
|  |  | ||||||
|     def __unicode__(self): |  | ||||||
|         return self.name |  | ||||||
|  |  | ||||||
|     def model_class(self): |  | ||||||
|         "Returns the Python model class for this type of content." |  | ||||||
|         from django.db import models |  | ||||||
|         return models.get_model(self.app_label, self.model) |  | ||||||
|  |  | ||||||
|     def get_object_for_this_type(self, **kwargs): |  | ||||||
|         """ |  | ||||||
|         Returns an object of this type for the keyword arguments given. |  | ||||||
|         Basically, this is a proxy around this object_type's get_object() model |  | ||||||
|         method. The ObjectNotExist exception, if thrown, will not be caught, |  | ||||||
|         so code that calls this method should catch it. |  | ||||||
|         """ |  | ||||||
|         return self.model_class()._default_manager.using(self._state.db).get(**kwargs) |  | ||||||
|  |  | ||||||
|     def natural_key(self): |  | ||||||
|         return (self.app_label, self.model) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class SiteProfileNotAvailable(Exception): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class PermissionManager(models.Manager): |  | ||||||
|     def get_by_natural_key(self, codename, app_label, model): |  | ||||||
|         return self.get( |  | ||||||
|             codename=codename, |  | ||||||
|             content_type=ContentType.objects.get_by_natural_key(app_label, model) |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Permission(Document): |  | ||||||
|     """The permissions system provides a way to assign permissions to specific |  | ||||||
|     users and groups of users. |  | ||||||
|  |  | ||||||
|     The permission system is used by the Django admin site, but may also be |  | ||||||
|     useful in your own code. The Django admin site uses permissions as follows: |  | ||||||
|  |  | ||||||
|         - The "add" permission limits the user's ability to view the "add" |  | ||||||
|           form and add an object. |  | ||||||
|         - The "change" permission limits a user's ability to view the change |  | ||||||
|           list, view the "change" form and change an object. |  | ||||||
|         - The "delete" permission limits the ability to delete an object. |  | ||||||
|  |  | ||||||
|     Permissions are set globally per type of object, not per specific object |  | ||||||
|     instance. It is possible to say "Mary may change news stories," but it's |  | ||||||
|     not currently possible to say "Mary may change news stories, but only the |  | ||||||
|     ones she created herself" or "Mary may only change news stories that have |  | ||||||
|     a certain status or publication date." |  | ||||||
|  |  | ||||||
|     Three basic permissions -- add, change and delete -- are automatically |  | ||||||
|     created for each Django model. |  | ||||||
|     """ |  | ||||||
|     name = StringField(max_length=50, verbose_name=_('username')) |  | ||||||
|     content_type = ReferenceField(ContentType) |  | ||||||
|     codename = StringField(max_length=100, verbose_name=_('codename')) |  | ||||||
|         # FIXME: don't access field of the other class |  | ||||||
|         # unique_with=['content_type__app_label', 'content_type__model']) |  | ||||||
|  |  | ||||||
|     objects = PermissionManager() |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|         verbose_name = _('permission') |  | ||||||
|         verbose_name_plural = _('permissions') |  | ||||||
|         # unique_together = (('content_type', 'codename'),) |  | ||||||
|         # ordering = ('content_type__app_label', 'content_type__model', 'codename') |  | ||||||
|  |  | ||||||
|     def __unicode__(self): |  | ||||||
|         return u"%s | %s | %s" % ( |  | ||||||
|             unicode(self.content_type.app_label), |  | ||||||
|             unicode(self.content_type), |  | ||||||
|             unicode(self.name)) |  | ||||||
|  |  | ||||||
|     def natural_key(self): |  | ||||||
|         return (self.codename,) + self.content_type.natural_key() |  | ||||||
|     natural_key.dependencies = ['contenttypes.contenttype'] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Group(Document): |  | ||||||
|     """Groups are a generic way of categorizing users to apply permissions, |  | ||||||
|     or some other label, to those users. A user can belong to any number of |  | ||||||
|     groups. |  | ||||||
|  |  | ||||||
|     A user in a group automatically has all the permissions granted to that |  | ||||||
|     group. For example, if the group Site editors has the permission |  | ||||||
|     can_edit_home_page, any user in that group will have that permission. |  | ||||||
|  |  | ||||||
|     Beyond permissions, groups are a convenient way to categorize users to |  | ||||||
|     apply some label, or extended functionality, to them. For example, you |  | ||||||
|     could create a group 'Special users', and you could write code that would |  | ||||||
|     do special things to those users -- such as giving them access to a |  | ||||||
|     members-only portion of your site, or sending them members-only |  | ||||||
|     e-mail messages. |  | ||||||
|     """ |  | ||||||
|     name = StringField(max_length=80, unique=True, verbose_name=_('name')) |  | ||||||
|     permissions = ListField(ReferenceField(Permission, verbose_name=_('permissions'), required=False)) |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|         verbose_name = _('group') |  | ||||||
|         verbose_name_plural = _('groups') |  | ||||||
|  |  | ||||||
|     def __unicode__(self): |  | ||||||
|         return self.name |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserManager(models.Manager): |  | ||||||
|     def create_user(self, username, email, password=None): |  | ||||||
|         """ |  | ||||||
|         Creates and saves a User with the given username, e-mail and password. |  | ||||||
|         """ |  | ||||||
|         now = datetime_now() |  | ||||||
|  |  | ||||||
|         # Normalize the address by lowercasing the domain part of the email |  | ||||||
|         # address. |  | ||||||
|         try: |  | ||||||
|             email_name, domain_part = email.strip().split('@', 1) |  | ||||||
|         except ValueError: |  | ||||||
|             pass |  | ||||||
|         else: |  | ||||||
|             email = '@'.join([email_name, domain_part.lower()]) |  | ||||||
|  |  | ||||||
|         user = self.model(username=username, email=email, is_staff=False, |  | ||||||
|                           is_active=True, is_superuser=False, last_login=now, |  | ||||||
|                           date_joined=now) |  | ||||||
|  |  | ||||||
|         user.set_password(password) |  | ||||||
|         user.save(using=self._db) |  | ||||||
|         return user |  | ||||||
|  |  | ||||||
|     def create_superuser(self, username, email, password): |  | ||||||
|         u = self.create_user(username, email, password) |  | ||||||
|         u.is_staff = True |  | ||||||
|         u.is_active = True |  | ||||||
|         u.is_superuser = True |  | ||||||
|         u.save(using=self._db) |  | ||||||
|         return u |  | ||||||
|  |  | ||||||
|     def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'): |  | ||||||
|         "Generates a random password with the given length and given allowed_chars" |  | ||||||
|         # Note that default value of allowed_chars does not have "I" or letters |  | ||||||
|         # that look like it -- just to avoid confusion. |  | ||||||
|         from random import choice |  | ||||||
|         return ''.join([choice(allowed_chars) for i in range(length)]) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class User(Document): |  | ||||||
|     """A User document that aims to mirror most of the API specified by Django |  | ||||||
|     at http://docs.djangoproject.com/en/dev/topics/auth/#users |  | ||||||
|     """ |  | ||||||
|     username = StringField(max_length=30, required=True, |  | ||||||
|                            verbose_name=_('username'), |  | ||||||
|                            help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters")) |  | ||||||
|  |  | ||||||
|     first_name = StringField(max_length=30, |  | ||||||
|                              verbose_name=_('first name')) |  | ||||||
|  |  | ||||||
|     last_name = StringField(max_length=30, |  | ||||||
|                             verbose_name=_('last name')) |  | ||||||
|     email = EmailField(verbose_name=_('e-mail address')) |  | ||||||
|     password = StringField(max_length=128, |  | ||||||
|                            verbose_name=_('password'), |  | ||||||
|                            help_text=_("Use '[algo]$[iterations]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>.")) |  | ||||||
|     is_staff = BooleanField(default=False, |  | ||||||
|                             verbose_name=_('staff status'), |  | ||||||
|                             help_text=_("Designates whether the user can log into this admin site.")) |  | ||||||
|     is_active = BooleanField(default=True, |  | ||||||
|                              verbose_name=_('active'), |  | ||||||
|                              help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")) |  | ||||||
|     is_superuser = BooleanField(default=False, |  | ||||||
|                                 verbose_name=_('superuser status'), |  | ||||||
|                                 help_text=_("Designates that this user has all permissions without explicitly assigning them.")) |  | ||||||
|     last_login = DateTimeField(default=datetime_now, |  | ||||||
|                                verbose_name=_('last login')) |  | ||||||
|     date_joined = DateTimeField(default=datetime_now, |  | ||||||
|                                 verbose_name=_('date joined')) |  | ||||||
|  |  | ||||||
|     user_permissions = ListField(ReferenceField(Permission), verbose_name=_('user permissions'), |  | ||||||
|                                                 help_text=_('Permissions for the user.')) |  | ||||||
|  |  | ||||||
|     USERNAME_FIELD = 'username' |  | ||||||
|     REQUIRED_FIELDS = ['email'] |  | ||||||
|  |  | ||||||
|     meta = { |  | ||||||
|         'allow_inheritance': True, |  | ||||||
|         'indexes': [ |  | ||||||
|             {'fields': ['username'], 'unique': True, 'sparse': True} |  | ||||||
|         ] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     def __unicode__(self): |  | ||||||
|         return self.username |  | ||||||
|  |  | ||||||
|     def get_full_name(self): |  | ||||||
|         """Returns the users first and last names, separated by a space. |  | ||||||
|         """ |  | ||||||
|         full_name = u'%s %s' % (self.first_name or '', self.last_name or '') |  | ||||||
|         return full_name.strip() |  | ||||||
|  |  | ||||||
|     def is_anonymous(self): |  | ||||||
|         return False |  | ||||||
|  |  | ||||||
|     def is_authenticated(self): |  | ||||||
|         return True |  | ||||||
|  |  | ||||||
|     def set_password(self, raw_password): |  | ||||||
|         """Sets the user's password - always use this rather than directly |  | ||||||
|         assigning to :attr:`~mongoengine.django.auth.User.password` as the |  | ||||||
|         password is hashed before storage. |  | ||||||
|         """ |  | ||||||
|         self.password = make_password(raw_password) |  | ||||||
|         self.save() |  | ||||||
|         return self |  | ||||||
|  |  | ||||||
|     def check_password(self, raw_password): |  | ||||||
|         """Checks the user's password against a provided password - always use |  | ||||||
|         this rather than directly comparing to |  | ||||||
|         :attr:`~mongoengine.django.auth.User.password` as the password is |  | ||||||
|         hashed before storage. |  | ||||||
|         """ |  | ||||||
|         return check_password(raw_password, self.password) |  | ||||||
|  |  | ||||||
|     @classmethod |  | ||||||
|     def create_user(cls, username, password, email=None): |  | ||||||
|         """Create (and save) a new user with the given username, password and |  | ||||||
|         email address. |  | ||||||
|         """ |  | ||||||
|         now = datetime_now() |  | ||||||
|  |  | ||||||
|         # Normalize the address by lowercasing the domain part of the email |  | ||||||
|         # address. |  | ||||||
|         if email is not None: |  | ||||||
|             try: |  | ||||||
|                 email_name, domain_part = email.strip().split('@', 1) |  | ||||||
|             except ValueError: |  | ||||||
|                 pass |  | ||||||
|             else: |  | ||||||
|                 email = '@'.join([email_name, domain_part.lower()]) |  | ||||||
|  |  | ||||||
|         user = cls(username=username, email=email, date_joined=now) |  | ||||||
|         user.set_password(password) |  | ||||||
|         user.save() |  | ||||||
|         return user |  | ||||||
|  |  | ||||||
|     def get_group_permissions(self, obj=None): |  | ||||||
|         """ |  | ||||||
|         Returns a list of permission strings that this user has through his/her |  | ||||||
|         groups. This method queries all available auth backends. If an object |  | ||||||
|         is passed in, only permissions matching this object are returned. |  | ||||||
|         """ |  | ||||||
|         permissions = set() |  | ||||||
|         for backend in auth.get_backends(): |  | ||||||
|             if hasattr(backend, "get_group_permissions"): |  | ||||||
|                 permissions.update(backend.get_group_permissions(self, obj)) |  | ||||||
|         return permissions |  | ||||||
|  |  | ||||||
|     def get_all_permissions(self, obj=None): |  | ||||||
|         return _user_get_all_permissions(self, obj) |  | ||||||
|  |  | ||||||
|     def has_perm(self, perm, obj=None): |  | ||||||
|         """ |  | ||||||
|         Returns True if the user has the specified permission. This method |  | ||||||
|         queries all available auth backends, but returns immediately if any |  | ||||||
|         backend returns True. Thus, a user who has permission from a single |  | ||||||
|         auth backend is assumed to have permission in general. If an object is |  | ||||||
|         provided, permissions for this specific object are checked. |  | ||||||
|         """ |  | ||||||
|  |  | ||||||
|         # Active superusers have all permissions. |  | ||||||
|         if self.is_active and self.is_superuser: |  | ||||||
|             return True |  | ||||||
|  |  | ||||||
|         # Otherwise we need to check the backends. |  | ||||||
|         return _user_has_perm(self, perm, obj) |  | ||||||
|  |  | ||||||
|     def has_module_perms(self, app_label): |  | ||||||
|         """ |  | ||||||
|         Returns True if the user has any permissions in the given app label. |  | ||||||
|         Uses pretty much the same logic as has_perm, above. |  | ||||||
|         """ |  | ||||||
|         # Active superusers have all permissions. |  | ||||||
|         if self.is_active and self.is_superuser: |  | ||||||
|             return True |  | ||||||
|  |  | ||||||
|         return _user_has_module_perms(self, app_label) |  | ||||||
|  |  | ||||||
|     def email_user(self, subject, message, from_email=None): |  | ||||||
|         "Sends an e-mail to this User." |  | ||||||
|         from django.core.mail import send_mail |  | ||||||
|         send_mail(subject, message, from_email, [self.email]) |  | ||||||
|  |  | ||||||
|     def get_profile(self): |  | ||||||
|         """ |  | ||||||
|         Returns site-specific profile for this user. Raises |  | ||||||
|         SiteProfileNotAvailable if this site does not allow profiles. |  | ||||||
|         """ |  | ||||||
|         if not hasattr(self, '_profile_cache'): |  | ||||||
|             from django.conf import settings |  | ||||||
|             if not getattr(settings, 'AUTH_PROFILE_MODULE', False): |  | ||||||
|                 raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO' |  | ||||||
|                                               'DULE in your project settings') |  | ||||||
|             try: |  | ||||||
|                 app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') |  | ||||||
|             except ValueError: |  | ||||||
|                 raise SiteProfileNotAvailable('app_label and model_name should' |  | ||||||
|                         ' be separated by a dot in the AUTH_PROFILE_MODULE set' |  | ||||||
|                         'ting') |  | ||||||
|  |  | ||||||
|             try: |  | ||||||
|                 model = models.get_model(app_label, model_name) |  | ||||||
|                 if model is None: |  | ||||||
|                     raise SiteProfileNotAvailable('Unable to load the profile ' |  | ||||||
|                         'model, check AUTH_PROFILE_MODULE in your project sett' |  | ||||||
|                         'ings') |  | ||||||
|                 self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id) |  | ||||||
|                 self._profile_cache.user = self |  | ||||||
|             except (ImportError, ImproperlyConfigured): |  | ||||||
|                 raise SiteProfileNotAvailable |  | ||||||
|         return self._profile_cache |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoEngineBackend(object): |  | ||||||
|     """Authenticate using MongoEngine and mongoengine.django.auth.User. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     supports_object_permissions = False |  | ||||||
|     supports_anonymous_user = False |  | ||||||
|     supports_inactive_user = False |  | ||||||
|     _user_doc = False |  | ||||||
|  |  | ||||||
|     def authenticate(self, username=None, password=None): |  | ||||||
|         user = self.user_document.objects(username=username).first() |  | ||||||
|         if user: |  | ||||||
|             if password and user.check_password(password): |  | ||||||
|                 backend = auth.get_backends()[0] |  | ||||||
|                 user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) |  | ||||||
|                 return user |  | ||||||
|         return None |  | ||||||
|  |  | ||||||
|     def get_user(self, user_id): |  | ||||||
|         return self.user_document.objects.with_id(user_id) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def user_document(self): |  | ||||||
|         if self._user_doc is False: |  | ||||||
|             from .mongo_auth.models import get_user_document |  | ||||||
|             self._user_doc = get_user_document() |  | ||||||
|         return self._user_doc |  | ||||||
|  |  | ||||||
| def get_user(userid): |  | ||||||
|     """Returns a User object from an id (User.id). Django's equivalent takes |  | ||||||
|     request, but taking an id instead leaves it up to the developer to store |  | ||||||
|     the id in any way they want (session, signed cookie, etc.) |  | ||||||
|     """ |  | ||||||
|     if not userid: |  | ||||||
|         return AnonymousUser() |  | ||||||
|     return MongoEngineBackend().get_user(userid) or AnonymousUser() |  | ||||||
| @@ -1,119 +0,0 @@ | |||||||
| from django.conf import settings |  | ||||||
| from django.contrib.auth.hashers import make_password |  | ||||||
| from django.contrib.auth.models import UserManager |  | ||||||
| from django.core.exceptions import ImproperlyConfigured |  | ||||||
| from django.db import models |  | ||||||
| try: |  | ||||||
|     from django.utils.module_loading import import_module |  | ||||||
| except ImportError: |  | ||||||
|     """Handle older versions of Django""" |  | ||||||
|     from django.utils.importlib import import_module |  | ||||||
| from django.utils.translation import ugettext_lazy as _ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ( |  | ||||||
|     'get_user_document', |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| MONGOENGINE_USER_DOCUMENT = getattr( |  | ||||||
|     settings, 'MONGOENGINE_USER_DOCUMENT', 'mongoengine.django.auth.User') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_user_document(): |  | ||||||
|     """Get the user document class used for authentication. |  | ||||||
|  |  | ||||||
|     This is the class defined in settings.MONGOENGINE_USER_DOCUMENT, which |  | ||||||
|     defaults to `mongoengine.django.auth.User`. |  | ||||||
|  |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     name = MONGOENGINE_USER_DOCUMENT |  | ||||||
|     dot = name.rindex('.') |  | ||||||
|     module = import_module(name[:dot]) |  | ||||||
|     return getattr(module, name[dot + 1:]) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoUserManager(UserManager): |  | ||||||
|     """A User manager wich allows the use of MongoEngine documents in Django. |  | ||||||
|  |  | ||||||
|     To use the manager, you must tell django.contrib.auth to use MongoUser as |  | ||||||
|     the user model. In you settings.py, you need: |  | ||||||
|  |  | ||||||
|         INSTALLED_APPS = ( |  | ||||||
|             ... |  | ||||||
|             'django.contrib.auth', |  | ||||||
|             'mongoengine.django.mongo_auth', |  | ||||||
|             ... |  | ||||||
|         ) |  | ||||||
|         AUTH_USER_MODEL = 'mongo_auth.MongoUser' |  | ||||||
|  |  | ||||||
|     Django will use the model object to access the custom Manager, which will |  | ||||||
|     replace the original queryset with MongoEngine querysets. |  | ||||||
|  |  | ||||||
|     By default, mongoengine.django.auth.User will be used to store users. You |  | ||||||
|     can specify another document class in MONGOENGINE_USER_DOCUMENT in your |  | ||||||
|     settings.py. |  | ||||||
|  |  | ||||||
|     The User Document class has the same requirements as a standard custom user |  | ||||||
|     model: https://docs.djangoproject.com/en/dev/topics/auth/customizing/ |  | ||||||
|  |  | ||||||
|     In particular, the User Document class must define USERNAME_FIELD and |  | ||||||
|     REQUIRED_FIELDS. |  | ||||||
|  |  | ||||||
|     `AUTH_USER_MODEL` has been added in Django 1.5. |  | ||||||
|  |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     def contribute_to_class(self, model, name): |  | ||||||
|         super(MongoUserManager, self).contribute_to_class(model, name) |  | ||||||
|         self.dj_model = self.model |  | ||||||
|         self.model = get_user_document() |  | ||||||
|  |  | ||||||
|         self.dj_model.USERNAME_FIELD = self.model.USERNAME_FIELD |  | ||||||
|         username = models.CharField(_('username'), max_length=30, unique=True) |  | ||||||
|         username.contribute_to_class(self.dj_model, self.dj_model.USERNAME_FIELD) |  | ||||||
|  |  | ||||||
|         self.dj_model.REQUIRED_FIELDS = self.model.REQUIRED_FIELDS |  | ||||||
|         for name in self.dj_model.REQUIRED_FIELDS: |  | ||||||
|             field = models.CharField(_(name), max_length=30) |  | ||||||
|             field.contribute_to_class(self.dj_model, name) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     def get(self, *args, **kwargs): |  | ||||||
|         try: |  | ||||||
|             return self.get_query_set().get(*args, **kwargs) |  | ||||||
|         except self.model.DoesNotExist: |  | ||||||
|             # ModelBackend expects this exception |  | ||||||
|             raise self.dj_model.DoesNotExist |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def db(self): |  | ||||||
|         raise NotImplementedError |  | ||||||
|  |  | ||||||
|     def get_empty_query_set(self): |  | ||||||
|         return self.model.objects.none() |  | ||||||
|  |  | ||||||
|     def get_query_set(self): |  | ||||||
|         return self.model.objects |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoUser(models.Model): |  | ||||||
|     """"Dummy user model for Django. |  | ||||||
|  |  | ||||||
|     MongoUser is used to replace Django's UserManager with MongoUserManager. |  | ||||||
|     The actual user document class is mongoengine.django.auth.User or any |  | ||||||
|     other document class specified in MONGOENGINE_USER_DOCUMENT. |  | ||||||
|  |  | ||||||
|     To get the user document class, use `get_user_document()`. |  | ||||||
|  |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     objects = MongoUserManager() |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|         app_label = 'mongo_auth' |  | ||||||
|  |  | ||||||
|     def set_password(self, password): |  | ||||||
|         """Doesn't do anything, but works around the issue with Django 1.6.""" |  | ||||||
|         make_password(password) |  | ||||||
| @@ -1,124 +0,0 @@ | |||||||
| from bson import json_util |  | ||||||
| from django.conf import settings |  | ||||||
| from django.contrib.sessions.backends.base import SessionBase, CreateError |  | ||||||
| from django.core.exceptions import SuspiciousOperation |  | ||||||
| try: |  | ||||||
|     from django.utils.encoding import force_unicode |  | ||||||
| except ImportError: |  | ||||||
|     from django.utils.encoding import force_text as force_unicode |  | ||||||
|  |  | ||||||
| from mongoengine.document import Document |  | ||||||
| from mongoengine import fields |  | ||||||
| from mongoengine.queryset import OperationError |  | ||||||
| from mongoengine.connection import DEFAULT_CONNECTION_NAME |  | ||||||
|  |  | ||||||
| from .utils import datetime_now |  | ||||||
|  |  | ||||||
|  |  | ||||||
| MONGOENGINE_SESSION_DB_ALIAS = getattr( |  | ||||||
|     settings, 'MONGOENGINE_SESSION_DB_ALIAS', |  | ||||||
|     DEFAULT_CONNECTION_NAME) |  | ||||||
|  |  | ||||||
| # a setting for the name of the collection used to store sessions |  | ||||||
| MONGOENGINE_SESSION_COLLECTION = getattr( |  | ||||||
|     settings, 'MONGOENGINE_SESSION_COLLECTION', |  | ||||||
|     'django_session') |  | ||||||
|  |  | ||||||
| # a setting for whether session data is stored encoded or not |  | ||||||
| MONGOENGINE_SESSION_DATA_ENCODE = getattr( |  | ||||||
|     settings, 'MONGOENGINE_SESSION_DATA_ENCODE', |  | ||||||
|     True) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoSession(Document): |  | ||||||
|     session_key = fields.StringField(primary_key=True, max_length=40) |  | ||||||
|     session_data = fields.StringField() if MONGOENGINE_SESSION_DATA_ENCODE \ |  | ||||||
|                                         else fields.DictField() |  | ||||||
|     expire_date = fields.DateTimeField() |  | ||||||
|  |  | ||||||
|     meta = { |  | ||||||
|         'collection': MONGOENGINE_SESSION_COLLECTION, |  | ||||||
|         'db_alias': MONGOENGINE_SESSION_DB_ALIAS, |  | ||||||
|         'allow_inheritance': False, |  | ||||||
|         'indexes': [ |  | ||||||
|             { |  | ||||||
|                 'fields': ['expire_date'], |  | ||||||
|                 'expireAfterSeconds': 0 |  | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     def get_decoded(self): |  | ||||||
|         return SessionStore().decode(self.session_data) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class SessionStore(SessionBase): |  | ||||||
|     """A MongoEngine-based session store for Django. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     def _get_session(self, *args, **kwargs): |  | ||||||
|         sess = super(SessionStore, self)._get_session(*args, **kwargs) |  | ||||||
|         if sess.get('_auth_user_id', None): |  | ||||||
|             sess['_auth_user_id'] = str(sess.get('_auth_user_id')) |  | ||||||
|         return sess |  | ||||||
|  |  | ||||||
|     def load(self): |  | ||||||
|         try: |  | ||||||
|             s = MongoSession.objects(session_key=self.session_key, |  | ||||||
|                                      expire_date__gt=datetime_now)[0] |  | ||||||
|             if MONGOENGINE_SESSION_DATA_ENCODE: |  | ||||||
|                 return self.decode(force_unicode(s.session_data)) |  | ||||||
|             else: |  | ||||||
|                 return s.session_data |  | ||||||
|         except (IndexError, SuspiciousOperation): |  | ||||||
|             self.create() |  | ||||||
|             return {} |  | ||||||
|  |  | ||||||
|     def exists(self, session_key): |  | ||||||
|         return bool(MongoSession.objects(session_key=session_key).first()) |  | ||||||
|  |  | ||||||
|     def create(self): |  | ||||||
|         while True: |  | ||||||
|             self._session_key = self._get_new_session_key() |  | ||||||
|             try: |  | ||||||
|                 self.save(must_create=True) |  | ||||||
|             except CreateError: |  | ||||||
|                 continue |  | ||||||
|             self.modified = True |  | ||||||
|             self._session_cache = {} |  | ||||||
|             return |  | ||||||
|  |  | ||||||
|     def save(self, must_create=False): |  | ||||||
|         if self.session_key is None: |  | ||||||
|             self._session_key = self._get_new_session_key() |  | ||||||
|         s = MongoSession(session_key=self.session_key) |  | ||||||
|         if MONGOENGINE_SESSION_DATA_ENCODE: |  | ||||||
|             s.session_data = self.encode(self._get_session(no_load=must_create)) |  | ||||||
|         else: |  | ||||||
|             s.session_data = self._get_session(no_load=must_create) |  | ||||||
|         s.expire_date = self.get_expiry_date() |  | ||||||
|         try: |  | ||||||
|             s.save(force_insert=must_create) |  | ||||||
|         except OperationError: |  | ||||||
|             if must_create: |  | ||||||
|                 raise CreateError |  | ||||||
|             raise |  | ||||||
|  |  | ||||||
|     def delete(self, session_key=None): |  | ||||||
|         if session_key is None: |  | ||||||
|             if self.session_key is None: |  | ||||||
|                 return |  | ||||||
|             session_key = self.session_key |  | ||||||
|         MongoSession.objects(session_key=session_key).delete() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class BSONSerializer(object): |  | ||||||
|     """ |  | ||||||
|     Serializer that can handle BSON types (eg ObjectId). |  | ||||||
|     """ |  | ||||||
|     def dumps(self, obj): |  | ||||||
|         return json_util.dumps(obj, separators=(',', ':')).encode('ascii') |  | ||||||
|  |  | ||||||
|     def loads(self, data): |  | ||||||
|         return json_util.loads(data.decode('ascii')) |  | ||||||
|  |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| from mongoengine.queryset import QuerySet |  | ||||||
| from mongoengine.base import BaseDocument |  | ||||||
| from mongoengine.errors import ValidationError |  | ||||||
|  |  | ||||||
| def _get_queryset(cls): |  | ||||||
|     """Inspired by django.shortcuts.*""" |  | ||||||
|     if isinstance(cls, QuerySet): |  | ||||||
|         return cls |  | ||||||
|     else: |  | ||||||
|         return cls.objects |  | ||||||
|  |  | ||||||
| def get_document_or_404(cls, *args, **kwargs): |  | ||||||
|     """ |  | ||||||
|     Uses get() to return an document, or raises a Http404 exception if the document |  | ||||||
|     does not exist. |  | ||||||
|  |  | ||||||
|     cls may be a Document or QuerySet object. All other passed |  | ||||||
|     arguments and keyword arguments are used in the get() query. |  | ||||||
|  |  | ||||||
|     Note: Like with get(), an MultipleObjectsReturned will be raised if more than one |  | ||||||
|     object is found. |  | ||||||
|  |  | ||||||
|     Inspired by django.shortcuts.* |  | ||||||
|     """ |  | ||||||
|     queryset = _get_queryset(cls) |  | ||||||
|     try: |  | ||||||
|         return queryset.get(*args, **kwargs) |  | ||||||
|     except (queryset._document.DoesNotExist, ValidationError): |  | ||||||
|         from django.http import Http404 |  | ||||||
|         raise Http404('No %s matches the given query.' % queryset._document._class_name) |  | ||||||
|  |  | ||||||
| def get_list_or_404(cls, *args, **kwargs): |  | ||||||
|     """ |  | ||||||
|     Uses filter() to return a list of documents, or raise a Http404 exception if |  | ||||||
|     the list is empty. |  | ||||||
|  |  | ||||||
|     cls may be a Document or QuerySet object. All other passed |  | ||||||
|     arguments and keyword arguments are used in the filter() query. |  | ||||||
|  |  | ||||||
|     Inspired by django.shortcuts.* |  | ||||||
|     """ |  | ||||||
|     queryset = _get_queryset(cls) |  | ||||||
|     obj_list = list(queryset.filter(*args, **kwargs)) |  | ||||||
|     if not obj_list: |  | ||||||
|         from django.http import Http404 |  | ||||||
|         raise Http404('No %s matches the given query.' % queryset._document._class_name) |  | ||||||
|     return obj_list |  | ||||||
| @@ -1,112 +0,0 @@ | |||||||
| import os |  | ||||||
| import itertools |  | ||||||
| import urlparse |  | ||||||
|  |  | ||||||
| from mongoengine import * |  | ||||||
| from django.conf import settings |  | ||||||
| from django.core.files.storage import Storage |  | ||||||
| from django.core.exceptions import ImproperlyConfigured |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class FileDocument(Document): |  | ||||||
|     """A document used to store a single file in GridFS. |  | ||||||
|     """ |  | ||||||
|     file = FileField() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class GridFSStorage(Storage): |  | ||||||
|     """A custom storage backend to store files in GridFS |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     def __init__(self, base_url=None): |  | ||||||
|  |  | ||||||
|         if base_url is None: |  | ||||||
|             base_url = settings.MEDIA_URL |  | ||||||
|         self.base_url = base_url |  | ||||||
|         self.document = FileDocument |  | ||||||
|         self.field = 'file' |  | ||||||
|  |  | ||||||
|     def delete(self, name): |  | ||||||
|         """Deletes the specified file from the storage system. |  | ||||||
|         """ |  | ||||||
|         if self.exists(name): |  | ||||||
|             doc = self.document.objects.first() |  | ||||||
|             field = getattr(doc, self.field) |  | ||||||
|             self._get_doc_with_name(name).delete()  # Delete the FileField |  | ||||||
|             field.delete()                          # Delete the FileDocument |  | ||||||
|  |  | ||||||
|     def exists(self, name): |  | ||||||
|         """Returns True if a file referened by the given name already exists in the |  | ||||||
|         storage system, or False if the name is available for a new file. |  | ||||||
|         """ |  | ||||||
|         doc = self._get_doc_with_name(name) |  | ||||||
|         if doc: |  | ||||||
|             field = getattr(doc, self.field) |  | ||||||
|             return bool(field.name) |  | ||||||
|         else: |  | ||||||
|             return False |  | ||||||
|  |  | ||||||
|     def listdir(self, path=None): |  | ||||||
|         """Lists the contents of the specified path, returning a 2-tuple of lists; |  | ||||||
|         the first item being directories, the second item being files. |  | ||||||
|         """ |  | ||||||
|         def name(doc): |  | ||||||
|             return getattr(doc, self.field).name |  | ||||||
|         docs = self.document.objects |  | ||||||
|         return [], [name(d) for d in docs if name(d)] |  | ||||||
|  |  | ||||||
|     def size(self, name): |  | ||||||
|         """Returns the total size, in bytes, of the file specified by name. |  | ||||||
|         """ |  | ||||||
|         doc = self._get_doc_with_name(name) |  | ||||||
|         if doc: |  | ||||||
|             return getattr(doc, self.field).length |  | ||||||
|         else: |  | ||||||
|             raise ValueError("No such file or directory: '%s'" % name) |  | ||||||
|  |  | ||||||
|     def url(self, name): |  | ||||||
|         """Returns an absolute URL where the file's contents can be accessed |  | ||||||
|         directly by a web browser. |  | ||||||
|         """ |  | ||||||
|         if self.base_url is None: |  | ||||||
|             raise ValueError("This file is not accessible via a URL.") |  | ||||||
|         return urlparse.urljoin(self.base_url, name).replace('\\', '/') |  | ||||||
|  |  | ||||||
|     def _get_doc_with_name(self, name): |  | ||||||
|         """Find the documents in the store with the given name |  | ||||||
|         """ |  | ||||||
|         docs = self.document.objects |  | ||||||
|         doc = [d for d in docs if hasattr(getattr(d, self.field), 'name') and getattr(d, self.field).name == name] |  | ||||||
|         if doc: |  | ||||||
|             return doc[0] |  | ||||||
|         else: |  | ||||||
|             return None |  | ||||||
|  |  | ||||||
|     def _open(self, name, mode='rb'): |  | ||||||
|         doc = self._get_doc_with_name(name) |  | ||||||
|         if doc: |  | ||||||
|             return getattr(doc, self.field) |  | ||||||
|         else: |  | ||||||
|             raise ValueError("No file found with the name '%s'." % name) |  | ||||||
|  |  | ||||||
|     def get_available_name(self, name): |  | ||||||
|         """Returns a filename that's free on the target storage system, and |  | ||||||
|         available for new content to be written to. |  | ||||||
|         """ |  | ||||||
|         file_root, file_ext = os.path.splitext(name) |  | ||||||
|         # If the filename already exists, add an underscore and a number (before |  | ||||||
|         # the file extension, if one exists) to the filename until the generated |  | ||||||
|         # filename doesn't exist. |  | ||||||
|         count = itertools.count(1) |  | ||||||
|         while self.exists(name): |  | ||||||
|             # file_ext includes the dot. |  | ||||||
|             name = os.path.join("%s_%s%s" % (file_root, count.next(), file_ext)) |  | ||||||
|  |  | ||||||
|         return name |  | ||||||
|  |  | ||||||
|     def _save(self, name, content): |  | ||||||
|         doc = self.document() |  | ||||||
|         getattr(doc, self.field).put(content, filename=name) |  | ||||||
|         doc.save() |  | ||||||
|  |  | ||||||
|         return name |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| #coding: utf-8 |  | ||||||
|  |  | ||||||
| from unittest import TestCase |  | ||||||
|  |  | ||||||
| from mongoengine import connect |  | ||||||
| from mongoengine.connection import get_db |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoTestCase(TestCase): |  | ||||||
|     """ |  | ||||||
|     TestCase class that clear the collection between the tests |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def db_name(self): |  | ||||||
|         from django.conf import settings |  | ||||||
|         return 'test_%s' % getattr(settings, 'MONGO_DATABASE_NAME', 'dummy') |  | ||||||
|  |  | ||||||
|     def __init__(self, methodName='runtest'): |  | ||||||
|         connect(self.db_name) |  | ||||||
|         self.db = get_db() |  | ||||||
|         super(MongoTestCase, self).__init__(methodName) |  | ||||||
|  |  | ||||||
|     def dropCollections(self): |  | ||||||
|         for collection in self.db.collection_names(): |  | ||||||
|             if collection.startswith('system.'): |  | ||||||
|                 continue |  | ||||||
|             self.db.drop_collection(collection) |  | ||||||
|  |  | ||||||
|     def tearDown(self): |  | ||||||
|         self.dropCollections() |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| try: |  | ||||||
|     # django >= 1.4 |  | ||||||
|     from django.utils.timezone import now as datetime_now |  | ||||||
| except ImportError: |  | ||||||
|     from datetime import datetime |  | ||||||
|     datetime_now = datetime.now |  | ||||||
							
								
								
									
										4
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								setup.py
									
									
									
									
									
								
							| @@ -53,12 +53,12 @@ CLASSIFIERS = [ | |||||||
| extra_opts = {"packages": find_packages(exclude=["tests", "tests.*"])} | extra_opts = {"packages": find_packages(exclude=["tests", "tests.*"])} | ||||||
| if sys.version_info[0] == 3: | if sys.version_info[0] == 3: | ||||||
|     extra_opts['use_2to3'] = True |     extra_opts['use_2to3'] = True | ||||||
|     extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'jinja2==2.6', 'Pillow>=2.0.0', 'django>=1.5.1'] |     extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'Pillow>=2.0.0'] | ||||||
|     if "test" in sys.argv or "nosetests" in sys.argv: |     if "test" in sys.argv or "nosetests" in sys.argv: | ||||||
|         extra_opts['packages'] = find_packages() |         extra_opts['packages'] = find_packages() | ||||||
|         extra_opts['package_data'] = {"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]} |         extra_opts['package_data'] = {"tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]} | ||||||
| else: | else: | ||||||
|     extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'django>=1.4.2', 'Pillow>=2.0.0', 'jinja2>=2.6', 'python-dateutil'] |     extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'Pillow>=2.0.0', 'python-dateutil'] | ||||||
|  |  | ||||||
|     if sys.version_info[0] == 2 and sys.version_info[1] == 6: |     if sys.version_info[0] == 2 and sys.version_info[1] == 6: | ||||||
|         extra_opts['tests_require'].append('unittest2') |         extra_opts['tests_require'].append('unittest2') | ||||||
|   | |||||||
| @@ -1,330 +0,0 @@ | |||||||
| import sys |  | ||||||
| sys.path[0:0] = [""] |  | ||||||
| import unittest |  | ||||||
| from nose.plugins.skip import SkipTest |  | ||||||
|  |  | ||||||
| from mongoengine import * |  | ||||||
| from mongoengine.django.shortcuts import get_document_or_404 |  | ||||||
|  |  | ||||||
| import django |  | ||||||
| from django.http import Http404 |  | ||||||
| from django.template import Context, Template |  | ||||||
| from django.conf import settings |  | ||||||
| from django.core.paginator import Paginator |  | ||||||
|  |  | ||||||
| settings.configure( |  | ||||||
|     USE_TZ=True, |  | ||||||
|     INSTALLED_APPS=('django.contrib.auth', 'mongoengine.django.mongo_auth'), |  | ||||||
|     AUTH_USER_MODEL=('mongo_auth.MongoUser'), |  | ||||||
|     AUTHENTICATION_BACKENDS = ('mongoengine.django.auth.MongoEngineBackend',) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     # For Django >= 1.7 |  | ||||||
|     if hasattr(django, 'setup'): |  | ||||||
|         django.setup() |  | ||||||
| except RuntimeError: |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     from django.contrib.auth import authenticate, get_user_model |  | ||||||
|     from mongoengine.django.auth import User |  | ||||||
|     from mongoengine.django.mongo_auth.models import ( |  | ||||||
|         MongoUser, |  | ||||||
|         MongoUserManager, |  | ||||||
|         get_user_document, |  | ||||||
|     ) |  | ||||||
|     DJ15 = True |  | ||||||
| except Exception: |  | ||||||
|     DJ15 = False |  | ||||||
| from mongoengine.django.sessions import SessionStore, MongoSession |  | ||||||
| from mongoengine.django.tests import MongoTestCase |  | ||||||
| from datetime import tzinfo, timedelta |  | ||||||
| ZERO = timedelta(0) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class FixedOffset(tzinfo): |  | ||||||
|     """Fixed offset in minutes east from UTC.""" |  | ||||||
|  |  | ||||||
|     def __init__(self, offset, name): |  | ||||||
|         self.__offset = timedelta(minutes=offset) |  | ||||||
|         self.__name = name |  | ||||||
|  |  | ||||||
|     def utcoffset(self, dt): |  | ||||||
|         return self.__offset |  | ||||||
|  |  | ||||||
|     def tzname(self, dt): |  | ||||||
|         return self.__name |  | ||||||
|  |  | ||||||
|     def dst(self, dt): |  | ||||||
|         return ZERO |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def activate_timezone(tz): |  | ||||||
|     """Activate Django timezone support if it is available. |  | ||||||
|     """ |  | ||||||
|     try: |  | ||||||
|         from django.utils import timezone |  | ||||||
|         timezone.deactivate() |  | ||||||
|         timezone.activate(tz) |  | ||||||
|     except ImportError: |  | ||||||
|         pass |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class QuerySetTest(unittest.TestCase): |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         connect(db='mongoenginetest') |  | ||||||
|  |  | ||||||
|         class Person(Document): |  | ||||||
|             name = StringField() |  | ||||||
|             age = IntField() |  | ||||||
|         self.Person = Person |  | ||||||
|  |  | ||||||
|     def test_order_by_in_django_template(self): |  | ||||||
|         """Ensure that QuerySets are properly ordered in Django template. |  | ||||||
|         """ |  | ||||||
|         self.Person.drop_collection() |  | ||||||
|  |  | ||||||
|         self.Person(name="A", age=20).save() |  | ||||||
|         self.Person(name="D", age=10).save() |  | ||||||
|         self.Person(name="B", age=40).save() |  | ||||||
|         self.Person(name="C", age=30).save() |  | ||||||
|  |  | ||||||
|         t = Template("{% for o in ol %}{{ o.name }}-{{ o.age }}:{% endfor %}") |  | ||||||
|  |  | ||||||
|         d = {"ol": self.Person.objects.order_by('-name')} |  | ||||||
|         self.assertEqual(t.render(Context(d)), u'D-10:C-30:B-40:A-20:') |  | ||||||
|         d = {"ol": self.Person.objects.order_by('+name')} |  | ||||||
|         self.assertEqual(t.render(Context(d)), u'A-20:B-40:C-30:D-10:') |  | ||||||
|         d = {"ol": self.Person.objects.order_by('-age')} |  | ||||||
|         self.assertEqual(t.render(Context(d)), u'B-40:C-30:A-20:D-10:') |  | ||||||
|         d = {"ol": self.Person.objects.order_by('+age')} |  | ||||||
|         self.assertEqual(t.render(Context(d)), u'D-10:A-20:C-30:B-40:') |  | ||||||
|  |  | ||||||
|         self.Person.drop_collection() |  | ||||||
|  |  | ||||||
|     def test_q_object_filter_in_template(self): |  | ||||||
|  |  | ||||||
|         self.Person.drop_collection() |  | ||||||
|  |  | ||||||
|         self.Person(name="A", age=20).save() |  | ||||||
|         self.Person(name="D", age=10).save() |  | ||||||
|         self.Person(name="B", age=40).save() |  | ||||||
|         self.Person(name="C", age=30).save() |  | ||||||
|  |  | ||||||
|         t = Template("{% for o in ol %}{{ o.name }}-{{ o.age }}:{% endfor %}") |  | ||||||
|  |  | ||||||
|         d = {"ol": self.Person.objects.filter(Q(age=10) | Q(name="C"))} |  | ||||||
|         self.assertEqual(t.render(Context(d)), 'D-10:C-30:') |  | ||||||
|  |  | ||||||
|         # Check double rendering doesn't throw an error |  | ||||||
|         self.assertEqual(t.render(Context(d)), 'D-10:C-30:') |  | ||||||
|  |  | ||||||
|     def test_get_document_or_404(self): |  | ||||||
|         p = self.Person(name="G404") |  | ||||||
|         p.save() |  | ||||||
|  |  | ||||||
|         self.assertRaises(Http404, get_document_or_404, self.Person, pk='1234') |  | ||||||
|         self.assertEqual(p, get_document_or_404(self.Person, pk=p.pk)) |  | ||||||
|  |  | ||||||
|     def test_pagination(self): |  | ||||||
|         """Ensure that Pagination works as expected |  | ||||||
|         """ |  | ||||||
|         class Page(Document): |  | ||||||
|             name = StringField() |  | ||||||
|  |  | ||||||
|         Page.drop_collection() |  | ||||||
|  |  | ||||||
|         for i in xrange(1, 11): |  | ||||||
|             Page(name=str(i)).save() |  | ||||||
|  |  | ||||||
|         paginator = Paginator(Page.objects.all(), 2) |  | ||||||
|  |  | ||||||
|         t = Template("{% for i in page.object_list  %}{{ i.name }}:{% endfor %}") |  | ||||||
|         for p in paginator.page_range: |  | ||||||
|             d = {"page": paginator.page(p)} |  | ||||||
|             end = p * 2 |  | ||||||
|             start = end - 1 |  | ||||||
|             self.assertEqual(t.render(Context(d)), u'%d:%d:' % (start, end)) |  | ||||||
|  |  | ||||||
|     def test_nested_queryset_template_iterator(self): |  | ||||||
|         # Try iterating the same queryset twice, nested, in a Django template. |  | ||||||
|         names = ['A', 'B', 'C', 'D'] |  | ||||||
|  |  | ||||||
|         class CustomUser(Document): |  | ||||||
|             name = StringField() |  | ||||||
|  |  | ||||||
|             def __unicode__(self): |  | ||||||
|                 return self.name |  | ||||||
|  |  | ||||||
|         CustomUser.drop_collection() |  | ||||||
|  |  | ||||||
|         for name in names: |  | ||||||
|             CustomUser(name=name).save() |  | ||||||
|  |  | ||||||
|         users = CustomUser.objects.all().order_by('name') |  | ||||||
|         template = Template("{% for user in users %}{{ user.name }}{% ifequal forloop.counter 2 %} {% for inner_user in users %}{{ inner_user.name }}{% endfor %} {% endifequal %}{% endfor %}") |  | ||||||
|         rendered = template.render(Context({'users': users})) |  | ||||||
|         self.assertEqual(rendered, 'AB ABCD CD') |  | ||||||
|  |  | ||||||
|     def test_filter(self): |  | ||||||
|         """Ensure that a queryset and filters work as expected |  | ||||||
|         """ |  | ||||||
|  |  | ||||||
|         class LimitCountQuerySet(QuerySet): |  | ||||||
|             def count(self, with_limit_and_skip=True): |  | ||||||
|                 return super(LimitCountQuerySet, self).count(with_limit_and_skip) |  | ||||||
|  |  | ||||||
|         class Note(Document): |  | ||||||
|             meta = dict(queryset_class=LimitCountQuerySet) |  | ||||||
|             name = StringField() |  | ||||||
|  |  | ||||||
|         Note.drop_collection() |  | ||||||
|  |  | ||||||
|         for i in xrange(1, 101): |  | ||||||
|             Note(name="Note: %s" % i).save() |  | ||||||
|  |  | ||||||
|         # Check the count |  | ||||||
|         self.assertEqual(Note.objects.count(), 100) |  | ||||||
|  |  | ||||||
|         # Get the first 10 and confirm |  | ||||||
|         notes = Note.objects[:10] |  | ||||||
|         self.assertEqual(notes.count(), 10) |  | ||||||
|  |  | ||||||
|         # Test djangos template filters |  | ||||||
|         # self.assertEqual(length(notes), 10) |  | ||||||
|         t = Template("{{ notes.count }}") |  | ||||||
|         c = Context({"notes": notes}) |  | ||||||
|         self.assertEqual(t.render(c), "10") |  | ||||||
|  |  | ||||||
|         # Test with skip |  | ||||||
|         notes = Note.objects.skip(90) |  | ||||||
|         self.assertEqual(notes.count(), 10) |  | ||||||
|  |  | ||||||
|         # Test djangos template filters |  | ||||||
|         self.assertEqual(notes.count(), 10) |  | ||||||
|         t = Template("{{ notes.count }}") |  | ||||||
|         c = Context({"notes": notes}) |  | ||||||
|         self.assertEqual(t.render(c), "10") |  | ||||||
|  |  | ||||||
|         # Test with limit |  | ||||||
|         notes = Note.objects.skip(90) |  | ||||||
|         self.assertEqual(notes.count(), 10) |  | ||||||
|  |  | ||||||
|         # Test djangos template filters |  | ||||||
|         self.assertEqual(notes.count(), 10) |  | ||||||
|         t = Template("{{ notes.count }}") |  | ||||||
|         c = Context({"notes": notes}) |  | ||||||
|         self.assertEqual(t.render(c), "10") |  | ||||||
|  |  | ||||||
|         # Test with skip and limit |  | ||||||
|         notes = Note.objects.skip(10).limit(10) |  | ||||||
|  |  | ||||||
|         # Test djangos template filters |  | ||||||
|         self.assertEqual(notes.count(), 10) |  | ||||||
|         t = Template("{{ notes.count }}") |  | ||||||
|         c = Context({"notes": notes}) |  | ||||||
|         self.assertEqual(t.render(c), "10") |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class _BaseMongoDBSessionTest(unittest.TestCase): |  | ||||||
|     backend = SessionStore |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         connect(db='mongoenginetest') |  | ||||||
|         MongoSession.drop_collection() |  | ||||||
|         super(_BaseMongoDBSessionTest, self).setUp() |  | ||||||
|  |  | ||||||
|     def assertIn(self, first, second, msg=None): |  | ||||||
|         self.assertTrue(first in second, msg) |  | ||||||
|  |  | ||||||
|     def assertNotIn(self, first, second, msg=None): |  | ||||||
|         self.assertFalse(first in second, msg) |  | ||||||
|  |  | ||||||
|     def test_first_save(self): |  | ||||||
|         session = SessionStore() |  | ||||||
|         session['test'] = True |  | ||||||
|         session.save() |  | ||||||
|         self.assertTrue('test' in session) |  | ||||||
|  |  | ||||||
|     def test_session_expiration_tz(self): |  | ||||||
|         activate_timezone(FixedOffset(60, 'UTC+1')) |  | ||||||
|         # create and save new session |  | ||||||
|         session = SessionStore() |  | ||||||
|         session.set_expiry(600)  # expire in 600 seconds |  | ||||||
|         session['test_expire'] = True |  | ||||||
|         session.save() |  | ||||||
|         # reload session with key |  | ||||||
|         key = session.session_key |  | ||||||
|         session = SessionStore(key) |  | ||||||
|         self.assertTrue('test_expire' in session, 'Session has expired before it is expected') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| try: |  | ||||||
|     # SessionTestsMixin isn't available for import on django > 1.8a1 |  | ||||||
|     from django.contrib.sessions.tests import SessionTestsMixin |  | ||||||
|  |  | ||||||
|     class _MongoDBSessionTest(SessionTestsMixin): |  | ||||||
|         pass |  | ||||||
|  |  | ||||||
|     class MongoDBSessionTest(_BaseMongoDBSessionTest): |  | ||||||
|         pass |  | ||||||
|  |  | ||||||
| except ImportError: |  | ||||||
|     class MongoDBSessionTest(_BaseMongoDBSessionTest): |  | ||||||
|         pass |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoAuthTest(unittest.TestCase): |  | ||||||
|     user_data = { |  | ||||||
|         'username': 'user', |  | ||||||
|         'email': 'user@example.com', |  | ||||||
|         'password': 'test', |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         if not DJ15: |  | ||||||
|             raise SkipTest('mongo_auth requires Django 1.5') |  | ||||||
|         connect(db='mongoenginetest') |  | ||||||
|         User.drop_collection() |  | ||||||
|         super(MongoAuthTest, self).setUp() |  | ||||||
|  |  | ||||||
|     def test_get_user_model(self): |  | ||||||
|         self.assertEqual(get_user_model(), MongoUser) |  | ||||||
|  |  | ||||||
|     def test_get_user_document(self): |  | ||||||
|         self.assertEqual(get_user_document(), User) |  | ||||||
|  |  | ||||||
|     def test_user_manager(self): |  | ||||||
|         manager = get_user_model()._default_manager |  | ||||||
|         self.assertTrue(isinstance(manager, MongoUserManager)) |  | ||||||
|  |  | ||||||
|     def test_user_manager_exception(self): |  | ||||||
|         manager = get_user_model()._default_manager |  | ||||||
|         self.assertRaises(MongoUser.DoesNotExist, manager.get, |  | ||||||
|                           username='not found') |  | ||||||
|  |  | ||||||
|     def test_create_user(self): |  | ||||||
|         manager = get_user_model()._default_manager |  | ||||||
|         user = manager.create_user(**self.user_data) |  | ||||||
|         self.assertTrue(isinstance(user, User)) |  | ||||||
|         db_user = User.objects.get(username='user') |  | ||||||
|         self.assertEqual(user.id, db_user.id) |  | ||||||
|  |  | ||||||
|     def test_authenticate(self): |  | ||||||
|         get_user_model()._default_manager.create_user(**self.user_data) |  | ||||||
|         user = authenticate(username='user', password='fail') |  | ||||||
|         self.assertEqual(None, user) |  | ||||||
|         user = authenticate(username='user', password='test') |  | ||||||
|         db_user = User.objects.get(username='user') |  | ||||||
|         self.assertEqual(user.id, db_user.id) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class MongoTestCaseTest(MongoTestCase): |  | ||||||
|     def test_mongo_test_case(self): |  | ||||||
|         self.db.dummy_collection.insert({'collection': 'will be dropped'}) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     unittest.main() |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| import sys |  | ||||||
| sys.path[0:0] = [""] |  | ||||||
|  |  | ||||||
| import unittest |  | ||||||
|  |  | ||||||
| from mongoengine import * |  | ||||||
|  |  | ||||||
| import jinja2 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TemplateFilterTest(unittest.TestCase): |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         connect(db='mongoenginetest') |  | ||||||
|  |  | ||||||
|     def test_jinja2(self): |  | ||||||
|         env = jinja2.Environment() |  | ||||||
|  |  | ||||||
|         class TestData(Document): |  | ||||||
|             title = StringField() |  | ||||||
|             description = StringField() |  | ||||||
|  |  | ||||||
|         TestData.drop_collection() |  | ||||||
|  |  | ||||||
|         examples = [('A', '1'), |  | ||||||
|                     ('B', '2'), |  | ||||||
|                     ('C', '3')] |  | ||||||
|  |  | ||||||
|         for title, description in examples: |  | ||||||
|             TestData(title=title, description=description).save() |  | ||||||
|  |  | ||||||
|         tmpl = """ |  | ||||||
| {%- for record in content -%} |  | ||||||
|     {%- if loop.first -%}{ {%- endif -%} |  | ||||||
|     "{{ record.title }}": "{{ record.description }}" |  | ||||||
|     {%- if loop.last -%} }{%- else -%},{% endif -%} |  | ||||||
| {%- endfor -%} |  | ||||||
| """ |  | ||||||
|         ctx = {'content': TestData.objects} |  | ||||||
|         template = env.from_string(tmpl) |  | ||||||
|         rendered = template.render(**ctx) |  | ||||||
|  |  | ||||||
|         self.assertEqual('{"A": "1","B": "2","C": "3"}', rendered) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     unittest.main() |  | ||||||
		Reference in New Issue
	
	Block a user