`SyntaxError:没有绑定非本地' topics_with_log_tag'发现它虽然受到了限制

时间:2018-05-18 13:15:17

标签: django

我想通过指定的' log'来获取主题。标记:

在嵌套函数get_topics_with_log_tag中,我设置变量topics_with_log_tag非本地:

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)

抛出SyntaxError:

SyntaxError: no binding for nonlocal 'topics_with_log_tag' found

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

使用nonlocal的问题是什么?

我发现了错误。

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

models.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)

2 个答案:

答案 0 :(得分:2)

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)

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

Django ORM中的高效数据库查询

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

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

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

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

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

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

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

答案 1 :(得分:2)

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)

你只能非局部的局部变量而不是全局变量