DataGridFieldFactory with Dynamic Vocabularies

Hello all.

I am using DataGridFieldFactory with a choice field that uses a dynamic vocabulary (IVocabularyFactory). All seems to work fine. I am able to call the vocabulary and able to add the content.

But Datagrid calls the vocabulary every second, and calls again if you click the + button of the datagrid to add a new row. 2 initial calls for page load, then calls it infinitely or at least (20 calls :frowning: ). This makes my add content page to load slowly, and adding a content would take minutessssss.

14 PM

04 PM

Any help is very much appreciated :slight_smile:
Thankss,

Sandrea

Maybe you could take a look at how it's done on existing add-ons ? I remember having looked at the code from collective.glossary and noticing the following;

from plone.memoize import ram
(...)
def _catalog_counter_cachekey(method, self):
    """Return a cachekey based on catalog updates."""

    catalog = api.portal.get_tool('portal_catalog')
    return str(catalog.getCounter())
(..)
class GlossaryView(BrowserView):

    """Default view of Glossary type"""

    def __init__(self, context, request):
        self.context = context
        self.request = request

    @ram.cache(_catalog_counter_cachekey)
    def get_entries(self):
        """Get glossary entries and keep them in the desired format"""

        catalog = api.portal.get_tool('portal_catalog')
        path = '/'.join(self.context.getPhysicalPath())
        query = dict(portal_type='Term', path={'query': path, 'depth': 1})

the catalog search is cached. Maybe you could take some inspiration from that ? full code at github

If you have success with this approach, be kind enough to post a report, I'll appreciate it (I have never used this technique and I am interested to know how much can be won with it)

Also it never hurts to have some caching on your site but I don't know if it applies in your case.

2 Likes

Apologies for reviving this old thread, but I'm trying to solve the same issue @milktea_plone was having. This page shows up in search results, so it may be useful to continue here.

The example from collective.glossary may not be optimal, see

Caching documentation is here:
https://docs.plone.org/manage/deploying/performance/decorators.html

There is also a Plone Training chapter which goes into this topic:
https://training.plone.org/5/mastering-plone/dexterity_3.html#the-viewlet

I'll report back on my findings...

Norbert

Please paste code properly instead of screenshots.
Screenshots are not searchable.

Using the HTTP Request example provided in the Annotations documentation, I was able to substantially (50% or more) bring down page load times for content types which have Datagrid fields.

Example below is for a product which may be available in up 50 color variations and 5 package sizes. The datagrid may then be filled with attributes such as a suggested retail price, ean etc. for each color/size combination which is available for this product.

Colors and sizes may have been stored in the actual product or in a default "master".

@provider(IVocabularyFactory)
def ArticlesVocabulary(context):
    request = getRequest()
    key = "cache-%s" % context.getId()

    # we could also set the annotation on the context
    # object itself when it is saved 
    cache = IAnnotations(request)
    data = cache.get(key, None)

    if not data:
        data = _ArticlesVocabulary(context)
        cache[key] = data

    return data


def _ArticlesVocabulary(context):
    terms = []

    p_catalog = getToolByName(context, 'portal_catalog')

    package_size_vocabulary_factory = getUtility(IVocabularyFactory, 'mdb_theme.PackageSize')
    package_size_vocabulary = package_size_vocabulary_factory(context)
    color_brains = get_color_brains_for_product(context)
      
    available_colors = get_ordered_colors( context
                                         , color_brains
                                         , color_type_list = u'default_color_names'
                                         , color_type_mode = None
                                         , priority_color_names = None
                                         , )

    """ Available packaging sizes for this product
        a set with one or more members
        e.g. set([u'l002500', u'x999999', u'l000250', u'l000750', u'l010000'])
    """
    available_package_sizes = getattr(context, 'gebinde_grossen', None)

    if not available_package_sizes:
        # query the MDB if no custom package sizes
        mdb_path   = context.mdb_verweis.to_path
        mdb_result = p_catalog.searchResults(path={'query':mdb_path, 'depth':0 })[0]
        available_package_sizes = mdb_result.gebinde_grossen

    for color in available_colors:
        if available_package_sizes:
            for p_size in sorted(available_package_sizes):

                # Returns SimpleTerm object by value look-up
                # Exclude "Grossere Mengen" value
                if p_size != u'x999999':
                    p_term = package_size_vocabulary.getTermByToken(p_size)

                    s_token = p_term.token
                    s_value = p_term.value
                    s_title = p_term.title

                    t_value = '%s_%s' % (color['color_id'], s_value)
                    t_token = t_value
                    t_title = '%s - %s' % (color['color_title'], s_title)

                    terms.append(SimpleTerm(value=t_value, token=t_token, title=t_title))
        else:
            terms.append(SimpleTerm(value=color['color_id'], token=color['color_id'], title=color['color_title']))

    return SimpleVocabulary(terms)