def preSave(doc, usr): response = {} modelClass = getattr(models, doc['_c']) # collNam = modelClass.meta['collection'] # collNamTmp = collNam + '_tmp' # collTmp = db[collNamTmp] # coll = db[collNam] # validate errors = validate_partial(modelClass, doc) if errors: response['errors'] = errors['errors'] response['total_errors'] = errors['count'] return {'response': response, 'status': 400} # init model instance model = modelClass(**doc) # if there is a vNam class property if hasattr(model, 'vNam') and 'dNam' in model._fields: model.dNam = model.vNam if hasattr(model, 'vNamS') and 'dNamS' in model._fields: model.dNamS = model.vNamS doc = doc_remove_empty_keys(to_python(model, allow_none=True)) # logit update doc = logit(usr, doc) response['doc'] = doc return {'response': response, 'status': 200}
def initDocListTypes(doc, usr): '''When posting a doc with listType attributes/fields, they must be initialized with provided data and validated and saved back to the doc for further handling, ie, validation, etc.''' attrNams = doc.keys() # loop through all doc keys for attrNam in attrNams: attrVal = doc[attrNam] # is it a list if type(attrVal) == list: attrValList = attrVal # loop through all the elements in the list for attrValListOffset in range(0, len(attrValList)): attrValListItem = attrValList[attrValListOffset] attr_c = attrValListItem['_c'] modelClass = _cs[attr_c]['modelClass'] # init a class model for this item model = modelClass() # set model attribute values for k,v in attrValListItem.iteritems(): setattr(model, k, v) # generate a next eId resp = nextEId(doc, attrNam) doc = resp['doc'] model.eId = resp['eId'] # generate dNam if hasattr(model, 'vNam') and 'dNam' in model._fields and not model.dNam: model.dNam = model.vNam # generate dNamS if 'dNamS' in model._fields and hasattr(model, 'vNamS') and not model.dNamS: model.dNamS = model.vNamS # remove all empty fields attrValListItemClean = doc_remove_empty_keys(to_python(model, allow_none=True)) # validate errors = validate(modelClass, attrValListItemClean) # TODO: handle and test errors if errors: total_errors += errors['count'] post_errors.append(errors) continue # logit update attrValListItemClean = logit(usr, attrValListItemClean, method='post') # save cleaned listType item back to doc doc[attrNam][attrValListOffset] = attrValListItemClean # now all listType items are clean, validated, and logged return doc
def put(self, **kwargs): """Update a doc""" db = self.db # TODO: accomodate where clause to put changes to more than one doc. usrOID = self.usr['OID'] data = kwargs['data'] _c = data['_c'] modelClass = getattr(models, _c) #attrNam = doc['attrNam'] if 'attrNam' in doc_keys else None #attr_c = doc['attr_c'] if attrNam else None #attrEid = doc['attrEid'] if attrNam else None #attrVal = doc['attrVal'] if attrNam else None collNam = modelClass.meta['collection'] collNamTmp = collNam + '_tmp' collTmp = db[collNamTmp] coll = db[collNam] response = {} docs = [] status = 200 where = data['where'] patch = data['patch'] eId = data['eId'] if 'eId' in data else None if eId and len(patch.keys()) > 1: # TODO Handle error pass # if element eId was passed, expect to put/patch change to one element in a ListType attribute/field if eId and len(patch) == 1: elem = patch.popitem() attrNam = elem[0] # enhance to support putting/updating multiple list elements attrVal = elem[1][0] resp = preSave(attrVal, self.usr) if not resp['status'] == 200: return {'response': resp, 'status': 400} attrVal = resp['response']['doc'] # http://docs.mongodb.org/manual/applications/update/ # patch update in tmp collection attrEl = attrNam + '.$' doc = collTmp.find_and_modify( query = where, update = { "$set": { attrEl: attrVal }}, new = True ) response['collection'] = collNamTmp response['total_invalid'] = 0 response['id'] = id.__str__() # remove this, not needed response['doc'] = doc return {'response': response, 'status': 200} else: # validate patch # init modelClass for this doc patch_errors = validate_partial(modelClass, patch) if patch_errors: response['errors'] = patch_errors['errors'] response['total_errors'] = patch_errors['count'] status = 400 return prep_response(response, status = status) # logit update patch = logit(self.usr, patch) # patch update in tmp collection doc = collTmp.find_and_modify( query = where, update = {"$set": patch}, new = True ) # init model instance model = modelClass(**doc) # if there is a vNam class property if hasattr(model, 'vNam') and 'dNam' in model._fields: doc = collTmp.find_one(where) _id = doc['_id'] model.dNam = model.vNam if hasattr(model, 'vNamS') and 'dNamS' in model._fields: model.dNamS = model.vNamS doc = doc_remove_empty_keys(to_python(model, allow_none=True)) collTmp.update(where, doc) # gotta put the _id back doc['_id'] = _id response['collection'] = collNamTmp response['total_invalid'] = 0 response['id'] = id.__str__() # remove this, not needed response['doc'] = doc return {'response': response, 'status': status}
def post(self, **kwargs): '''Insert a doc newDocTmp: Initialize a temp (tmp) doc if no OID and no data. cloneDocTmp: Clone to a temp doc if OID and no isTmp flag set. upsertDocTmp: Update or Insert temp doc to base collection if OID and isTmp is set. insertDoc: Insert a new doc if no OID and there are more attributes than _c. ''' db = self.db response = {} docs = [] status = 200 usrOID = self.usr['OID'] docs_to_post = kwargs['docs'] post_errors = [] total_errors = 0 for doc in docs_to_post: errors = {} doc_info = {} # required attribute _c = doc['_c'] # shortcut doc_keys = doc.keys() modelClass = getattr(models, _c) _id = doc['_id'] if '_id' in doc_keys else None where = {'_id': ObjectId(_id)} if _id else None attrNam = doc['attrNam'] if 'attrNam' in doc_keys else None attr_c = doc['attr_c'] if attrNam else None attrVal = doc['attrVal'] if attrNam else None collNam = modelClass.meta['collection'] collTmp = db[collNam + '_tmp'] coll = db[collNam] # if _ id is passed, it directs that a temp doc be initialized and inserted into the appropriate Tmp (temp) collection. # if an _id IS passed, it directs that the doc passed in be validated and inserted in the base collection and the temp doc in the temp collection be deleted. # if attrNam, posting a new value to a listtype attribute/field if attrNam: for elem in attrVal: modelClass = getattr(models.embed, attr_c) model = modelClass() for k,v in elem.iteritems(): setattr(model, k, v) resp = nextEId(doc, attrNam) doc = resp['doc'] # model.eIds = doc['eIds'] model.eId = resp['eId'] if hasattr(model, 'vNam') and 'dNam' in model._fields and not model.dNam: model.dNam = model.vNam # generate a slug if slug is empty if 'slug' in model._fields and 'dNam' in model._fields and not model.slug: model.slug = slugify(model.dNam, coll) # set dNamS if used: # if dNamS is empty, set to value of slug if 'dNamS' in model._fields and not model.dNamS: if hasattr(model, 'vNamS'): model.dNamS = model.vNamS else: model.dNamS = model.slug embedDoc = doc_remove_empty_keys(to_python(model, allow_none=True)) doc_errors = validate(modelClass, embedDoc) if doc_errors: total_errors += doc_errors['count'] post_errors.append(doc_errors) continue embedDoc['_c'] = attr_c # logit update embedDoc = logit(self.usr, embedDoc, method='post') collTmp.update(where, {"$push": { attrNam: embedDoc}, "$set": {'eIds': doc['eIds']}} ) doc_info['doc'] = embedDoc docs.append(doc_info) # http://docs.mongodb.org/manual/applications/update/ # { $set: { 'emails.$.eId': 2 } response['total_inserted'] = len(docs) if post_errors: response['total_invalid'] = len(post_errors) response['errors'] = post_errors response['total_errors'] = total_errors status = 400 else: response['total_invalid'] = 0 response['docs'] = docs return {'response': response, 'status': status} # Initialize a temp (tmp) doc if no OID and no data. elif not '_id' in doc_keys and len(doc_keys) == 1: newDocTmp = True useTmpDoc = True # Clone to a temp doc if OID and no isTmp flag set. elif '_id' in doc_keys and not 'isTmp' in doc_keys and len(doc_keys) > 2: cloneDocTmp = True useTmpDoc = True # find source doc # set locked = True doc_cloned = coll.find_and_modify( query = {'_id':_id}, update = {"$set": {'locked': True}}, new = True ) # set cloned doc in tmp collection isTmp = True doc_cloned['isTmp'] = True # don't need locked set in tmp doc del doc_cloned['locked'] # clone/save doc to tmp collection # TODO properly handle exception try: collTmp.insert(doc_cloned) except: pass doc_info['doc'] = doc_cloned # TODO # should return a link to object according to good practice #doc_info['link'] = get_document_link(class_name, id) docs.append(doc_info) continue # Update temp doc to base collection if OID and isTmp is set. elif '_id' in doc_keys and 'isTmp' in doc_keys and doc['isTmp']: upsertDocTmp = True useTmpDoc = False tmp_doc = collTmp.find_one({'_id': _id}) # unset isTmp _id = tmp_doc['_id'] tmp_doc['locked'] = False del tmp_doc['_id'] del tmp_doc['isTmp'] # logit update tmp_doc = logit(self.usr, tmp_doc) # update original/source doc doc = coll.update({'_id': _id}, {"$set": tmp_doc}, upsert=True, safe=True) # remove tmp doc collTmp.remove({'_id': _id}) # though not necessary, to be consistant, return full doc doc = coll.find_one({'_id': _id}) doc_info['doc'] = doc docs.append(doc_info) continue # Insert a new doc if no OID and there are more attributes than _c. elif not '_id' in doc_keys and len(doc_keys) > 2: insertDoc = True useTmpDoc = False # need to cruz through all doc keys to handle arrays/listtype fields. # they need to be initialized according to the appropriate model and validated, etc. doc = initDocListTypes(doc, self.usr) # init model instance model = modelClass(**doc) # set isTemp model.isTmp = useTmpDoc and 'isTmp' in model._fields # try to generate dNam # if there is a vNam class property # if already has a value, use it if hasattr(model, 'vNam') and not useTmpDoc and 'dNam' in model._fields and not model.dNam: model.dNam = model.vNam # assign dId if 'dId' in model._fields and not model.dId: response = nextId(coll) if response['status'] == 200: model.dId = response['nextId'] else: # handle error condition pass # generate a slug if: # not a temp doc and slug is empty if 'slug' in model._fields and not useTmpDoc and not model.slug: model.slug = slugify(model.dNam, coll) # set dNamS if used: # not a temp doc and dNamS is empty, set to value of slug if 'dNamS' in model._fields and not useTmpDoc and not model.dNamS: model.dNamS = model.slug # do not validate if using temp doc if not useTmpDoc: doc_errors = validate(modelClass, doc) if doc_errors: total_errors += doc_errors['count'] post_errors.append(doc_errors) continue # modelClass stuffed in all available fields # let's remove all empty fields to keep mongo clean. doc = to_python(model, allow_none=True) # do not log if using temp doc #log date time user involved with this event if not useTmpDoc: doc = logit(self.usr, doc, 'post') doc['_c'] = _c doc_clean = doc_remove_empty_keys(doc) # posting an existing temp doc to base collection if _id: doc_clean['_id'] = str(_id) id = str(coll.insert(doc_clean)) # TODO properly handle exception try: collTmp.remove({'_id': _id}) except: pass # posting initialized temp doc else: # TODO properly handle exception try: coll.insert(doc_clean) except: pass doc_info['doc'] = doc_clean # TODO # should return a link to object according to good practice #doc_info['link'] = get_document_link(class_name, id) docs.append(doc_info) response['total_inserted'] = len(docs) if post_errors: response['total_invalid'] = len(post_errors) response['errors'] = post_errors response['total_errors'] = total_errors status = 400 else: response['total_invalid'] = 0 response['docs'] = docs return {'response': response, 'status': status}
def post_attr(self, doc, attrNam, attr_c, attrVal, useTmpDoc = True): ''' doc = base doc to post/add attrVal to (in the attrNam field) Must contain _c, and _id usr = user object coll = collection attr_c = model class _id = doc id attrNam = attribute/fieldname attrVal = value to post/add ''' db = self.db errors = {} doc_info = {} response = {} status = 200 post_errors = [] total_errors = 0 doc_id = doc['_id'] usrOID = self.usr['OID'] where = {'_id': doc_id} doc_c = doc['_c'] docModel = _cs[doc_c]['modelClass']() collNam = docModel.meta['collection'] coll = db[collNam + '_tmp'] if useTmpDoc else db[collNam] attrModel = _cs[attr_c]['modelClass']() for k,v in attrVal.iteritems(): setattr(attrModel, k, v) # set the next element id eId resp = nextEId(doc, attrNam) doc = resp['doc'] attrModel.eId = resp['eId'] if hasattr(attrModel, 'vNam') and 'dNam' in attrModel._fields and not attrModel.dNam: attrModel.dNam = attrModel.vNam # set dNamS if used: # if dNamS is empty, set to value of slug if hasattr(attrModel, 'vNam') and 'dNamS' in attrModel._fields and not attrModel.dNamS: attrModel.dNamS = attrModel.vNamS attrValClean = doc_remove_empty_keys(to_python(attrModel, allow_none=True)) attrErrors = validate(_cs[attr_c]['modelClass'], attrValClean) if attrErrors: response['total_invalid'] = 1 response['errors'] = attrErrors response['total_errors'] = 1 return {'response': response, 'status': 400} # logit update attrValClean = logit(self.usr, attrValClean, method='post') resp = coll.update(where, {"$push": { attrNam: attrValClean}, "$set": {'eIds': doc['eIds']}} ) response['update_response'] = resp return {'response': response, 'status': 200}