question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Protecting Dict and List Fields between class instances

See original GitHub issue

Generally 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:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:7

github_iconTop GitHub Comments

1reaction
molten-firescar96commented, Mar 30, 2020

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.

import datetime
from mongoengine import *
connect('mydb')

class BlogPost(Document):
    sharable_field = ListField(IntField(max_length=50), default=[])
    
    @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]

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.

0reactions
lafrechcommented, Mar 30, 2020

Thanks.

If it would be helpful I’m happy to make a minimum reproducible build for SQLAlchemy (or another library) as well.

No, don’t bother.

Whatever patch you come up with, please share it here in case someone wants to do the same.

Read more comments on GitHub >

github_iconTop Results From Across the Web

python - Creating class instance properties from a dictionary?
Any easy fix if the keys have spaces in them? Example: instance.Something Else won't work because of the space. I'm wondering if there's...
Read more >
The problem with inheriting from dict and list in Python
I'm writing this article to explain why I often don't recommend inheriting from these built-in classes in Python. My examples will focus on...
Read more >
Understanding internals of Python classes - Artem Golubin
In Python, all instance variables are stored as a regular dictionary. When working with attributes, you just changing a dictionary. We can ...
Read more >
Custom Python Dictionaries: Inheriting From dict vs UserDict
In this tutorial, you'll learn how to create custom dictionary-like classes in Python by inheriting from the built-in dict class or by ...
Read more >
Model field reference - Django documentation
One exception is when a CharField has both unique=True and blank=True set. In this situation, null=True is required to avoid unique constraint violations...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found