我知道我的问题可能涉及相互/循环导入,我在发布之前就搜索过了。我发现当前解决我问题的唯一解决方案是将导入移动到其中一个文件的末尾,就在实际使用导入的函数之前。但我也读到了这一点,这是非常不受推荐的。
推荐的解决方案,只需:
A.py
中的 - > import B
并在B.py
- > import A
然后访问这些功能,不起作用。
所以我的Django应用程序中有三个应用程序:“核心”,“通知”和“帐户”。以下是他们的片段:
core.models:
from django.db import models
from django.contrib.auth.models import User
import notifications.models
from z_misc import general_scripts
# Create your models here.
class BaseModel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
以下是我在Notification
中使用core.models
的方式:
# inspect mb for each artist
### Assume class is defined and @classmethod is intended, having some formatting pains here
@classmethod
def refresh_from_mb(cls):
artists = cls.get_artists_with_subs()
for artist in artists:
added_rgs = general_scripts.refresh_artist(artist.mbid)
for rg in added_rgs:
new_notification = Notification(artist_id=artist.id, release_group=rg)
new_notification.save()
notification.models:
from django.db import models
import core.models
import accounts.models
class Notification(core.models.BaseModel):
artist_id = models.OneToOneField(core.models.Artist, related_name="artist_notifications", null=False)
release_group = models.OneToOneField(core.models.ReleaseGroup, related_name="rg_notifications", null=False)
def get_rg_type(self):
return self.release_group.type
accounts.models:
from django.db import models
from django.contrib.auth.models import User
import core.models
from django.db.models import Q
# Create your models here.
class UserProfile(core.models.BaseModel):
#TODO: add email_activated = models.BooleanField(default=False)
user = models.OneToOneField(User, related_name="user_profile")
正如您所看到的,我遵循的建议不是from
然后是import
,而是import
,然后使用完整的符号。它不起作用。完整的错误回溯是:
Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x10fa9af28>
Traceback (most recent call last):
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/utils/autoreload.py", line 228, in wrapper
fn(*args, **kwargs)
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run
autoreload.raise_last_exception()
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/utils/autoreload.py", line 251, in raise_last_exception
six.reraise(*_exception)
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/utils/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/utils/autoreload.py", line 228, in wrapper
fn(*args, **kwargs)
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/__init__.py", line 27, in setup
apps.populate(settings.INSTALLED_APPS)
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/apps/registry.py", line 108, in populate
app_config.import_models()
File "/Users/username/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/apps/config.py", line 202, in import_models
self.models_module = import_module(models_module_name)
File "/Users/username/.pyenv/versions/3.6.1/lib/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 978, in _gcd_import
File "<frozen importlib._bootstrap>", line 961, in _find_and_load
File "<frozen importlib._bootstrap>", line 950, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
File "/Users/username/PycharmProjects/artist_notify/core/models.py", line 3, in <module>
import notifications.models
File "/Users/username/PycharmProjects/artist_notify/notifications/models.py", line 3, in <module>
import accounts.models
File "/Users/username/PycharmProjects/artist_notify/accounts/models.py", line 8, in <module>
class UserProfile(core.models.BaseModel):
AttributeError: module 'core' has no attribute 'models'
我不知道在这一点上我该怎么做才能解决它。除了开始将import语句移动到文件的中间/结尾,这似乎是一个很大的禁忌。
请不要建议我使用from app.models import ModelName
,因为这是我之前使用的,直到由于循环导入而出现错误。
答案 0 :(得分:1)
我不知道你在哪里读到这个建议,但这是完全错误的。无论您使用何种形式的import语句,循环导入都是循环导入。
此处的解决方案 not 将模型完全导入核心。你似乎不需要它们,因为你没有引用它们。
答案 1 :(得分:1)
在设计过程中,您需要避免使用循环引用,特别是导入。 IOW,这是一个设计错误而不是实现错误。简化的情况是(A -> B
表示A
导入B
,N
=通知,C
=核心,{A
1}} =帐户):
N -> C
C -> N
A -> C
A -> N
如果我们以图形方式绘制,则更容易看出我们只需要解决C
和N
之间的循环:
即。我们只需要删除C
和N
之间的箭头/导入之一。
Django有许多功能可用于解耦循环参考图,例如:
对外键中的模型使用字符串引用,而不是
from core.models import ReleaseGroup # this is how you should import models
class Notification(...):
release_group = models.OneToOneField(ReleaseGroup, ..)
你会这样做:
# no import of ReleaseGroup model
class Notification(...):
release_group = models.OneToOneField('core.models.ReleaseGroup', ..)
Django有一些有用的功能,例如:您可以多次遍历关系并避免导入,例如而不是:
Notification.objects.filter(release_group=ReleaseGroup.objects.get(pk=1))
需要导入Notification
和ReleaseGroup
,您可以这样做:
Notification.objects.filter(release_group__pk=1)
在一般情况下,您需要使用联合,提取,移动中的一个或多个。
合并:这个很简单,如果您将C
和N
合并到一个更大的模块中,那么您已经解决了圆形度(一个文件中的圆形度是非常好。)
提取:如果您可以找到可以提取到新模块中的某些代码子集,这样这个新模块位于导入图的顶部或底部,那么您就是&#39已经解决了循环问题,例如如果你有两个模块,有很多模型,但只有两个模块有一个循环引用(这里是X2和Y2):
x.py:
class X1(Model):
...
class X2(Model):
y2 = ForeignKey(Y2)
class X3...
y.py:
class Y1(...): ..
class Y2(Model):
x2 = ForeignKey(X1)
...
然后通过引入一个新模块来解决循环(x.py和y.py将分别从xy.py导入X2和Y2)
xy.py:
class X2(Model):
y2 = ForeignKey(Y2)
class Y2(Model):
x2 = ForeignKey(X1)
将其他模型留在原处。 (注意:提取也可以只与一个模型一起使用 - 我认为你的BaseModel
可能是提取的候选者。)
move:在精神上类似于提取,您会找到一小段代码导致循环并将其移动到另一个现有模块,因此它不再导致循环。在上面的示例中,X2可能已移至y.py或Y2可能已移至x.py。