3

I have code that looks like this in my Django app's models.py:

from main.models import SmartPrefetchQuerySet

class EventPrivacyManager(SoftDeletablePrivacyManager):
    def get_query_set_for_producer(self, producer):
        return self.get_query_set().filter(users_about=producer)

    def get_query_set(self):
        return SmartPrefetchQuerySet(self.model, using=self._db)

    ...

The import works fine -- I can print SmartPrefetchQuerySet in the module itself and the output is <class 'main.models.SmartPrefetchQuerySet'>. But when I actually call the get_query_set method I get this:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File ".../feeds/models.py", line 29, in smart_prefetch_for
    return self.get_query_set().smart_prefetch_for(*args, **kwargs)
  File ".../feeds/models.py", line 26, in get_query_set
    return SmartPrefetchQuerySet(self.model, using=self._db)
TypeError: 'NoneType' object is not callable

If, on the other hand, I move the import inside the get_query_set method itself, it works fine. And if I open up a Django shell and instantiate SmartPrefetchQuerySet by passing the Event model to it directly, that works fine too. It's only inside the method that SmartPrefetchQuerySet appears to be None (a print statement injected into that method verifies that the name indeed refers to None).

The "SmartPrefetchQuerySet" name is only used in the class definition for that class, and in this place calling it, and nowhere else in the codebase.

I don't understand how this behavior is possible given how Python namespaces normally work. How can an imported name be defined to be one thing at the module level, and a totally different thing inside a method in that same module, with no other assignments to that name anywhere in the module? My only thought so far is that it might be a circular import problem, but I can't find any such circular import, and usually circular imports seem to result in less subtle problems.

Edit: the entire models.py file, somewhat sanitized and abbreviated:

from django.db import models

from django.conf import settings

from auth.models import HasPrivacy, SoftDeletablePrivacyManager
from lck.django.common.models import TimeTrackable, SoftDeletable
from groups.models import Group

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.utils import timezone

from main.models import SmartPrefetchQuerySet # not literally from a module named "main", but the name has to be sanitized for this post

print SmartPrefetchQuerySet # prints "<class 'main.models.SmartPrefetchQuerySet'>"

# Note: there is an additional import at the bottom of this file for event signal registration

class EventPrivacyManager(SoftDeletablePrivacyManager):
    def get_query_set_for_producer(self, producer):
        "class that can be overridden by children of this manager for models that don't have a 'user' attrib"
        return self.get_query_set().filter(users_about=producer)

    def get_query_set(self):
        print SmartPrefetchQuerySet # prints "None"
        return SmartPrefetchQuerySet(self.model, using=self._db)

    def smart_prefetch_for(self, *args, **kwargs):
        return self.get_query_set().smart_prefetch_for(*args, **kwargs)

class Event(HasPrivacy, SoftDeletable, TimeTrackable):
    ...

    objects = EventPrivacyManager()

    ...

class Notification(SoftDeletable, TimeTrackable):
    ...

print SmartPrefetchQuerySet # prints "<class 'main.models.SmartPrefetchQuerySet'>"

# this import registers the event signal handlers.
# don't remove it even if it doesn't look like it's being used in this
# file, and don't move it from the bottom of this file in order to avoid
# circular imports!
import events

print SmartPrefetchQuerySet # prints "<class 'main.models.SmartPrefetchQuerySet'>"
Andrew Gorcester
  • 19,595
  • 7
  • 57
  • 73
  • Which version of Django you are using? In `1.6` `get_query_set` is now `get_queryset` – Aamir Rind Aug 06 '13 at 21:05
  • I am using version 1.5, so this naming should be correct. – Andrew Gorcester Aug 06 '13 at 21:06
  • Try to add `print SmartPrefetchQuerySet` at the end of the module, *before* any other running code and see if the class is already `None`. Somewhere something *is* re-assigning it, otherwise you'd get a `NameError`. – Bakuriu Aug 06 '13 at 21:58
  • @Bakuriu Okay, I tried that; unfortunately the results are unhelpful, as the name remains defined to the class and not to None throughout the module, including before and after the final import. I will edit the code example to show those print statements and their output. – Andrew Gorcester Aug 06 '13 at 22:06

1 Answers1

0

If SmartPrefetchQuerySet is written in C, the problem may actually be inside the call, rather than SmartPrefetchQuerySet itself being None. You can see similar results with built-ins:

>>> max([1,2], key=None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Check what you're passing as arguments.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • `SmartPrefetchQuerySet` is pure python, subclassing from the pure python `django.db.models.query.QuerySet`. Also, as mentioned in the question, a print statement placed before the call verifies that the name really is None inside that context. I can publish the SmartPrefetchQuerySet module but since the module's `__init__` is definitely not being called, it doesn't seem relevant yet. – Andrew Gorcester Aug 06 '13 at 21:08
  • Huh. That's odd. Can you post the rest of the code, so we can see if any other bindings for the name are being generated? – user2357112 Aug 06 '13 at 21:10
  • Of `SmartPrefetchQuerySet`, of `EventPrivacyManager`, or of the entire events module? – Andrew Gorcester Aug 06 '13 at 21:12
  • The rest of the file the code you've shown so far comes from, with print statements. – user2357112 Aug 06 '13 at 21:14
  • Okay, but for brevity I'll have to gut the classes defined in that file other than `EventPrivacyManager`. That shouldn't be a problem, as I can't imagine how class definitions not being called here would affect the namespace. – Andrew Gorcester Aug 06 '13 at 21:15
  • They could, in certain weird cases, but unless you're using `global` or `globals`, it shouldn't matter. – user2357112 Aug 06 '13 at 21:19
  • Definitely not using `global` or `globals`! They're normal Django model definitions. – Andrew Gorcester Aug 06 '13 at 21:20