def _array_op(self, op, arrName, vals): """ array operations If the array corresponding to arrName is an array of pointers, vals must be an array of ParseObjects. WARNING! This will set the current array to the 1 returned, which only has objectId. """ # format vals to pointers if it is an array of objects arr_ptrs = "_" + arrName.upper() array_not_none = getattr(self, arrName) is not None if self.__dict__[arrName] is None: self.__dict__[arrName] = [] if arr_ptrs in self.__dict__: vals = [ format_pointer(val.__class__.__name__, val.objectId) for\ val in vals ] if array_not_none: # array is not null/None res = parse("PUT", self.path() + '/' + self.objectId, {arrName: {'__op':op, 'objects':vals} }) else: # array does not exist. initialize it here. res = parse("PUT", self.path() + '/' + self.objectId, {arrName:vals }) if res and 'error' not in res: self.update_locally(res, False) return True return False
def _array_op(self, op, arrName, vals): """ array operations If the array corresponding to arrName is an array of pointers, vals must be an array of ParseObjects. WARNING! This will set the current array to the 1 returned, which only has objectId. """ # format vals to pointers if it is an array of objects arr_ptrs = "_" + arrName.upper() array_not_none = getattr(self, arrName) is not None if self.__dict__[arrName] is None: self.__dict__[arrName] = [] if arr_ptrs in self.__dict__: vals = [ format_pointer(val.__class__.__name__, val.objectId) for\ val in vals ] if array_not_none: # array is not null/None res = parse("PUT", self.path() + '/' + self.objectId, {arrName: { '__op': op, 'objects': vals }}) else: # array does not exist. initialize it here. res = parse("PUT", self.path() + '/' + self.objectId, {arrName: vals}) if res and 'error' not in res: self.update_locally(res, False) return True return False
def pointer_query(dst_class, dst_class_where, dst_class_key, dst_class_key_class, dst_class_key_where, dst_class_key_where_exclude=False, count=False, order="createdAt", limit=900, skip=0): if dst_class_key_where_exclude: x = "$notInQuery" else: x = "$inQuery" where = { # this is how queries are done to Pointer types dst_class_key: { x: { "where": query(dst_class_key_where, where_only=True), "className": dst_class_key_class, # inner limit applies! "limit": 990, } } } if dst_class_where: where.update(query(dst_class_where, where_only=True)) if count: res = parse("GET", "classes" + "/" + dst_class, query={ "where": json.dumps(where), "count": 1, "limit": 0, }) if 'error' not in res: return res['count'] # May occur if the class or the relation column does not yet exist return 0 res = parse("GET", "classes" + "/" + dst_class, query={ "where": json.dumps(where), "limit": limit, "order": order, "skip": skip, }) if 'error' not in res: return res # May occur if the class or the relation column does not yet exist return None
def _relation_op(self, op, relAttrName, objectIds): """ Helper function for add and remove relations. """ if relAttrName not in self.__dict__: return False className = getattr(self, relAttrName) if className == USER_CLASS: className = "_User" objs = [] for oid in objectIds: objs.append( { "__type": "Pointer", "className": className, "objectId": oid } ) res = parse("PUT", self.path() + "/" + self.objectId, { relAttrName[:-1]: { "__op": op, "objects": objs, } } ) if res and 'error' not in res: cacheAttrName = relAttrName[0].lower() + relAttrName[1:-1] self.__dict__[cacheAttrName] = None return True else: return False
def _relation_op(self, op, relAttrName, objectIds): """ Helper function for add and remove relations. """ if relAttrName not in self.__dict__: return False className = getattr(self, relAttrName) if className == USER_CLASS: className = "_User" objs = [] for oid in objectIds: objs.append({ "__type": "Pointer", "className": className, "objectId": oid }) res = parse("PUT", self.path() + "/" + self.objectId, {relAttrName[:-1]: { "__op": op, "objects": objs, }}) if res and 'error' not in res: cacheAttrName = relAttrName[0].lower() + relAttrName[1:-1] self.__dict__[cacheAttrName] = None return True else: return False
def pointer_query(dst_class, dst_class_where, dst_class_key, dst_class_key_class, dst_class_key_where, dst_class_key_where_exclude=False, count=False, order="createdAt", limit=900, skip=0): if dst_class_key_where_exclude: x = "$notInQuery" else: x = "$inQuery" where = { # this is how queries are done to Pointer types dst_class_key:{ x:{ "where":query(dst_class_key_where, where_only=True), "className": dst_class_key_class, # inner limit applies! "limit":990, } } } if dst_class_where: where.update(query(dst_class_where, where_only=True)) if count: res = parse("GET", "classes" + "/" + dst_class, query={ "where":json.dumps(where), "count":1, "limit":0, }) if 'error' not in res: return res['count'] # May occur if the class or the relation column does not yet exist return 0 res = parse("GET", "classes" + "/" + dst_class, query={ "where":json.dumps(where), "limit":limit, "order":order, "skip":skip, }) if 'error' not in res: return res # May occur if the class or the relation column does not yet exist return None
def count(self, **constraints): """ returns the number of objects that matches the constraints """ constraints["count"] = 1 constraints["limit"] = 0 res = parse("GET", self.path, query=query(constraints)) if res and 'count' in res: return res['count'] return 0
def count(self, **constraints): """ returns the number of objects that matches the constraints """ constraints["count"] = 1 constraints["limit"] = 0 res = parse("GET", self.path, query=query(constraints) ) if res and 'count' in res: return res['count'] return 0
def create(self): """ Override to trim the cc_number before storing """ data = self._get_formatted_data() self._trim_cc_number() res = parse('POST', self.path(), data) if res and "error" not in res: self.update_locally(res, True) return True return False
def fetch_all(self, clear_first=False, with_cache=True): """ Gets all of this object's data from parse and update all of its value locally. This includes pulling each pointer for caching. If clear_first, sets all non-BUILTINS in __dict__ to None. - note that cache attrs are also set to None If with_cache, retrieves all the cache attributes. """ if clear_first: keys = self.__dict__.keys() for key in keys: if key not in ParseObject.BUILTINS: self.__dict__[key] = None if with_cache: cache_attrs = "" # fill up the Pointer cache attributes and array of pointers for key in self.__dict__.iterkeys(): if key[0].isupper() and not key.endswith("_") and\ not key == "ACL": cache_attrs = cache_attrs + key + "," if len(cache_attrs) > 0: res = parse("GET", self.path() + "/" + self.objectId, query={"include": cache_attrs}) else: res = parse("GET", self.path() + "/" + self.objectId) else: res = parse("GET", self.path() + "/" + self.objectId) if res and "error" not in res: self.update_locally(res, False) return True return False
def increment(self, attr, amount): """ increments this object's attribute by amount. Attribute must be an integer! """ res = parse("PUT", self.path() + "/" + self.objectId, { attr: { "__op": "Increment", "amount": amount } }) if res and "error" not in res: self.set(attr, self.__dict__.get(attr) + amount) return True return False
def fetch_all(self, clear_first=False, with_cache=True): """ Gets all of this object's data from parse and update all of its value locally. This includes pulling each pointer for caching. If clear_first, sets all non-BUILTINS in __dict__ to None. - note that cache attrs are also set to None If with_cache, retrieves all the cache attributes. """ if clear_first: keys = self.__dict__.keys() for key in keys: if key not in ParseObject.BUILTINS: self.__dict__[key] = None if with_cache: cache_attrs = "" # fill up the Pointer cache attributes and array of pointers for key in self.__dict__.iterkeys(): if key[0].isupper() and not key.endswith("_") and\ not key == "ACL": cache_attrs = cache_attrs + key + "," if len(cache_attrs) > 0: res = parse("GET", self.path() + "/" + self.objectId, query={ "include": cache_attrs }) else: res = parse("GET", self.path() + "/" + self.objectId) else: res = parse("GET", self.path() + "/" + self.objectId) if res and "error" not in res: self.update_locally(res, False) return True return False
def update(self): """ Override to trim the cc_number before storing """ # get the formated data to be put in the request self._trim_cc_number() data = self._get_formatted_data() res = parse("PUT", self.path() + "/" + self.objectId, data) if res and "error" not in res: self.update_locally(res, True) return True return False
def increment(self, attr, amount): """ increments this object's attribute by amount. Attribute must be an integer! """ res = parse("PUT", self.path() + "/" + self.objectId, {attr: { "__op": "Increment", "amount": amount }}) if res and "error" not in res: self.set(attr, self.__dict__.get(attr) + amount) return True return False
def filter(self, **constraints): """ Returns the list of objects that matches the constraints. See WHERE_OPTIONS for all currently supported options. Double underscores allow for usage of WHERE_OPTIONS like gte (greater than or equal to). """ res = parse("GET", self.path, query=query(constraints)) if res and "results" in res: objs = [] for data in res['results']: objs.append(self.cls(**data)) return objs return []
def update(self): """ Save changes to this object to the Parse DB. Returns True if update is successful. Handles Pointers to other objects. If the pointer changed, then the cache corresponding to the attr is set to None. Relations are not handled by update. Use add_relation for Relations. """ # get the formated data to be put in the request data = self._get_formatted_data() res = parse("PUT", self.path() + "/" + self.objectId, data) if res and "error" not in res: self.update_locally(res, False) return True return False
def update(self): """ Capitalize certain strings before saving to parse. """ self.street = title(self.street).strip() self.city = title(self.city).strip() if self.state: self.state = self.state.upper().strip() if self.zip: self.zip = self.zip.strip() data = self._get_formatted_data() res = parse("PUT", self.path() + "/" + self.objectId, data) if res and "error" not in res: self.update_locally(res, False) return True return False
def update(self, save_password=False): # get the formated data to be put in the request data = self._get_formatted_data() # this is actually unnecessary since parse will not save # the given password if it is None. if not save_password: del data['password'] res = parse("PUT", self.path() + "/" + self.objectId, data) # always set the password to None to prevent passing it around in comet receive if self.__dict__.get("password") is not None: self.password = None if res and "error" not in res: self.update_locally(res, False) return True return False
def create(self): """ creates this object to the DB. This does not check uniqueness of any field. Use update to save an existing object. After a successful request, this object will now have the objectId available but not the rest of its attributes. Note that self.__dict__ contains objectId which will result to the parse request returning an error only if it is not None If there exist a Pointer to other objects, this method makes sure that it is saved as a Pointer. """ data = self._get_formatted_data() res = parse('POST', self.path(), data) if res and "error" not in res: self.update_locally(res, False) return True return False
def delete(self): """ delete the row corresponding to this object in Parse """ res = parse("DELETE", self.path() + '/' + self.objectId) if 'error' not in res: return True return False
def relational_query(src_id, src_class, src_key, dst_class, dst_class_key, dst_class_key_class, dst_class_key_where, dst_class_where=None, dst_class_key_where_exclude=False, count=False, order="createdAt", limit=900, skip=0): """ Make a query in an object's relation where the query involves a pointer to another class. src_id : the objectId of the of the object in the class in which the relation column exists. src_class : the name of the class of the object with the src_id src_key : the name of the relation column of the src_class dst_class : the output object's class - the class we are querying. dst_class_where : append more constraints to the where clause. dst_class_key : the name of the column in the dst_class in which the inQuery applies. dst_class_key_class : the class name of the dst_class_key dst_class_key_where : where parameter of the dst_class_key_class dst_class_key_where_exclude : inQuery if false count : if True, will only return the count of the results """ if dst_class_key_where_exclude: x = "$notInQuery" else: x = "$inQuery" where = { # specify the relation (which class contains # it and which column is it "$relatedTo": { "object": { "__type": "Pointer", "className": src_class, "objectId": src_id }, "key": src_key }, # this is how queries are done to Pointer types dst_class_key: { x: { "where": query(dst_class_key_where, where_only=True), "className": dst_class_key_class, # inner limit applies! "limit": 990, } } } if dst_class_where: where.update(query(dst_class_where, where_only=True)) if count: res = parse("GET", "classes" + "/" + dst_class, query={ "where": json.dumps(where), "count": 1, "limit": 0, }) if 'error' not in res: return res['count'] # May occur if the class or the relation column does not yet exist return 0 res = parse("GET", "classes" + "/" + dst_class, query={ "where": json.dumps(where), "limit": limit, "order": order, "skip": skip, }) if 'error' not in res: return res # May occur if the class or the relation column does not yet exist return None
def request_password_reset(email): res = parse("POST", "requestPasswordReset", {"email": email}) # success if res is {} return len(res) == 0
def get(self, attr, **constraints): """ returns attr if it is not None, otherwise fetches the attr from the Parse DB and returns that. If the attr is a #2, then it is treated as a cache for a ParseObject. Note that all of this attribute's data is retrieved. Getting an array of pointers using this method will return a list of ParseObjects with only the objectId set even if an include is provided. Constraints may also be provided to filter objects in a relation. If limit of 0 is given or result is empty, then the cache will be set to None. If count is given, then this method will return the count and not the list. """ # all objects have these by default. These should not be # manually set to None! if attr in ParseObject.BUILTINS: return self.__dict__.get(attr) if self.__dict__.get(attr) is not None: # make sure that if count constraints are # present the cache does not block if not (attr[0].islower() and attr[0].upper() +\ attr[1:] + "_" in self.__dict__ and\ 'count' in constraints): return self.__dict__.get(attr) # Pointer cache if attr[0].islower() and\ self.__dict__.get(attr[0].upper() + attr[1:]): # if pointer meta exist then use it if "_" + attr.lower() in self.__dict__: className = self.__dict__["_" + attr.lower()] else: className = attr[0].upper() + attr[1:] q = {} if "include" in constraints: q["include"] = constraints['include'] res = parse("GET", "classes/" + className +\ "/" + self.__dict__.get(attr[0].upper() +\ attr[1:]), query=q) if res and "error" not in res: setattr(self, attr, get_class(className)(**res)) else: return None # Relation cache elif attr[0].islower() and attr[0].upper() + attr[1:] +\ "_" in self.__dict__: className = self.__dict__[attr[0].upper() + attr[1:] + "_"] # need to use _user as classname if it is the USER_CLASS if className == USER_CLASS: tmp = "_User" else: tmp = className relName = attr[0].upper() + attr[1:] where_dict = { "$relatedTo":{ "object":{ "__type": "Pointer", "className": self.path().split('/')[1], "objectId": self.objectId}, "key": relName}, } where_dict.update(query(constraints, where_only=True)) q = {} q.update({"where":dumps(where_dict)}) # add the not where options for k,v in constraints.iteritems(): if k in NOT_WHERE_CONSTRAINTS: q.update({k:v}) res = parse("GET", 'classes/' + tmp, query=q) if res and "error" not in res: if len(res['results']) == 0: setattr(self, attr, None) else: c = get_class(className) setattr(self, attr, [ c(**d) for d in res['results'] ]) if 'count' in res: return res['count'] else: if 'count' in constraints: return 0 return None # date object elif attr.startswith("date_"): res = parse("GET", self.path(), query={"keys":attr, "where":dumps({"objectId":self.objectId})}) if 'results' in res and res['results']: setattr(self, attr, parser.parse(res.get('results')[0].get(\ attr).get("iso"))) # Image types elif is_image(attr) and attr.endswith("_url") and\ attr.replace("_url", "") in self.__dict__: attr_name = attr.replace("_url","") res = parse("GET", self.path(), query={"keys":attr_name, "where":dumps({"objectId":self.objectId})}) if 'results' in res and res['results']: img = res['results'][0].get(attr_name) if img: setattr(self, attr, img.get('url').replace(\ "http:/", "https://s3.amazonaws.com")) setattr(self, attr_name, img.get('name')) # attr is a geopoint elif attr == "coordinates" and attr in self.__dict__: res = parse("GET", self.path(), query={"keys":attr, "where":dumps({"objectId":self.objectId})}) if 'results' in res and res['results']: coordinates = res['results'][0].get("coordinates") latitude = coordinates.get("latitude") longitude = coordinates.get("longitude") if coordinates and latitude and longitude: setattr(self, attr, [latitude, longitude]) else: setattr(self, attr, None) # array of pointers elif "_" + attr.upper() in self.__dict__: q={"keys":attr, "where":dumps({"objectId":self.objectId})} if "include" in constraints: q["include"] = constraints['include'] res = parse("GET", self.path(), query=q) if 'results' in res and res['results']: result = res['results'][0][attr] if result is not None: setattr(self, attr, [ get_class(\ v['className'])(**v) for v in result ]) # attr is a regular attr or Pointer/Relation attr elif attr in self.__dict__: res = parse("GET", self.path(), query={"keys":attr, "where":dumps({"objectId":self.objectId})}) if 'results' in res and res['results']: # what if Pointer attr was set to None? # getting them will return a dict! if attr[0].isupper() and not attr.endswith("_"): p = res.get('results')[0].get(attr) if type(p) is dict: setattr(self, attr, p.get("objectId") ) else: setattr(self, attr, None) else: setattr(self, attr, res.get('results')[0].get(attr) ) return self.__dict__.get(attr)
def request_password_reset(email): res = parse("POST", "requestPasswordReset", { "email":email }) # success if res is {} return len(res) == 0
def relational_query(src_id, src_class, src_key, dst_class, dst_class_key, dst_class_key_class, dst_class_key_where, dst_class_where=None, dst_class_key_where_exclude=False, count=False, order="createdAt", limit=900, skip=0): """ Make a query in an object's relation where the query involves a pointer to another class. src_id : the objectId of the of the object in the class in which the relation column exists. src_class : the name of the class of the object with the src_id src_key : the name of the relation column of the src_class dst_class : the output object's class - the class we are querying. dst_class_where : append more constraints to the where clause. dst_class_key : the name of the column in the dst_class in which the inQuery applies. dst_class_key_class : the class name of the dst_class_key dst_class_key_where : where parameter of the dst_class_key_class dst_class_key_where_exclude : inQuery if false count : if True, will only return the count of the results """ if dst_class_key_where_exclude: x = "$notInQuery" else: x = "$inQuery" where = { # specify the relation (which class contains # it and which column is it "$relatedTo": { "object": { "__type": "Pointer", "className": src_class, "objectId": src_id }, "key": src_key }, # this is how queries are done to Pointer types dst_class_key:{ x:{ "where":query(dst_class_key_where, where_only=True), "className": dst_class_key_class, # inner limit applies! "limit":990, } } } if dst_class_where: where.update(query(dst_class_where, where_only=True)) if count: res = parse("GET", "classes" + "/" + dst_class, query={ "where":json.dumps(where), "count":1, "limit":0, }) if 'error' not in res: return res['count'] # May occur if the class or the relation column does not yet exist return 0 res = parse("GET", "classes" + "/" + dst_class, query={ "where":json.dumps(where), "limit":limit, "order":order, "skip":skip, }) if 'error' not in res: return res # May occur if the class or the relation column does not yet exist return None
def get(self, attr, **constraints): """ returns attr if it is not None, otherwise fetches the attr from the Parse DB and returns that. If the attr is a #2, then it is treated as a cache for a ParseObject. Note that all of this attribute's data is retrieved. Getting an array of pointers using this method will return a list of ParseObjects with only the objectId set even if an include is provided. Constraints may also be provided to filter objects in a relation. If limit of 0 is given or result is empty, then the cache will be set to None. If count is given, then this method will return the count and not the list. """ # all objects have these by default. These should not be # manually set to None! if attr in ParseObject.BUILTINS: return self.__dict__.get(attr) if self.__dict__.get(attr) is not None: # make sure that if count constraints are # present the cache does not block if not (attr[0].islower() and attr[0].upper() +\ attr[1:] + "_" in self.__dict__ and\ 'count' in constraints): return self.__dict__.get(attr) # Pointer cache if attr[0].islower() and\ self.__dict__.get(attr[0].upper() + attr[1:]): # if pointer meta exist then use it if "_" + attr.lower() in self.__dict__: className = self.__dict__["_" + attr.lower()] else: className = attr[0].upper() + attr[1:] q = {} if "include" in constraints: q["include"] = constraints['include'] res = parse("GET", "classes/" + className +\ "/" + self.__dict__.get(attr[0].upper() +\ attr[1:]), query=q) if res and "error" not in res: setattr(self, attr, get_class(className)(**res)) else: return None # Relation cache elif attr[0].islower() and attr[0].upper() + attr[1:] +\ "_" in self.__dict__: className = self.__dict__[attr[0].upper() + attr[1:] + "_"] # need to use _user as classname if it is the USER_CLASS if className == USER_CLASS: tmp = "_User" else: tmp = className relName = attr[0].upper() + attr[1:] where_dict = { "$relatedTo": { "object": { "__type": "Pointer", "className": self.path().split('/')[1], "objectId": self.objectId }, "key": relName }, } where_dict.update(query(constraints, where_only=True)) q = {} q.update({"where": dumps(where_dict)}) # add the not where options for k, v in constraints.iteritems(): if k in NOT_WHERE_CONSTRAINTS: q.update({k: v}) res = parse("GET", 'classes/' + tmp, query=q) if res and "error" not in res: if len(res['results']) == 0: setattr(self, attr, None) else: c = get_class(className) setattr(self, attr, [c(**d) for d in res['results']]) if 'count' in res: return res['count'] else: if 'count' in constraints: return 0 return None # date object elif attr.startswith("date_"): res = parse("GET", self.path(), query={ "keys": attr, "where": dumps({"objectId": self.objectId}) }) if 'results' in res and res['results']: setattr(self, attr, parser.parse(res.get('results')[0].get(\ attr).get("iso"))) # Image types elif is_image(attr) and attr.endswith("_url") and\ attr.replace("_url", "") in self.__dict__: attr_name = attr.replace("_url", "") res = parse("GET", self.path(), query={ "keys": attr_name, "where": dumps({"objectId": self.objectId}) }) if 'results' in res and res['results']: img = res['results'][0].get(attr_name) if img: setattr(self, attr, img.get('url').replace(\ "http:/", "https://s3.amazonaws.com")) setattr(self, attr_name, img.get('name')) # attr is a geopoint elif attr == "coordinates" and attr in self.__dict__: res = parse("GET", self.path(), query={ "keys": attr, "where": dumps({"objectId": self.objectId}) }) if 'results' in res and res['results']: coordinates = res['results'][0].get("coordinates") latitude = coordinates.get("latitude") longitude = coordinates.get("longitude") if coordinates and latitude and longitude: setattr(self, attr, [latitude, longitude]) else: setattr(self, attr, None) # array of pointers elif "_" + attr.upper() in self.__dict__: q = {"keys": attr, "where": dumps({"objectId": self.objectId})} if "include" in constraints: q["include"] = constraints['include'] res = parse("GET", self.path(), query=q) if 'results' in res and res['results']: result = res['results'][0][attr] if result is not None: setattr(self, attr, [ get_class(\ v['className'])(**v) for v in result ]) # attr is a regular attr or Pointer/Relation attr elif attr in self.__dict__: res = parse("GET", self.path(), query={ "keys": attr, "where": dumps({"objectId": self.objectId}) }) if 'results' in res and res['results']: # what if Pointer attr was set to None? # getting them will return a dict! if attr[0].isupper() and not attr.endswith("_"): p = res.get('results')[0].get(attr) if type(p) is dict: setattr(self, attr, p.get("objectId")) else: setattr(self, attr, None) else: setattr(self, attr, res.get('results')[0].get(attr)) return self.__dict__.get(attr)