def _get_schema_field_mapping(self): if self._schema_field_mapping_cache is None: schema_objs = self.schemas.values() mapping = field_mapping([s.id for s in schema_objs]) fm = dict([(s.slug, mapping[s.id]) for s in schema_objs]) if len(self.schema_slugs) == 1: fm = fm[self.schema_slugs[0]] self._schema_field_mapping_cache = fm return self._schema_field_mapping_cache
def populate_attributes_if_needed(newsitem_list, schema_list, get_lookups=True): """ Optimization helper function that takes a list of NewsItems and ensures the ni.attributes pseudo-dictionary is populated, for all NewsItems whose schemas have uses_attributes_in_list=True. This is accomplished with a minimal amount of database queries. The values in the NewsItem.attributes pseudo-dictionary are Lookup instances in the case of Lookup fields. Otherwise, they're the direct values from the Attribute table. (Note this is different than accessing NewsItem.attributes without having called this function, in which case Lookups are not dereferenced automatically. Client code such as AttributesForTemplate should handle both cases - or really .attributes should be fixed to be consistent.) schema_list should be a list of all Schemas that are referenced in newsitem_list. Note that the list is edited in place; there is no return value. """ from ebpub.db.models import Attribute, Lookup, SchemaField # To accomplish this, we determine which NewsItems in ni_list require # attribute prepopulation, and run a single DB query that loads all of the # attributes. Another way to do this would be to load all of the attributes # when loading the NewsItems in the first place (via a JOIN), but we want # to avoid joining such large tables. preload_schema_ids = set([s.id for s in schema_list if s.uses_attributes_in_list]) if not preload_schema_ids: return preloaded_nis = [ni for ni in newsitem_list if ni.schema_id in preload_schema_ids] if not preloaded_nis: return # fmap is a mapping like: # {schema_id: {'fields': [(name, real_name)], 'lookups': [real_name1, real_name2]}} fmap = {} attribute_columns_to_select = set(['news_item']) for sf in SchemaField.objects.filter(schema__id__in=[s.id for s in schema_list]).values('schema', 'name', 'real_name', 'is_lookup'): fmap.setdefault(sf['schema'], {'fields': [], 'lookups': []})['fields'].append((sf['name'], sf['real_name'])) if sf['is_lookup']: fmap[sf['schema']]['lookups'].append(sf['real_name']) attribute_columns_to_select.add(str(sf['real_name'])) att_dict = dict([(i['news_item'], i) for i in Attribute.objects.filter(news_item__id__in=[ni.id for ni in preloaded_nis]).values(*list(attribute_columns_to_select))]) if not fmap: return # Determine which Lookup objects need to be retrieved. lookup_ids = set() for ni in preloaded_nis: # Fix for #38: not all Schemas have SchemaFields, can be 100% vanilla. if not ni.schema_id in fmap: continue for real_name in fmap[ni.schema_id]['lookups']: if ni.id not in att_dict: # AFAICT this should not happen, but if you have # newsitems created before a schema defined any # schemafields, and schemafields were added later, # then you might get some NewsItems that don't have a # corresponding att_dict result. continue value = att_dict[ni.id][real_name] if ',' in str(value): lookup_ids.update(value.split(',')) else: lookup_ids.add(value) # Retrieve only the Lookups that are referenced in preloaded_nis. lookup_ids = [i for i in lookup_ids if i] if lookup_ids: lookup_objs = Lookup.objects.in_bulk(lookup_ids) else: lookup_objs = {} # Cache attribute values for each NewsItem in preloaded_nis. for ni in preloaded_nis: if not ni.id in att_dict: # Fix for #38: Schemas may not have any SchemaFields, and # thus the ni will have no attributes, and that's OK. continue att = att_dict[ni.id] att_values = {} for field_name, real_name in fmap[ni.schema_id]['fields']: value = att[real_name] if real_name in fmap[ni.schema_id]['lookups']: if real_name.startswith('int'): value = lookup_objs[value] else: # Many-to-many lookups are comma-separated strings. value = [lookup_objs[int(i)] for i in value.split(',') if i] att_values[field_name] = value select_dict = field_mapping([ni.schema_id]).get(ni.schema_id, {}) ni._attributes_cache = AttributeDict(ni.id, ni.schema_id, select_dict) ni._attributes_cache.cached = True ni._attributes_cache.update(att_values)
def populate_attributes_if_needed(newsitem_list, schema_list, get_lookups=True): """ Optimization helper function that takes a list of NewsItems and ensures the ni.attributes pseudo-dictionary is populated, for all NewsItems whose schemas have uses_attributes_in_list=True. This is accomplished with a minimal amount of database queries. The values in the NewsItem.attributes pseudo-dictionary are Lookup instances in the case of Lookup fields. Otherwise, they're the direct values from the Attribute table. (Note this is different than accessing NewsItem.attributes without having called this function, in which case Lookups are not dereferenced automatically. Client code such as AttributesForTemplate should handle both cases - or really .attributes should be fixed to be consistent.) schema_list should be a list of all Schemas that are referenced in newsitem_list. Note that the list is edited in place; there is no return value. """ from ebpub.db.models import Attribute, Lookup, SchemaField # To accomplish this, we determine which NewsItems in ni_list require # attribute prepopulation, and run a single DB query that loads all of the # attributes. Another way to do this would be to load all of the attributes # when loading the NewsItems in the first place (via a JOIN), but we want # to avoid joining such large tables. preload_schema_ids = set([s.id for s in schema_list if s.uses_attributes_in_list]) if not preload_schema_ids: return preloaded_nis = [ni for ni in newsitem_list if ni.schema_id in preload_schema_ids] if not preloaded_nis: return # fmap is a mapping like: # {schema_id: {'fields': [(name, real_name)], 'lookups': [real_name1, real_name2]}} fmap = {} attribute_columns_to_select = set(['news_item']) for sf in SchemaField.objects.filter(schema__id__in=[s.id for s in schema_list]).values('schema', 'name', 'real_name', 'is_lookup'): fmap.setdefault(sf['schema'], {'fields': [], 'lookups': []})['fields'].append((sf['name'], sf['real_name'])) if sf['is_lookup']: fmap[sf['schema']]['lookups'].append(sf['real_name']) attribute_columns_to_select.add(str(sf['real_name'])) att_dict = dict([(i['news_item'], i) for i in Attribute.objects.filter(news_item__id__in=[ni.id for ni in preloaded_nis]).values(*list(attribute_columns_to_select))]) if not fmap: return # Determine which Lookup objects need to be retrieved. lookup_ids = set() for ni in preloaded_nis: # Fix for #38: not all Schemas have SchemaFields, can be 100% vanilla. if not ni.schema_id in fmap: continue for real_name in fmap[ni.schema_id]['lookups']: if ni.id not in att_dict: # AFAICT this should not happen, but if you have # newsitems created before a schema defined any # schemafields, and schemafields were added later, # then you might get some NewsItems that don't have a # corresponding att_dict result. continue value = att_dict[ni.id][real_name] if ',' in str(value): lookup_ids.update(value.split(',')) else: lookup_ids.add(value) # Retrieve only the Lookups that are referenced in preloaded_nis. lookup_ids = [i for i in lookup_ids if i] if lookup_ids: lookup_objs = Lookup.objects.in_bulk(lookup_ids) else: lookup_objs = {} # Cache attribute values for each NewsItem in preloaded_nis. for ni in preloaded_nis: # Fix for #38: Schemas may not have any SchemaFields, and thus # the ni will have no attributes, and the schema won't be in # fmap, and that's OK. if not ni.id in att_dict: continue if not ni.schema_id in fmap: continue att = att_dict[ni.id] att_values = {} for field_name, real_name in fmap[ni.schema_id]['fields']: value = att[real_name] if real_name in fmap[ni.schema_id]['lookups']: if real_name.startswith('int'): value = lookup_objs[value] else: # Many-to-many lookups are comma-separated strings. value = [lookup_objs[int(i)] for i in value.split(',') if i] att_values[field_name] = value select_dict = field_mapping([ni.schema_id]).get(ni.schema_id, {}) ni._attributes_cache = AttributeDict(ni.id, ni.schema_id, select_dict) ni._attributes_cache.cached = True ni._attributes_cache.update(att_values)