def from_query_result(cls, model, doc, nested_doc=False): parent_key = None if nested_doc: doc_dict = doc elif doc: parent_key = utils.get_parent_doc(doc.reference.path) if doc.to_dict(): doc_dict = doc.to_dict() else: return None else: return None # instance values is changed according to firestore # so mark it modified this will help later for figuring # out the updated fields when need to update this document setattr(model, '_instance_modified', True) for k, v in doc_dict.items(): field = model._meta.get_field_by_column_name(k) # if missing field setting is set to "ignore" then # get_field_by_column_name return None So, just skip this field if field is None: continue # Check if it is Reference field if isinstance(field, ReferenceField): val = ReferenceFieldWrapper.from_doc_ref( model, field, field.field_value(v)) elif isinstance(field, NestedModel): nested_doc_val = field.field_value(v) if nested_doc_val: val = NestedModelWrapper.from_model_dict( field, nested_doc_val) else: val = None else: val = field.field_value(v) setattr(model, field.name, val) # If parent key is None but here is parent key from doc then set the parent for this model # This is case when you filter the documents parent key not auto set just set it if not model.parent and parent_key is not None: model.parent = parent_key # If it is not nested model then set the id for this model if not nested_doc: # When getting document attach the IDField if there is no user specify # it will prevent to generate new id everytime when document save # For more information see issue #45 https://github.com/octabytes/FireO/issues/45 if model._meta.id is None: model._meta.id = ('id', IDField()) setattr(model, '_id', doc.id) # save the firestore reference doc so that further actions can be performed (i.e. collections()) model._meta.set_reference_doc(doc.reference) # even though doc.reference currently points to self, there is no guarantee this will be true # in the future, therefore we should store the create time and update time separate. model._meta._firestore_create_time = doc.create_time model._meta._firestore_update_time = doc.update_time return model
def test_simple_create_get_direct_child_model(): p = DirectParentModel.collection.create(name="Direct_Parent_Model") c = DirectChildModel.collection.create(parent=p.key, age=26) assert utils.get_parent_doc(c.key) == p.key child = DirectChildModel.collection.get(c.key) assert child.age == 26
def test_parent_create_id(): p = AbParent1.collection.create(id='test_parent_create_id', name='test_parent') c = AbChild1.collection.create(parent=p.key, id='child_create_id', address="child_address") assert utils.get_parent_doc(c.key) == p.key assert c.id == 'child_create_id' assert p.id == 'test_parent_create_id'
def test_parent_create_with_obj(): p = AbParent() p.name = 'test_parent' p.save() c = AbChild(parent=p.key) c.address = 'child_address' c.save() assert utils.get_parent_doc(c.key) == p.key
def test_parent_create_diff_name(): p = AbParent2.collection.create(p_id='test_parent_create_id', name='test_parent') c = AbChild2.collection.create(parent=p.key, c_id='child_create_id', address="child_address") assert utils.get_parent_doc(c.key) == p.key assert c.c_id == 'child_create_id' assert p.p_id == 'test_parent_create_id' assert c.id is None assert p.id is None
def test_parent_cursor_fetch(): # Sample data p = CursorParentFetch.collection.create(name='Some Name') parent_key = p.key for n in range(10): ch = CursorChildFetch(parent=parent_key) ch.age = n ch.save() childs = CursorChildFetch.collection.order('created_on').fetch(3) for c in childs: assert utils.get_parent_doc(c.key) == parent_key assert c.age in [1, 2, 3] c = childs.cursor childs = CursorChildFetch.collection.cursor(c).fetch(3) for c in childs: assert utils.get_parent_doc(c.key) == parent_key assert c.age in [4, 5, 6]
def test_parent_create_with_id(): p = AbParent1() p.id = 'test_parent_create_id' p.name = 'test_parent' p.save() c = AbChild1(parent=p.key) c.id = 'child_create_id' c.address = 'child_address' c.save() assert utils.get_parent_doc(c.key) == p.key assert c.id == 'child_create_id' assert p.id == 'test_parent_create_id'
def test_parent_filter_update(): p = FilterUpdateParent.collection.create(name="Some Name") parent_key = p.key c = FilterUpdateChild(parent=parent_key) c.name = 'Azeem' c.age = 26 c.save() c = FilterUpdateChild.collection.parent(parent_key).filter( 'name', '==', 'Azeem').get() assert utils.get_parent_doc(c.key) == parent_key print(c.key) c.age = 27 c.update() child_key = c.key c = FilterUpdateChild.collection.get(child_key) assert c.key == child_key assert utils.get_parent_doc(c.key) == parent_key assert c.name == 'Azeem' assert c.age == 27
def __init__(self, model_cls, key): super().__init__(model_cls) super().set_collection_path(key=key) self.model = model_cls() # set parent to this model if any self.model.parent = utils.get_parent_doc(key) # Attach key to this model for updating this model # Purpose of attaching this key is user can update # this model after getting it # # For example: # u = User.collection.get(user_key) # u.name = "Updated Name" # u.update() self.model.update_doc = key self.id = utils.get_id(key)
def test_parent_create_with_obj_diff_name(): p = AbParent2() p.p_id = 'test_parent_create_id' p.name = 'test_parent' p.save() c = AbChild2(parent=p.key) c.c_id = 'child_create_id' c.address = 'child_address' c.save() assert utils.get_parent_doc(c.key) == p.key assert c.c_id == 'child_create_id' assert p.p_id == 'test_parent_create_id' assert c.id is None assert p.id is None
def from_query_result(cls, model, doc, nested_doc=False): parent_key = None if nested_doc: doc_dict = doc elif doc: parent_key = utils.get_parent_doc(doc.reference.path) if doc.to_dict(): doc_dict = doc.to_dict() else: return None else: return None # instance values is changed according to firestore # so mark it modified this will help later for figuring # out the updated fields when need to update this document setattr(model, '_instance_modified', True) for k, v in doc_dict.items(): field = model._meta.get_field_by_column_name(k) # if missing field setting is set to "ignore" then # get_field_by_column_name return None So, just skip this field if field is None: continue # Check if it is Reference field if isinstance(field, ReferenceField): val = ReferenceFieldWrapper.from_doc_ref( model, field, field.field_value(v)) elif isinstance(field, NestedModel): nested_doc_val = field.field_value(v) if nested_doc_val: val = NestedModelWrapper.from_model_dict( field, nested_doc_val) else: val = None else: val = field.field_value(v) setattr(model, field.name, val) # If parent key is None but here is parent key from doc then set the parent for this model # This is case when you filter the documents parent key not auto set just set it if not model.parent and parent_key is not None: model.parent = parent_key # If it is not nested model then set the id for this model if not nested_doc: setattr(model, '_id', doc.id) return model
def update(self, key=None, transaction=None, batch=None): """Update the existing document Update document without overriding it. You can update selected fields. Examples -------- .. code-block:: python class User(Model): name = TextField() age = NumberField() u = User.collection.create(name="Azeem", age=25) id = u.id # update this user = User.collection.get(id) user.name = "Arfan" user.update() print(user.name) # Arfan print(user.age) # 25 Parameters ---------- key: str Key of document which is going to update this is optional you can also set the update_doc explicitly transaction: Firestore transaction batch: Firestore batch writes """ # Check doc key is given or not if key: self._update_doc = key # make sure update doc in not None if self._update_doc is not None and '@temp_doc_id' not in self._update_doc: # set parent doc from this updated document key self.parent = utils.get_parent_doc(self._update_doc) # Get id from key and set it for model setattr(self, '_id', utils.get_id(self._update_doc)) # Add the temp id field if user is not specified any if self._id is None and self.id: setattr(self._meta, 'id', ('id', fields.IDField())) elif self._update_doc is None and '@temp_doc_id' in self.key: raise InvalidKey( f'Invalid key to update model "{self.__class__.__name__}" ') # Get the updated fields updated_fields = {} for k, v in self._get_fields().items(): if k in self._field_changed: updated_fields[k] = v # Get nested fields if any # Nested model store as dict in firestore so check values type is dict if type(v) is dict: # nested field name and value for name, value in v.items(): if name in self._field_changed: # create the name with parent field name and child name # For example: # class User(Model): # address = TextField() # class Student(Model): # age = NumberField() # user = NestedModel(User) # # Then the field name for nested model will be "user.address" updated_fields[k + "." + name] = value # pass the model instance if want change in it after save, fetch etc operations # otherwise it will return new model instance return self.__class__.collection._update(self, transaction=transaction, batch=batch, **updated_fields)
def test_parent_create_get(): p = AbParent.collection.create(name='test_parent') c = AbChild.collection.create(parent=p.key, address="child_address") assert utils.get_parent_doc(c.key) == p.key