merged hmarr's updates

This commit is contained in:
blackbrrr 2009-12-19 14:31:17 -06:00
commit 8ad0df41a0
15 changed files with 277 additions and 68 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.pyc *.pyc
.*.swp .*.swp
docs/.build docs/.build
docs/_build

View File

@ -5,7 +5,7 @@
SPHINXOPTS = SPHINXOPTS =
SPHINXBUILD = sphinx-build SPHINXBUILD = sphinx-build
PAPER = PAPER =
BUILDDIR = .build BUILDDIR = _build
# Internal variables. # Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_a4 = -D latex_paper_size=a4

46
docs/apireference.rst Normal file
View File

@ -0,0 +1,46 @@
API Reference
=============
Connecting
----------
.. autofunction:: mongoengine.connect
Documents
---------
.. autoclass:: mongoengine.Document
:members:
.. attribute:: objects
A :class:`~mongoengine.queryset.QuerySet` object that is created lazily
on access.
.. autoclass:: mongoengine.EmbeddedDocument
:members:
Querying
--------
.. autoclass:: mongoengine.queryset.QuerySet
:members:
Fields
------
.. autoclass:: mongoengine.StringField
.. autoclass:: mongoengine.IntField
.. autoclass:: mongoengine.FloatField
.. autoclass:: mongoengine.DateTimeField
.. autoclass:: mongoengine.EmbeddedDocumentField
.. autoclass:: mongoengine.ListField
.. autoclass:: mongoengine.ObjectIdField
.. autoclass:: mongoengine.ReferenceField

View File

@ -16,13 +16,13 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.')) sys.path.append(os.path.abspath('..'))
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions # Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [] extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates'] templates_path = ['.templates']
@ -64,7 +64,7 @@ release = '0.1'
# List of directories, relative to source directory, that shouldn't be searched # List of directories, relative to source directory, that shouldn't be searched
# for source files. # for source files.
exclude_trees = ['.build'] exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents. # The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None #default_role = None
@ -99,7 +99,7 @@ html_theme = 'nature'
#html_theme_options = {} #html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory. # Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['.themes'] html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to # The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation". # "<project> v<release> documentation".
@ -120,7 +120,7 @@ html_theme_path = ['.themes']
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['.static'] html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format. # using the given strftime format.

View File

@ -3,7 +3,7 @@
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. contain the root `toctree` directive.
Welcome to MongoEngine's documentation! MongoEngine User Documentation
======================================= =======================================
Contents: Contents:
@ -12,6 +12,7 @@ Contents:
:maxdepth: 2 :maxdepth: 2
tutorial.rst tutorial.rst
apireference.rst
Indices and tables Indices and tables
================== ==================

View File

@ -136,20 +136,36 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
if attrs.get('__metaclass__') == TopLevelDocumentMetaclass: if attrs.get('__metaclass__') == TopLevelDocumentMetaclass:
return super_new(cls, name, bases, attrs) return super_new(cls, name, bases, attrs)
collection = attrs.get('__collection__', name.lower()) collection = name.lower()
simple_class = True
# Subclassed documents inherit collection from superclass # Subclassed documents inherit collection from superclass
for base in bases: for base in bases:
if hasattr(base, '_meta') and 'collection' in base._meta: if hasattr(base, '_meta') and 'collection' in base._meta:
# Ensure that the Document class may be subclassed -
# inheritance may be disabled to remove dependency on
# additional fields _cls and _types
if base._meta.get('allow_inheritance', True) == False:
raise ValueError('Document %s may not be subclassed' %
base.__name__)
else:
simple_class = False
collection = base._meta['collection'] collection = base._meta['collection']
meta = { meta = {
'collection': collection, 'collection': collection,
'allow_inheritance': True,
} }
meta.update(attrs.get('meta', {})) meta.update(attrs.get('meta', {}))
# Only simple classes - direct subclasses of Document - may set
# allow_inheritance to False
if not simple_class and not meta['allow_inheritance']:
raise ValueError('Only direct subclasses of Document may set '
'"allow_inheritance" to False')
attrs['_meta'] = meta attrs['_meta'] = meta
attrs['_id'] = ObjectIdField() attrs['id'] = ObjectIdField(name='_id')
# Set up collection manager, needs the class to have fields so use # Set up collection manager, needs the class to have fields so use
# DocumentMetaclass before instantiating CollectionManager object # DocumentMetaclass before instantiating CollectionManager object
@ -168,10 +184,11 @@ class BaseDocument(object):
if attr_name in values: if attr_name in values:
setattr(self, attr_name, values.pop(attr_name)) setattr(self, attr_name, values.pop(attr_name))
else: else:
if attr_value.required: # Use default value if present
value = getattr(self, attr_name, None)
if value is None and attr_value.required:
raise ValidationError('Field "%s" is required' % attr_name) raise ValidationError('Field "%s" is required' % attr_name)
# Use default value setattr(self, attr_name, value)
setattr(self, attr_name, getattr(self, attr_name, None))
@classmethod @classmethod
def _get_subclasses(cls): def _get_subclasses(cls):
@ -226,9 +243,12 @@ class BaseDocument(object):
for field_name, field in self._fields.items(): for field_name, field in self._fields.items():
value = getattr(self, field_name, None) value = getattr(self, field_name, None)
if value is not None: if value is not None:
data[field_name] = field.to_mongo(value) data[field.name] = field.to_mongo(value)
data['_cls'] = self._class_name # Only add _cls and _types if allow_inheritance is not False
data['_types'] = self._superclasses.keys() + [self._class_name] if not (hasattr(self, '_meta') and
self._meta.get('allow_inheritance', True) == False):
data['_cls'] = self._class_name
data['_types'] = self._superclasses.keys() + [self._class_name]
return data return data
@classmethod @classmethod
@ -241,6 +261,9 @@ class BaseDocument(object):
data = dict((str(key), value) for key, value in son.items()) data = dict((str(key), value) for key, value in son.items())
if '_types' in data:
del data['_types']
if '_cls' in data: if '_cls' in data:
del data['_cls'] del data['_cls']
@ -254,7 +277,7 @@ class BaseDocument(object):
cls = subclasses[class_name] cls = subclasses[class_name]
for field_name, field in cls._fields.items(): for field_name, field in cls._fields.items():
if field_name in data: if field.name in data:
data[field_name] = field.to_python(data[field_name]) data[field_name] = field.to_python(data[field.name])
return cls(**data) return cls(**data)

View File

@ -6,25 +6,58 @@ __all__ = ['Document', 'EmbeddedDocument']
class EmbeddedDocument(BaseDocument): class EmbeddedDocument(BaseDocument):
"""A :class:`~mongoengine.Document` that isn't stored in its own
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
fields on :class:`~mongoengine.Document`\ s through the
:class:`~mongoengine.EmbeddedDocumentField` field type.
"""
__metaclass__ = DocumentMetaclass __metaclass__ = DocumentMetaclass
class Document(BaseDocument): class Document(BaseDocument):
"""The base class used for defining the structure and properties of
collections of documents stored in MongoDB. Inherit from this class, and
add fields as class attributes to define a document's structure.
Individual documents may then be created by making instances of the
:class:`~mongoengine.Document` subclass.
By default, the MongoDB collection used to store documents created using a
:class:`~mongoengine.Document` subclass will be the name of the subclass
converted to lowercase. A different collection may be specified by
providing :attr:`collection` to the :attr:`meta` dictionary in the class
definition.
A :class:`~mongoengine.Document` subclass may be itself subclassed, to
create a specialised version of the document that will be stored in the
same collection. To facilitate this behaviour, `_cls` and `_types`
fields are added to documents (hidden though the MongoEngine interface
though). To disable this behaviour and remove the dependence on the
presence of `_cls` and `_types`, set :attr:`allow_inheritance` to
``False`` in the :attr:`meta` dictionary.
"""
__metaclass__ = TopLevelDocumentMetaclass __metaclass__ = TopLevelDocumentMetaclass
def save(self): def save(self):
"""Save the document to the database. If the document already exists, """Save the :class:`~mongoengine.Document` to the database. If the
it will be updated, otherwise it will be created. document already exists, it will be updated, otherwise it will be
created.
""" """
_id = self.objects._collection.save(self.to_mongo()) object_id = self.objects._collection.save(self.to_mongo())
self._id = _id self.id = object_id
def delete(self):
"""Delete the :class:`~mongoengine.Document` from the database. This
will only take effect if the document has been previously saved.
"""
object_id = self._fields['id'].to_mongo(self.id)
self.__class__.objects(_id=object_id).delete()
@classmethod @classmethod
def drop_collection(cls): def drop_collection(cls):
"""Drops the entire collection associated with this Document type from """Drops the entire collection associated with this
the database. :class:`~mongoengine.Document` type from the database.
""" """
db = _get_db() db = _get_db()
db.drop_collection(cls._meta['collection']) db.drop_collection(cls._meta['collection'])

View File

@ -87,7 +87,7 @@ class DateTimeField(BaseField):
class EmbeddedDocumentField(BaseField): class EmbeddedDocumentField(BaseField):
"""An embedded document field. Only valid values are subclasses of """An embedded document field. Only valid values are subclasses of
EmbeddedDocument. :class:`~mongoengine.EmbeddedDocument`.
""" """
def __init__(self, document, **kwargs): def __init__(self, document, **kwargs):
@ -179,19 +179,19 @@ class ReferenceField(BaseField):
def to_mongo(self, document): def to_mongo(self, document):
if isinstance(document, (str, unicode, pymongo.objectid.ObjectId)): if isinstance(document, (str, unicode, pymongo.objectid.ObjectId)):
_id = document id_ = document
else: else:
try: try:
_id = document._id id_ = document.id
except: except:
raise ValidationError('You can only reference documents once ' raise ValidationError('You can only reference documents once '
'they have been saved to the database') 'they have been saved to the database')
if not isinstance(_id, pymongo.objectid.ObjectId): if not isinstance(id_, pymongo.objectid.ObjectId):
_id = pymongo.objectid.ObjectId(_id) id_ = pymongo.objectid.ObjectId(id_)
collection = self.document_type._meta['collection'] collection = self.document_type._meta['collection']
return pymongo.dbref.DBRef(collection, _id) return pymongo.dbref.DBRef(collection, id_)
def validate(self, value): def validate(self, value):
assert(isinstance(value, (self.document_type, pymongo.dbref.DBRef))) assert(isinstance(value, (self.document_type, pymongo.dbref.DBRef)))

View File

@ -5,15 +5,19 @@ import pymongo
class QuerySet(object): class QuerySet(object):
"""A set of results returned from a query. Wraps a MongoDB cursor, """A set of results returned from a query. Wraps a MongoDB cursor,
providing Document objects as the results. providing :class:`~mongoengine.Document` objects as the results.
""" """
def __init__(self, document, collection): def __init__(self, document, collection):
self._document = document self._document = document
self._collection = collection self._collection = collection
self._query = {} self._query = {}
# If inheritance is allowed, only return instances and instances of
# subclasses of the class being used
if document._meta.get('allow_inheritance'):
self._query = {'_types': self._document._class_name}
self._cursor_obj = None self._cursor_obj = None
self._ordering = []
def ensure_index(self, key_or_list, direction=None): def ensure_index(self, key_or_list, direction=None):
"""Ensure that the given indexes are in place. """Ensure that the given indexes are in place.
@ -29,7 +33,8 @@ class QuerySet(object):
return self return self
def __call__(self, **query): def __call__(self, **query):
"""Filter the selected documents by calling the queryset with a query. """Filter the selected documents by calling the
:class:`~mongoengine.QuerySet` with a query.
""" """
self._query.update(QuerySet._transform_query(**query)) self._query.update(QuerySet._transform_query(**query))
return self return self
@ -73,7 +78,7 @@ class QuerySet(object):
return result return result
def with_id(self, object_id): def with_id(self, object_id):
"""Retrieve the object matching the _id provided. """Retrieve the object matching the id provided.
""" """
if not isinstance(object_id, pymongo.objectid.ObjectId): if not isinstance(object_id, pymongo.objectid.ObjectId):
object_id = pymongo.objectid.ObjectId(object_id) object_id = pymongo.objectid.ObjectId(object_id)
@ -84,7 +89,7 @@ class QuerySet(object):
return result return result
def next(self): def next(self):
"""Wrap the result in a Document object. """Wrap the result in a :class:`~mongoengine.Document` object.
""" """
return self._document._from_son(self._cursor.next()) return self._document._from_son(self._cursor.next())
@ -94,41 +99,54 @@ class QuerySet(object):
return self._cursor.count() return self._cursor.count()
def limit(self, n): def limit(self, n):
"""Limit the number of returned documents to. """Limit the number of returned documents to `n`. This may also be
achieved using array-slicing syntax (e.g. ``User.objects[:5]``).
""" """
self._cursor.limit(n) self._cursor.limit(n)
# Return self to allow chaining # Return self to allow chaining
return self return self
def skip(self, n): def skip(self, n):
"""Skip n documents before returning the results. """Skip `n` documents before returning the results. This may also be
achieved using array-slicing syntax (e.g. ``User.objects[5:]``).
""" """
self._cursor.skip(n) self._cursor.skip(n)
return self return self
def order_by(self, *params):
"""Apply ordering conditions, Django-style.
e.g., ``Model.objects.().order_by("-published_date", "ordering")``
will order first by ``published_date DESC``, and then ``ordering ASC``.
"""
if not params:
self._ordering = []
for param in params:
if param.startswith("-"):
param = param[1:]
sort_dir = pymongo.DESCENDING
else:
sort_dir = pymongo.ASCENDING
sort_rule = (param, sort_dir)
if not sort_rule in self._ordering: def __getitem__(self, key):
self._ordering.append(sort_rule) """Support skip and limit using getitem and slicing syntax.
self._cursor.sort(self._ordering) """
# Slice provided
if isinstance(key, slice):
self._cursor_obj = self._cursor[key]
# Allow further QuerySet modifications to be performed
return self
# Integer index provided
elif isinstance(key, int):
return self._document._from_son(self._cursor[key])
def order_by(self, *keys):
"""Order the :class:`~mongoengine.queryset.QuerySet` by the keys. The
order may be specified by prepending each of the keys by a + or a -.
Ascending order is assumed.
"""
key_list = []
for key in keys:
direction = pymongo.ASCENDING
if key[0] == '-':
direction = pymongo.DESCENDING
if key[0] in ('-', '+'):
key = key[1:]
key_list.append((key, direction))
self._cursor.sort(key_list)
return self return self
def explain(self, format=False): def explain(self, format=False):
"""Return an explain plan record for the
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
"""
plan = self._cursor.explain() plan = self._cursor.explain()
if format: if format:
import pprint import pprint

View File

@ -41,7 +41,7 @@ class DocumentTest(unittest.TestCase):
self.assertEqual(Person._fields['name'], name_field) self.assertEqual(Person._fields['name'], name_field)
self.assertEqual(Person._fields['age'], age_field) self.assertEqual(Person._fields['age'], age_field)
self.assertFalse('non_field' in Person._fields) self.assertFalse('non_field' in Person._fields)
self.assertTrue('_id' in Person._fields) self.assertTrue('id' in Person._fields)
# Test iteration over fields # Test iteration over fields
fields = list(Person()) fields = list(Person())
self.assertTrue('name' in fields and 'age' in fields) self.assertTrue('name' in fields and 'age' in fields)
@ -126,6 +126,36 @@ class DocumentTest(unittest.TestCase):
self.assertEqual(Employee._meta['collection'], self.assertEqual(Employee._meta['collection'],
self.Person._meta['collection']) self.Person._meta['collection'])
def test_allow_inheritance(self):
"""Ensure that inheritance may be disabled on simple classes and that
_cls and _types will not be used.
"""
class Animal(Document):
meta = {'allow_inheritance': False}
name = StringField()
Animal.drop_collection()
def create_dog_class():
class Dog(Animal):
pass
self.assertRaises(ValueError, create_dog_class)
# Check that _cls etc aren't present on simple documents
dog = Animal(name='dog')
dog.save()
collection = self.db[Animal._meta['collection']]
obj = collection.find_one()
self.assertFalse('_cls' in obj)
self.assertFalse('_types' in obj)
Animal.drop_collection()
def create_employee_class():
class Employee(self.Person):
meta = {'allow_inheritance': False}
self.assertRaises(ValueError, create_employee_class)
def test_creation(self): def test_creation(self):
"""Ensure that document may be created using keyword arguments. """Ensure that document may be created using keyword arguments.
""" """
@ -145,7 +175,7 @@ class DocumentTest(unittest.TestCase):
person['name'] = 'Another User' person['name'] = 'Another User'
self.assertEquals(person['name'], 'Another User') self.assertEquals(person['name'], 'Another User')
# Length = length(assigned fields + _id) # Length = length(assigned fields + id)
self.assertEquals(len(person), 3) self.assertEquals(len(person), 3)
self.assertTrue('age' in person) self.assertTrue('age' in person)
@ -160,7 +190,7 @@ class DocumentTest(unittest.TestCase):
content = StringField() content = StringField()
self.assertTrue('content' in Comment._fields) self.assertTrue('content' in Comment._fields)
self.assertFalse('_id' in Comment._fields) self.assertFalse('id' in Comment._fields)
self.assertFalse(hasattr(Comment, '_meta')) self.assertFalse(hasattr(Comment, '_meta'))
def test_save(self): def test_save(self):
@ -174,14 +204,23 @@ class DocumentTest(unittest.TestCase):
person_obj = collection.find_one({'name': 'Test User'}) person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(person_obj['name'], 'Test User') self.assertEqual(person_obj['name'], 'Test User')
self.assertEqual(person_obj['age'], 30) self.assertEqual(person_obj['age'], 30)
self.assertEqual(person_obj['_id'], person._id) self.assertEqual(person_obj['_id'], person.id)
def test_delete(self):
"""Ensure that document may be deleted using the delete method.
"""
person = self.Person(name="Test User", age=30)
person.save()
self.assertEqual(self.Person.objects.count(), 1)
person.delete()
self.assertEqual(self.Person.objects.count(), 0)
def test_save_custom_id(self): def test_save_custom_id(self):
"""Ensure that a document may be saved with a custom _id. """Ensure that a document may be saved with a custom _id.
""" """
# Create person object and save it to the database # Create person object and save it to the database
person = self.Person(name='Test User', age=30, person = self.Person(name='Test User', age=30,
_id='497ce96f395f2f052a494fd4') id='497ce96f395f2f052a494fd4')
person.save() person.save()
# Ensure that the object is in the database with the correct _id # Ensure that the object is in the database with the correct _id
collection = self.db[self.Person._meta['collection']] collection = self.db[self.Person._meta['collection']]
@ -268,7 +307,7 @@ class DocumentTest(unittest.TestCase):
post_obj.author.age = 25 post_obj.author.age = 25
post_obj.author.save() post_obj.author.save()
author = self.Person.objects(name='Test User').first() author = list(self.Person.objects(name='Test User'))[-1]
self.assertEqual(author.age, 25) self.assertEqual(author.age, 25)
BlogPost.drop_collection() BlogPost.drop_collection()

View File

@ -46,10 +46,10 @@ class FieldTest(unittest.TestCase):
name = StringField() name = StringField()
person = Person(name='Test User') person = Person(name='Test User')
self.assertRaises(AttributeError, getattr, person, '_id') self.assertRaises(AttributeError, getattr, person, 'id')
self.assertRaises(ValidationError, person.__setattr__, '_id', 47) self.assertRaises(ValidationError, person.__setattr__, 'id', 47)
self.assertRaises(ValidationError, person.__setattr__, '_id', 'abc') self.assertRaises(ValidationError, person.__setattr__, 'id', 'abc')
person._id = '497ce96f395f2f052a494fd4' person.id = '497ce96f395f2f052a494fd4'
def test_string_validation(self): def test_string_validation(self):
"""Ensure that invalid values cannot be assigned to string fields. """Ensure that invalid values cannot be assigned to string fields.

View File

@ -53,7 +53,7 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(people.count(), 2) self.assertEqual(people.count(), 2)
results = list(people) results = list(people)
self.assertTrue(isinstance(results[0], self.Person)) self.assertTrue(isinstance(results[0], self.Person))
self.assertTrue(isinstance(results[0]._id, (pymongo.objectid.ObjectId, self.assertTrue(isinstance(results[0].id, (pymongo.objectid.ObjectId,
str, unicode))) str, unicode)))
self.assertEqual(results[0].name, "User A") self.assertEqual(results[0].name, "User A")
self.assertEqual(results[0].age, 20) self.assertEqual(results[0].age, 20)
@ -77,6 +77,26 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(len(people), 1) self.assertEqual(len(people), 1)
self.assertEqual(people[0].name, 'User B') self.assertEqual(people[0].name, 'User B')
person3 = self.Person(name="User C", age=40)
person3.save()
# Test slice limit
people = list(self.Person.objects[:2])
self.assertEqual(len(people), 2)
self.assertEqual(people[0].name, 'User A')
self.assertEqual(people[1].name, 'User B')
# Test slice skip
people = list(self.Person.objects[1:])
self.assertEqual(len(people), 2)
self.assertEqual(people[0].name, 'User B')
self.assertEqual(people[1].name, 'User C')
# Test slice limit and skip
people = list(self.Person.objects[1:2])
self.assertEqual(len(people), 1)
self.assertEqual(people[0].name, 'User B')
def test_find_one(self): def test_find_one(self):
"""Ensure that a query using find_one returns a valid result. """Ensure that a query using find_one returns a valid result.
""" """
@ -97,9 +117,18 @@ class QuerySetTest(unittest.TestCase):
person = self.Person.objects(age__lt=30).first() person = self.Person.objects(age__lt=30).first()
self.assertEqual(person.name, "User A") self.assertEqual(person.name, "User A")
# Use array syntax
person = self.Person.objects[0]
self.assertEqual(person.name, "User A")
person = self.Person.objects[1]
self.assertEqual(person.name, "User B")
self.assertRaises(IndexError, self.Person.objects.__getitem__, 2)
# Find a document using just the object id # Find a document using just the object id
person = self.Person.objects.with_id(person1._id) person = self.Person.objects.with_id(person1.id)
self.assertEqual(person.name, "User A") self.assertEqual(person.name, "User A")
def test_find_embedded(self): def test_find_embedded(self):
@ -137,6 +166,25 @@ class QuerySetTest(unittest.TestCase):
self.Person.objects.delete() self.Person.objects.delete()
self.assertEqual(self.Person.objects.count(), 0) self.assertEqual(self.Person.objects.count(), 0)
def test_order_by(self):
"""Ensure that QuerySets may be ordered.
"""
self.Person(name="User A", age=20).save()
self.Person(name="User B", age=40).save()
self.Person(name="User C", age=30).save()
names = [p.name for p in self.Person.objects.order_by('-age')]
self.assertEqual(names, ['User B', 'User C', 'User A'])
names = [p.name for p in self.Person.objects.order_by('+age')]
self.assertEqual(names, ['User A', 'User C', 'User B'])
names = [p.name for p in self.Person.objects.order_by('age')]
self.assertEqual(names, ['User A', 'User C', 'User B'])
ages = [p.age for p in self.Person.objects.order_by('-name')]
self.assertEqual(ages, [30, 40, 20])
def tearDown(self): def tearDown(self):
self.Person.drop_collection() self.Person.drop_collection()