1

How is the distinction between class attribute and instance attribute made for classes extending mongoengine Document? All the attributes look like class attributes, but when creating two instances, those variables are not shared. How can both, class attributes and instance attributes be created using mongoengine?

------ edit ------

class User(Document):
    email = StringField(required=True)
    first_name = StringField(max_length=50)
    last_name = StringField(max_length=50)

This is the example mongoengine documentation gives for defining User document. The way these are defined, they are class level attributes. However, Document.py and BaseDocument.py take care that these are not shared among instances of User. I want to have class level attributes which are shared between instances and are also saved to mongodb when save() method is called on the document

tunetopj
  • 561
  • 2
  • 5
  • 15

1 Answers1

1

If you create two instances of User and save them then you will have two documents in the collection. If you want them both to have common information then there are few ways to achieve this:

1. default attributes

class User(db.Document):
    email = db.StringField(required=True)
    first_name = db.StringField(max_length=50)
    last_name = db.StringField(max_length=50)
    usertype = db.StringField(default="web user")


u1 = User(email="u1@example.com", first_name="user", last_name="one").save()
u2 = User(email="u2@example.com", first_name="user", last_name="two").save()

assert u1.usertype == "web user"
assert u2.usertype == "web user"

2. embedded documents

class UserType(db.EmbeddedDocument):
    role = db.StringField(choices=['Admin','Basic','Guest'])

class User(db.Document):
    email = db.StringField(required=True)
    first_name = db.StringField(max_length=50)
    last_name = db.StringField(max_length=50)
    usertype = db.EmbeddedDocumentField(UserType)


basic = UserType(role="Basic")
u1 = User(email="u1@example.com", first_name="user", last_name="one", usertype=basic).save()
u2 = User(email="u2@example.com", first_name="user", last_name="two", usertype=basic).save()

assert u1.usertype.role == "Basic"
assert u2.usertype.role == "Basic"

3. referenced documents

class UserType(db.Document):
    role = db.StringField(choices=['Admin','Basic','Guest'])

class User(db.Document):
    email = db.StringField(required=True)
    first_name = db.StringField(max_length=50)
    last_name = db.StringField(max_length=50)
    usertype = db.ReferenceField(UserType)


basic = UserType(role="Basic").save()
u1 = User(email="u1@example.com", first_name="user", last_name="one", usertype=basic).save()
u2 = User(email="u2@example.com", first_name="user", last_name="two", usertype=basic).save()

assert u1.usertype.role == "Basic"
assert u2.usertype.role == "Basic"

When to use an EmbeddedDocument versus a ReferenceField depends on your data model design.

Community
  • 1
  • 1
Steve Rossiter
  • 2,624
  • 21
  • 29
  • Either of the 3 things don't solve my purpose. The use of class attributes is to share an attribute across instances of that class. But I suppose it does not make sense to have a shared variable in an entity that gets persisted. – tunetopj Feb 04 '16 at 19:15
  • The purpose of mongoengine `Document` classes is to map the documents in a collection to python objects. If you require some other variable that does't fit your data model to be referenced then you should just use plain python Classes to do that. – Steve Rossiter Feb 04 '16 at 19:18
  • Can I ask a related question; why mongoengine chose to have Documents defined at class level, rather then having the fields define in __init__()? That would keep it intuitive for regular python developers and also make shifting away from(or shifting to) mongoengine more cleaner and easier. – tunetopj Feb 04 '16 at 19:19
  • 1
    This is so that the schema can be defined and enforced on creation of Document instances. It also allows fields to be created but not added for every document. – Steve Rossiter Feb 04 '16 at 19:27