3

Django アプリの 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)

    ...

インポートは正常に機能します-print SmartPrefetchQuerySetモジュール自体で実行でき、出力は<class 'main.models.SmartPrefetchQuerySet'>. しかし、実際に get_query_set メソッドを呼び出すと、次のようになります。

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

一方、get_query_set メソッド自体の内部にインポートを移動すると、正常に動作します。また、Django シェルを開いて Event モデルを直接渡すことで SmartPrefetchQuerySet をインスタンス化すると、それも問題なく動作します。SmartPrefetchQuerySet が None に見えるのはメソッド内だけです (そのメソッドに挿入された print ステートメントは、名前が実際に None を参照していることを確認します)。

「SmartPrefetchQuerySet」という名前は、そのクラスのクラス定義でのみ使用され、この場所でそれを呼び出し、コードベースの他の場所では使用されません。

Python名前空間が通常どのように機能するかを考えると、この動作がどのように可能になるかわかりません。インポートされた名前を、モジュール レベルで 1 つのものとして定義し、同じモジュール内のメソッド内でまったく異なるものとして定義し、モジュール内のどこにもその名前への他の割り当てを行わないようにするにはどうすればよいでしょうか? これまでの私の唯一の考えは、循環インポートの問題である可能性があるということですが、そのような循環インポートは見つかりません。通常、循環インポートはそれほど微妙な問題ではないようです。

編集:models.pyファイル全体、多少サニタイズされ、省略されています:

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'>"
4

1 に答える 1