`SyntaxError: no binding for nonlocal 'topics_with_log_tag' found` 虽然它是有界的

Alg*_*bra 5 django

我想获取带有指定“日志”标签的主题:

在嵌套函数中get_topics_with_log_tag,我设置了变量topics_with_log_tagnonlocal:

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

    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)
Run Code Online (Sandbox Code Playgroud)

它抛出语法错误:

SyntaxError: no binding for nonlocal 'topics_with_log_tag' found
Run Code Online (Sandbox Code Playgroud)

实际上,我确实绑定了它 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)
    return topics_with_log_tag

topics_with_log_tag = get_topics_with_log_tag(topics)
Run Code Online (Sandbox Code Playgroud)

我的使用有nonlocal什么问题?

我发现了错误。

Willem Van Onsem 引入了数据库级过滤器而不是嵌套的 for 循环。

模型.py

 class Topic(models.Model):
    """A topic the user is learning about."""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User)

    def __str__(self):
        """Return a string representation of the model."""
        return self.text

class Entry(models.Model):
    """Something specific learned about a topic"""
    topic = models.ForeignKey(Topic)
    title = models.CharField(max_length=200)
    text = models.TextField()
    tags = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 7

如何nonlocal工作

如果您使用nonlocal,这意味着 Python 将在函数的开始处从上面(以及超出)一个范围内查找具有相同名称的变量。但是在这里你没有定义这样的一个。我们可以通过定义一个更高的级别来修复它:

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)
Run Code Online (Sandbox Code Playgroud)

您可以使用global在这种情况下不需要声明这样的变量(在这种情况下它是在上层声明的),但这实际上也是一种反模式。

Django ORM中高效的数据库查询

然而,您在此处执行过滤的方式通常效率很低:您首先在这里迭代所有Topics,然后对每个主题执行额外的查询以获取所有Entrys,然后为每个Entry获取所有Tags,然后查看是否其中一个标签是#log。现在假设您有 10 个主题,每个主题有 10 个条目,每个条目有 5 个标签。这会导致您在数据库级别执行 500 多个查询。我们可以构造一个过滤器,如:

topics_with_log_tag = Topics.objects.filter(entry__tags__contains='#log').distinct()
Run Code Online (Sandbox Code Playgroud)

或更易读(括号用于允许多行表达式):

topics_with_log_tag = (Topics.objects
                             .filter(entry__tags__contains='#log')
                             .distinct())
Run Code Online (Sandbox Code Playgroud)

请注意,上面将(就像您的代码所做的那样)还包含主题,tags例如'#logarithm'. 它只检查它是否包含某个子字符串。为了防止这种情况发生,您将需要更高级的过滤或更好的标签表示(带有结束标记)。

例如,如果每个主题都以逗号(如'#foo,#bar,')结尾,那么我们可以查询'#log,'.

我们还可以使用正则表达式并检查新的哈希字符或字符串的结尾。


小智 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)
Run Code Online (Sandbox Code Playgroud)

您只能使用非局部局部变量而不是全局变量