Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c0b00e42d | ||
|
|
3327388f1f | ||
|
|
19cbb442ee | ||
|
|
c0e7f341cb | ||
|
|
b708dabf98 | ||
|
|
899e56e5b8 | ||
|
|
f6d3bd8ccb | ||
|
|
deb5677a57 | ||
|
|
50b271c868 | ||
|
|
a57f28ac83 | ||
|
|
3f3747a2fe | ||
|
|
d133913c3d | ||
|
|
e049cef00a | ||
|
|
5a038de1d5 |
@@ -5,6 +5,7 @@ python:
|
|||||||
- '3.2'
|
- '3.2'
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '3.4'
|
- '3.4'
|
||||||
|
- '3.5'
|
||||||
- pypy
|
- pypy
|
||||||
- pypy3
|
- pypy3
|
||||||
env:
|
env:
|
||||||
|
|||||||
1
AUTHORS
1
AUTHORS
@@ -229,3 +229,4 @@ that much better:
|
|||||||
* Emile Caron (https://github.com/emilecaron)
|
* Emile Caron (https://github.com/emilecaron)
|
||||||
* Amit Lichtenberg (https://github.com/amitlicht)
|
* Amit Lichtenberg (https://github.com/amitlicht)
|
||||||
* Lars Butler (https://github.com/larsbutler)
|
* Lars Butler (https://github.com/larsbutler)
|
||||||
|
* George Macon (https://github.com/gmacon)
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
Changes in 0.10.2
|
||||||
|
=================
|
||||||
|
- Allow shard key to point to a field in an embedded document. #551
|
||||||
|
- Allow arbirary metadata in fields. #1129
|
||||||
|
|
||||||
Changes in 0.10.1
|
Changes in 0.10.1
|
||||||
=======================
|
=======================
|
||||||
- Fix infinite recursion with CASCADE delete rules under specific conditions. #1046
|
- Fix infinite recursion with CASCADE delete rules under specific conditions. #1046
|
||||||
|
|||||||
@@ -172,11 +172,11 @@ arguments can be set on all fields:
|
|||||||
class Shirt(Document):
|
class Shirt(Document):
|
||||||
size = StringField(max_length=3, choices=SIZE)
|
size = StringField(max_length=3, choices=SIZE)
|
||||||
|
|
||||||
:attr:`help_text` (Default: None)
|
:attr:`**kwargs` (Optional)
|
||||||
Optional help text to output with the field -- used by form libraries
|
You can supply additional metadata as arbitrary additional keyword
|
||||||
|
arguments. You can not override existing attributes, however. Common
|
||||||
:attr:`verbose_name` (Default: None)
|
choices include `help_text` and `verbose_name`, commonly used by form and
|
||||||
Optional human-readable name for the field -- used by form libraries
|
widget libraries.
|
||||||
|
|
||||||
|
|
||||||
List fields
|
List fields
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ class BaseField(object):
|
|||||||
|
|
||||||
def __init__(self, db_field=None, name=None, required=False, default=None,
|
def __init__(self, db_field=None, name=None, required=False, default=None,
|
||||||
unique=False, unique_with=None, primary_key=False,
|
unique=False, unique_with=None, primary_key=False,
|
||||||
validation=None, choices=None, verbose_name=None,
|
validation=None, choices=None, null=False, sparse=False,
|
||||||
help_text=None, null=False, sparse=False, custom_data=None):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
:param db_field: The database field to store this field in
|
:param db_field: The database field to store this field in
|
||||||
(defaults to the name of the field)
|
(defaults to the name of the field)
|
||||||
@@ -60,16 +60,15 @@ class BaseField(object):
|
|||||||
field. Generally this is deprecated in favour of the
|
field. Generally this is deprecated in favour of the
|
||||||
`FIELD.validate` method
|
`FIELD.validate` method
|
||||||
:param choices: (optional) The valid choices
|
:param choices: (optional) The valid choices
|
||||||
:param verbose_name: (optional) The verbose name for the field.
|
|
||||||
Designed to be human readable and is often used when generating
|
|
||||||
model forms from the document model.
|
|
||||||
:param help_text: (optional) The help text for this field and is often
|
|
||||||
used when generating model forms from the document model.
|
|
||||||
:param null: (optional) Is the field value can be null. If no and there is a default value
|
:param null: (optional) Is the field value can be null. If no and there is a default value
|
||||||
then the default value is set
|
then the default value is set
|
||||||
:param sparse: (optional) `sparse=True` combined with `unique=True` and `required=False`
|
:param sparse: (optional) `sparse=True` combined with `unique=True` and `required=False`
|
||||||
means that uniqueness won't be enforced for `None` values
|
means that uniqueness won't be enforced for `None` values
|
||||||
:param custom_data: (optional) Custom metadata for this field.
|
:param **kwargs: (optional) Arbitrary indirection-free metadata for
|
||||||
|
this field can be supplied as additional keyword arguments and
|
||||||
|
accessed as attributes of the field. Must not conflict with any
|
||||||
|
existing attributes. Common metadata includes `verbose_name` and
|
||||||
|
`help_text`.
|
||||||
"""
|
"""
|
||||||
self.db_field = (db_field or name) if not primary_key else '_id'
|
self.db_field = (db_field or name) if not primary_key else '_id'
|
||||||
|
|
||||||
@@ -83,12 +82,19 @@ class BaseField(object):
|
|||||||
self.primary_key = primary_key
|
self.primary_key = primary_key
|
||||||
self.validation = validation
|
self.validation = validation
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
self.verbose_name = verbose_name
|
|
||||||
self.help_text = help_text
|
|
||||||
self.null = null
|
self.null = null
|
||||||
self.sparse = sparse
|
self.sparse = sparse
|
||||||
self._owner_document = None
|
self._owner_document = None
|
||||||
self.custom_data = custom_data
|
|
||||||
|
# Detect and report conflicts between metadata and base properties.
|
||||||
|
conflicts = set(dir(self)) & set(kwargs)
|
||||||
|
if conflicts:
|
||||||
|
raise TypeError("%s already has attribute(s): %s" % (
|
||||||
|
self.__class__.__name__, ', '.join(conflicts) ))
|
||||||
|
|
||||||
|
# Assign metadata to the instance
|
||||||
|
# This efficient method is available because no __slots__ are defined.
|
||||||
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
# Adjust the appropriate creation counter, and save our local copy.
|
# Adjust the appropriate creation counter, and save our local copy.
|
||||||
if self.db_field == '_id':
|
if self.db_field == '_id':
|
||||||
|
|||||||
@@ -341,8 +341,12 @@ class Document(BaseDocument):
|
|||||||
select_dict['_id'] = object_id
|
select_dict['_id'] = object_id
|
||||||
shard_key = self.__class__._meta.get('shard_key', tuple())
|
shard_key = self.__class__._meta.get('shard_key', tuple())
|
||||||
for k in shard_key:
|
for k in shard_key:
|
||||||
actual_key = self._db_field_map.get(k, k)
|
path = self._lookup_field(k.split('.'))
|
||||||
select_dict[actual_key] = doc[actual_key]
|
actual_key = [p.db_field for p in path]
|
||||||
|
val = doc
|
||||||
|
for ak in actual_key:
|
||||||
|
val = val[ak]
|
||||||
|
select_dict['.'.join(actual_key)] = val
|
||||||
|
|
||||||
def is_new_object(last_error):
|
def is_new_object(last_error):
|
||||||
if last_error is not None:
|
if last_error is not None:
|
||||||
@@ -444,7 +448,12 @@ class Document(BaseDocument):
|
|||||||
select_dict = {'pk': self.pk}
|
select_dict = {'pk': self.pk}
|
||||||
shard_key = self.__class__._meta.get('shard_key', tuple())
|
shard_key = self.__class__._meta.get('shard_key', tuple())
|
||||||
for k in shard_key:
|
for k in shard_key:
|
||||||
select_dict[k] = getattr(self, k)
|
path = self._lookup_field(k.split('.'))
|
||||||
|
actual_key = [p.db_field for p in path]
|
||||||
|
val = self
|
||||||
|
for ak in actual_key:
|
||||||
|
val = getattr(val, ak)
|
||||||
|
select_dict['__'.join(actual_key)] = val
|
||||||
return select_dict
|
return select_dict
|
||||||
|
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
|
|||||||
@@ -863,12 +863,11 @@ class ReferenceField(BaseField):
|
|||||||
|
|
||||||
The options are:
|
The options are:
|
||||||
|
|
||||||
* DO_NOTHING - don't do anything (default).
|
* DO_NOTHING (0) - don't do anything (default).
|
||||||
* NULLIFY - Updates the reference to null.
|
* NULLIFY (1) - Updates the reference to null.
|
||||||
* CASCADE - Deletes the documents associated with the reference.
|
* CASCADE (2) - Deletes the documents associated with the reference.
|
||||||
* DENY - Prevent the deletion of the reference object.
|
* DENY (3) - Prevent the deletion of the reference object.
|
||||||
* PULL - Pull the reference from a :class:`~mongoengine.fields.ListField`
|
* PULL (4) - Pull the reference from a :class:`~mongoengine.fields.ListField` of references
|
||||||
of references
|
|
||||||
|
|
||||||
Alternative syntax for registering delete rules (useful when implementing
|
Alternative syntax for registering delete rules (useful when implementing
|
||||||
bi-directional delete rules)
|
bi-directional delete rules)
|
||||||
|
|||||||
@@ -484,6 +484,20 @@ class InstanceTest(unittest.TestCase):
|
|||||||
doc.reload()
|
doc.reload()
|
||||||
Animal.drop_collection()
|
Animal.drop_collection()
|
||||||
|
|
||||||
|
def test_reload_sharded_nested(self):
|
||||||
|
class SuperPhylum(EmbeddedDocument):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
class Animal(Document):
|
||||||
|
superphylum = EmbeddedDocumentField(SuperPhylum)
|
||||||
|
meta = {'shard_key': ('superphylum.name',)}
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
doc = Animal(superphylum=SuperPhylum(name='Deuterostomia'))
|
||||||
|
doc.save()
|
||||||
|
doc.reload()
|
||||||
|
Animal.drop_collection()
|
||||||
|
|
||||||
def test_reload_referencing(self):
|
def test_reload_referencing(self):
|
||||||
"""Ensures reloading updates weakrefs correctly
|
"""Ensures reloading updates weakrefs correctly
|
||||||
"""
|
"""
|
||||||
@@ -2715,6 +2729,32 @@ class InstanceTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(OperationError, change_shard_key)
|
self.assertRaises(OperationError, change_shard_key)
|
||||||
|
|
||||||
|
def test_shard_key_in_embedded_document(self):
|
||||||
|
class Foo(EmbeddedDocument):
|
||||||
|
foo = StringField()
|
||||||
|
|
||||||
|
class Bar(Document):
|
||||||
|
meta = {
|
||||||
|
'shard_key': ('foo.foo',)
|
||||||
|
}
|
||||||
|
foo = EmbeddedDocumentField(Foo)
|
||||||
|
bar = StringField()
|
||||||
|
|
||||||
|
foo_doc = Foo(foo='hello')
|
||||||
|
bar_doc = Bar(foo=foo_doc, bar='world')
|
||||||
|
bar_doc.save()
|
||||||
|
|
||||||
|
self.assertTrue(bar_doc.id is not None)
|
||||||
|
|
||||||
|
bar_doc.bar = 'baz'
|
||||||
|
bar_doc.save()
|
||||||
|
|
||||||
|
def change_shard_key():
|
||||||
|
bar_doc.foo.foo = 'something'
|
||||||
|
bar_doc.save()
|
||||||
|
|
||||||
|
self.assertRaises(OperationError, change_shard_key)
|
||||||
|
|
||||||
def test_shard_key_primary(self):
|
def test_shard_key_primary(self):
|
||||||
class LogEntry(Document):
|
class LogEntry(Document):
|
||||||
machine = StringField(primary_key=True)
|
machine = StringField(primary_key=True)
|
||||||
|
|||||||
2
tox.ini
2
tox.ini
@@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = {py26,py27,py32,py33,py34,pypy,pypy3}-{mg27,mg28}
|
envlist = {py26,py27,py32,py33,py34,py35,pypy,pypy3}-{mg27,mg28}
|
||||||
#envlist = {py26,py27,py32,py33,py34,pypy,pypy3}-{mg27,mg28,mg30,mgdev}
|
#envlist = {py26,py27,py32,py33,py34,pypy,pypy3}-{mg27,mg28,mg30,mgdev}
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
|||||||
Reference in New Issue
Block a user