Protecting Dict and List Fields between class instances
See original GitHub issueGenerally python allows sharing of class attributes between instances of that class. But I don’t think this this is the right behaviour for Umongo.
In this example the behaviour is expected, both instances of RegularClass share the sharable_field
property.
class RegularClass():
primitive_field = None
sharable_field = []
@classmethod
def create(cls, update):
new_object = cls()
new_object.sharable_field.append(update)
new_object.primitive_field = update
return new_object
a = RegularClass.create(update=1)
b = RegularClass.create(update=2)
print(a.primitive_field, a.sharable_field)
# 1 [1, 2]
print(b.primitive_field, b.sharable_field)
# 2 [1, 2]
But even though Umongo documents give the same output (when list fields have a default value), it’s not what I expect.
import motor
from umongo import Instance
from umongo.document import Document
from umongo.fields import ListField, IntField
db = motor.motor_asyncio.AsyncIOMotorClient()
instance = Instance(db.data)
@instance.register
class UmongoClass(Document):
primitive_field = None
sharable_field = ListField(IntField(), default=[])
@classmethod
def create(cls, update):
new_object = cls()
new_object.sharable_field.append(update)
new_object.primitive_field = update
return new_object
a = UmongoClass.create(update=1)
b = UmongoClass.create(update=2)
print(a.primitive_field, a.sharable_field)
# 1 [1, 2]
print(b.primitive_field, b.sharable_field)
# 2 [1, 2]
It’s not what I expect because when I use SQLAlchemy or MongoEngine they do something different.
import datetime
from mongoengine import *
connect('mydb')
class BlogPost(Document):
sharable_field = ListField(IntField(max_length=50))
@classmethod
def create(cls, update):
new_object = cls()
new_object.sharable_field.append(update)
return new_object
a = BlogPost.create(update=1)
b = BlogPost.create(update=2)
print(a.sharable_field)
# [1]
print(b.sharable_field)
# [2]
We’ve patched this for ourselves by automatically reinitializing dict and list fields. But this seems like it should be changed upstream. What do you think, could the constructor for DocumentTemplates initialize a different object for each instance? Here is our patch in the class that all of our documents implement.
@classmethod
def create(cls,**kwargs):
new_object = cls()
for key, value in cls.schema.fields.items():
if type(value) == ListField:
new_object[key] = []
if type(value) == DictField:
new_object[key] = {}
Issue Analytics
- State:
- Created 4 years ago
- Reactions:2
- Comments:7
Top GitHub Comments
I uploaded the wrong version, here is the equivalent code in MongoEngine. If it would be helpful I’m happy to make a minimum reproducible build for SQLAlchemy (or another library) as well.
Okay, for our own development we need to protect against interns making this mistake so we will continue using either the patch I referenced above or something more explicit. I think we’ll be okay if umongo/marshmallow doesn’t change.
Thanks.
No, don’t bother.
Whatever patch you come up with, please share it here in case someone wants to do the same.