fixed merge conflict in BaseField.__init__
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user