fixed merge conflict in BaseField.__init__

This commit is contained in:
blackbrrr
2010-01-12 12:19:30 -06:00
12 changed files with 497 additions and 34 deletions

View File

@@ -1,9 +1,14 @@
from connection import _get_db
import pymongo
import copy
__all__ = ['queryset_manager', 'InvalidQueryError', 'InvalidCollectionError']
__all__ = ['queryset_manager', 'Q', 'InvalidQueryError',
'InvalidCollectionError']
# The maximum number of items to display in a QuerySet.__repr__
REPR_OUTPUT_SIZE = 20
class InvalidQueryError(Exception):
@@ -14,6 +19,88 @@ class OperationError(Exception):
pass
class Q(object):
OR = '||'
AND = '&&'
OPERATORS = {
'eq': 'this.%(field)s == %(value)s',
'neq': 'this.%(field)s != %(value)s',
'gt': 'this.%(field)s > %(value)s',
'gte': 'this.%(field)s >= %(value)s',
'lt': 'this.%(field)s < %(value)s',
'lte': 'this.%(field)s <= %(value)s',
'lte': 'this.%(field)s <= %(value)s',
'in': 'this.%(field)s.indexOf(%(value)s) != -1',
'nin': 'this.%(field)s.indexOf(%(value)s) == -1',
'mod': '%(field)s %% %(value)s',
'all': ('%(value)s.every(function(a){'
'return this.%(field)s.indexOf(a) != -1 })'),
'size': 'this.%(field)s.length == %(value)s',
'exists': 'this.%(field)s != null',
}
def __init__(self, **query):
self.query = [query]
def _combine(self, other, op):
obj = Q()
obj.query = ['('] + copy.deepcopy(self.query) + [op]
obj.query += copy.deepcopy(other.query) + [')']
return obj
def __or__(self, other):
return self._combine(other, self.OR)
def __and__(self, other):
return self._combine(other, self.AND)
def as_js(self, document):
js = []
js_scope = {}
for i, item in enumerate(self.query):
if isinstance(item, dict):
item_query = QuerySet._transform_query(document, **item)
# item_query will values will either be a value or a dict
js.append(self._item_query_as_js(item_query, js_scope, i))
else:
js.append(item)
return pymongo.code.Code(' '.join(js), js_scope)
def _item_query_as_js(self, item_query, js_scope, item_num):
# item_query will be in one of the following forms
# {'age': 25, 'name': 'Test'}
# {'age': {'$lt': 25}, 'name': {'$in': ['Test', 'Example']}
# {'age': {'$lt': 25, '$gt': 18}}
js = []
for i, (key, value) in enumerate(item_query.items()):
op = 'eq'
# Construct a variable name for the value in the JS
value_name = 'i%sf%s' % (item_num, i)
if isinstance(value, dict):
# Multiple operators for this field
for j, (op, value) in enumerate(value.items()):
# Create a custom variable name for this operator
op_value_name = '%so%s' % (value_name, j)
# Update the js scope with the value for this op
js_scope[op_value_name] = value
# Construct the JS that uses this op
operation_js = Q.OPERATORS[op.strip('$')] % {
'field': key,
'value': op_value_name
}
js.append(operation_js)
else:
js_scope[value_name] = value
# Construct the JS for this field
field_js = Q.OPERATORS[op.strip('$')] % {
'field': key,
'value': value_name
}
js.append(field_js)
return ' && '.join(js)
class QuerySet(object):
"""A set of results returned from a query. Wraps a MongoDB cursor,
providing :class:`~mongoengine.Document` objects as the results.
@@ -24,6 +111,7 @@ class QuerySet(object):
self._collection_obj = collection
self._accessed_collection = False
self._query = {}
self._where_clauses = []
# If inheritance is allowed, only return instances and instances of
# subclasses of the class being used
@@ -33,6 +121,10 @@ class QuerySet(object):
def ensure_index(self, key_or_list):
"""Ensure that the given indexes are in place.
:param key_or_list: a single index key or a list of index keys (to
construct a multi-field index); keys may be prefixed with a **+**
or a **-** to determine the index ordering
"""
if isinstance(key_or_list, basestring):
key_or_list = [key_or_list]
@@ -55,10 +147,15 @@ class QuerySet(object):
self._collection.ensure_index(index_list)
return self
def __call__(self, **query):
def __call__(self, *q_objs, **query):
"""Filter the selected documents by calling the
:class:`~mongoengine.QuerySet` with a query.
:param q_objs: :class:`~mongoengine.Q` objects to be used in the query
:param query: Django-style query keyword arguments
"""
for q in q_objs:
self._where_clauses.append(q.as_js(self._document))
query = QuerySet._transform_query(_doc_cls=self._document, **query)
self._query.update(query)
return self
@@ -92,6 +189,9 @@ class QuerySet(object):
def _cursor(self):
if not self._cursor_obj:
self._cursor_obj = self._collection.find(self._query)
# Apply where clauses to cursor
for js in self._where_clauses:
self._cursor_obj.where(js)
# apply default ordering
if self._document._meta['ordering']:
@@ -179,11 +279,13 @@ class QuerySet(object):
def with_id(self, object_id):
"""Retrieve the object matching the id provided.
"""
if not isinstance(object_id, pymongo.objectid.ObjectId):
object_id = pymongo.objectid.ObjectId(str(object_id))
result = self._collection.find_one(object_id)
:param object_id: the value for the id of the document to look up
"""
id_field = self._document._meta['id_field']
object_id = self._document._fields[id_field].to_mongo(object_id)
result = self._collection.find_one({'_id': object_id})
if result is not None:
result = self._document._from_son(result)
return result
@@ -204,6 +306,8 @@ class QuerySet(object):
def limit(self, n):
"""Limit the number of returned documents to `n`. This may also be
achieved using array-slicing syntax (e.g. ``User.objects[:5]``).
:param n: the maximum number of objects to return
"""
self._cursor.limit(n)
# Return self to allow chaining
@@ -212,6 +316,8 @@ class QuerySet(object):
def skip(self, n):
"""Skip `n` documents before returning the results. This may also be
achieved using array-slicing syntax (e.g. ``User.objects[5:]``).
:param n: the number of objects to skip before returning results
"""
self._cursor.skip(n)
return self
@@ -232,6 +338,9 @@ class QuerySet(object):
"""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.
:param keys: fields to order the query results by; keys may be
prefixed with **+** or **-** to determine the ordering direction
"""
key_list = []
for key in keys:
@@ -248,6 +357,8 @@ class QuerySet(object):
def explain(self, format=False):
"""Return an explain plan record for the
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
:param format: format the plan before returning it
"""
plan = self._cursor.explain()
@@ -256,10 +367,12 @@ class QuerySet(object):
plan = pprint.pformat(plan)
return plan
def delete(self):
def delete(self, safe=False):
"""Delete the documents matched by the query.
:param safe: check if the operation succeeded before returning
"""
self._collection.remove(self._query)
self._collection.remove(self._query, safe=safe)
@classmethod
def _transform_update(cls, _doc_cls=None, **update):
@@ -312,6 +425,11 @@ class QuerySet(object):
def update(self, safe_update=True, **update):
"""Perform an atomic update on the fields matched by the query.
:param safe: check if the operation succeeded before returning
:param update: Django-style update keyword arguments
.. versionadded:: 0.2
"""
if pymongo.version < '1.1.1':
raise OperationError('update() method requires PyMongo 1.1.1+')
@@ -327,6 +445,11 @@ class QuerySet(object):
def update_one(self, safe_update=True, **update):
"""Perform an atomic update on first field matched by the query.
:param safe: check if the operation succeeded before returning
:param update: Django-style update keyword arguments
.. versionadded:: 0.2
"""
update = QuerySet._transform_update(self._document, **update)
try:
@@ -352,6 +475,12 @@ class QuerySet(object):
collection in use; ``query``, which is an object representing the
current query; and ``options``, which is an object containing any
options specified as keyword arguments.
:param code: a string of Javascript code to execute
:param fields: fields that you will be using in your function, which
will be passed in to your function as arguments
:param options: options that you want available to the function
(accessed in Javascript through the ``options`` object)
"""
fields = [QuerySet._translate_field_name(self._document, f)
for f in fields]
@@ -368,6 +497,9 @@ class QuerySet(object):
def sum(self, field):
"""Sum over the values of the specified field.
:param field: the field to sum over; use dot-notation to refer to
embedded document fields
"""
sum_func = """
function(sumField) {
@@ -382,6 +514,9 @@ class QuerySet(object):
def average(self, field):
"""Average over the values of the specified field.
:param field: the field to average over; use dot-notation to refer to
embedded document fields
"""
average_func = """
function(averageField) {
@@ -402,6 +537,9 @@ class QuerySet(object):
"""Returns a dictionary of all items present in a list field across
the whole queried set of documents, and their corresponding frequency.
This is useful for generating tag clouds, or searching documents.
:param list_field: the list field to use
:param normalize: normalize the results so they add to 1.0
"""
freq_func = """
function(listField) {
@@ -427,6 +565,11 @@ class QuerySet(object):
"""
return self.exec_js(freq_func, list_field, normalize=normalize)
def __repr__(self):
data = list(self[:REPR_OUTPUT_SIZE + 1])
if len(data) > REPR_OUTPUT_SIZE:
data[-1] = "...(remaining elements truncated)..."
return repr(data)
class InvalidCollectionError(Exception):
pass