[Fixed]-`SyntaxError: no binding for nonlocal 'topics_with_log_tag' found` although it's bounded

18๐Ÿ‘

โœ…

How nonlocal works

If you use nonlocal, that means that Python will, at the start of the function, look for a variable with the same name from one scope above (and beyond). But here you did not define such one. We can fix it with defining one, one level higher:

def log(request):
    """Show all topics and entries with  log tags"""

    topics = Topic.objects.all()
    #select entries with log tag
    topics_with_log_tag = []
    def get_topics_with_log_tag(topics):
        nonlocal topics_with_log_tag
        topics_with_log_tag = []
        for topic in topics:
            for entry in topic.entry_set.all():
                if "#log" in entry.tags:
                    topics_with_log_tag.append(topic)

    get_topics_with_log_tag(topics)

You can use global in which case you do not need to declare such variable (in that case it is declared at the upper level), but this is actually an anti-pattern as well.

Efficient database querying in Django ORM

Nevertheless the way you perform the filtering here, will usually be quite inefficient: you here first iterate over all Topics, then for each topic, you do an extra query fetching all Entrys, then for each Entry you fetch all Tags, and then you look whether one of the tags is #log. Now imagine that you have 10 topics, that have 10 entries per topic, and 5 tags per entry. That results in 500+ queries you do at the database level. We can construct a filter like:

topics_with_log_tag = Topics.objects.filter(entry__tags__contains='#log').distinct()

or more readable (brackets are used to allow multi-line expressions):

topics_with_log_tag = (Topics.objects
                             .filter(entry__tags__contains='#log')
                             .distinct())

Note that the above will (just like your code did), also contains topics with as tags for example '#logarithm'. It only checks if it contains a certain substring. In order to prevent that, you will need more advanced filtering, or better tag representation (with an end marker).

For example if every topic ends with a comma (like '#foo,#bar,') then we could query for '#log,'.

We can also work with regular expressions and check for a new hash character, or the end of the string.

5๐Ÿ‘

def log(request):
    """Show all topics and entries with  log tags"""
    topics_with_log_tag=[]
    topics = Topic.objects.all()
    #select entries with log tag
    def get_topics_with_log_tag(topics):
        nonlocal topics_with_log_tag
        topics_with_log_tag = []
        for topic in topics:
            for entry in topic.entry_set.all():
                if "#log" in entry.tags:
                    topics_with_log_tag.append(topic)

    get_topics_with_log_tag(topics)

you can only nonlocal the local variables not global variables

๐Ÿ‘คMohamed Javith

Leave a comment