Example #1
0
 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
Example #2
0
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)
Example #3
0
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)