def query_parent_objects(self, context, query=None): """Return the objects of the same type from the parent object :param query: Catalog query to narrow down the objects :type query: dict :returns: Content objects of the same portal type in the parent """ # return the object values if we have no catalog query if query is None: return self.get_parent_objects(context) # avoid undefined reference of catalog in except... catalog = None # try to fetch the results via the catalog try: catalogs = api.get_catalogs_for(context) catalog = catalogs[0] return map(api.get_object, catalog(query)) except (IndexError, UnicodeDecodeError, ParseError, APIError) as e: # fall back to the object values of the parent logger.warn("UniqueFieldValidator: Catalog query {} failed " "for catalog {} ({}) -> returning object values of {}" .format(query, repr(catalog), str(e), repr(api.get_parent(context)))) return self.get_parent_objects(context)
def query_parent_objects(self, context, query=None): """Return the objects of the same type from the parent object :param query: Catalog query to narrow down the objects :type query: dict :returns: Content objects of the same portal type in the parent """ # return the object values if we have no catalog query if query is None: return self.get_parent_objects(context) # avoid undefined reference of catalog in except... catalog = None # try to fetch the results via the catalog try: catalogs = api.get_catalogs_for(context) catalog = catalogs[0] return map(api.get_object, catalog(query)) except (IndexError, UnicodeDecodeError, ParseError, api.BikaLIMSError) as e: # fall back to the object values of the parent logger.warn("UniqueFieldValidator: Catalog query {} failed " "for catalog {} ({}) -> returning object values of {}" .format(query, repr(catalog), str(e), repr(api.get_parent(context)))) return self.get_parent_objects(context)
def __call__(self, value, *args, **kwargs): field = kwargs['field'] fieldname = field.getName() instance = kwargs['instance'] translate = getToolByName(instance, 'translation_service').translate # return directly if nothing changed if value == field.get(instance): return True # We want to use the catalog to speed things up, as using `objectValues` # is very expensive if the parent object contains many items parent_objects = [] # 1. Get the right catalog for this object catalogs = api.get_catalogs_for(instance) catalog = catalogs[0] # 2. Check if the field accessor is indexed field_index = None accessor = field.getAccessor(instance) if accessor: field_index = accessor.__name__ # 3. Check if the field index is in the indexes # Field is indexed, use the catalog instead of objectValues parent_path = api.get_parent_path(instance) portal_type = instance.portal_type catalog_query = {"portal_type": portal_type, "path": {"query": parent_path, "depth": 1}} if field_index and field_index in catalog.indexes(): # We use the field index to reduce the results list catalog_query[field_index] = value parent_objects = map(api.get_object, catalog(catalog_query)) elif fieldname in catalog.indexes(): # We use the fieldname as index to reduce the results list catalog_query[fieldname] = value parent_objects = map(api.get_object, catalog(catalog_query)) else: # fall back to the objectValues :( parent_object = api.get_parent(instance) parent_objects = parent_object.objectValues() for item in parent_objects: if hasattr(item, 'UID') and item.UID() != instance.UID() and \ fieldname in item.Schema() and \ str(item.Schema()[fieldname].get(item)) == str(value): # We have to compare them as strings because # even if a number (as an id) is saved inside # a string widget and string field, it will be # returned as an int. I don't know if it is # caused because is called with # <item.Schema()[fieldname].get(item)>, # but it happens... msg = _("Validation failed: '${value}' is not unique", mapping={'value': safe_unicode(value)}) return to_utf8(translate(msg)) return True
def get_catalog_for(self, brain_or_object): """Return the primary catalog for the given brain or object """ if not api.is_object(brain_or_object): raise TypeError("Invalid object type %r" % brain_or_object) catalogs = api.get_catalogs_for(brain_or_object, default="uid_catalog") return catalogs[0]
def make_catalog_query(self, context, field, value): """Create a catalog query for the field """ # get the catalogs for the context catalogs = api.get_catalogs_for(context) # context not in any catalog? if not catalogs: logger.warn("UniqueFieldValidator: Context '{}' is not assigned" "to any catalog!".format(repr(context))) return None # take the first catalog catalog = catalogs[0] # Check if the field accessor is indexed field_index = field.getName() accessor = field.getAccessor(context) if accessor: field_index = accessor.__name__ # return if the field is not indexed if field_index not in catalog.indexes(): return None # build a catalog query query = { "portal_type": api.get_portal_type(context), "path": { "query": api.get_parent_path(context), "depth": 1, } } query[field_index] = value logger.info("UniqueFieldValidator:Query={}".format(query)) return query
def __call__(self, value, *args, **kwargs): field = kwargs['field'] fieldname = field.getName() instance = kwargs['instance'] translate = getToolByName(instance, 'translation_service').translate # return directly if nothing changed if value == field.get(instance): return True # We want to use the catalog to speed things up, as using `objectValues` # is very expensive if the parent object contains many items parent_objects = [] # 1. Get the right catalog for this object catalogs = api.get_catalogs_for(instance) catalog = catalogs[0] # 2. Check if the field accessor is indexed field_index = None accessor = field.getAccessor(instance) if accessor: field_index = accessor.__name__ # 3. Check if the field index is in the indexes # Field is indexed, use the catalog instead of objectValues parent_path = api.get_parent_path(instance) portal_type = instance.portal_type catalog_query = { "portal_type": portal_type, "path": { "query": parent_path, "depth": 1 } } if field_index and field_index in catalog.indexes(): # We use the field index to reduce the results list catalog_query[field_index] = value parent_objects = map(api.get_object, catalog(catalog_query)) elif fieldname in catalog.indexes(): # We use the fieldname as index to reduce the results list catalog_query[fieldname] = value parent_objects = map(api.get_object, catalog(catalog_query)) else: # fall back to the objectValues :( parent_object = api.get_parent(instance) parent_objects = parent_object.objectValues() for item in parent_objects: if hasattr(item, 'UID') and item.UID() != instance.UID() and \ fieldname in item.Schema() and \ str(item.Schema()[fieldname].get(item)) == str(value): # We have to compare them as strings because # even if a number (as an id) is saved inside # a string widget and string field, it will be # returned as an int. I don't know if it is # caused because is called with # <item.Schema()[fieldname].get(item)>, # but it happens... msg = _("Validation failed: '${value}' is not unique", mapping={'value': safe_unicode(value)}) return to_utf8(translate(msg)) return True