【问题标题】:Django psycopg2.ProgrammingError relation does not exist using custom model Manager使用自定义模型管理器不存在 Django psycopg2.ProgrammingError 关系
【发布时间】:2017-01-19 13:08:59
【问题描述】:

我无法使用我当前的代码库从头开始进行任何迁移。

运行 ./manage.py makemigrations reports 给出以下回溯

Traceback (most recent call last):
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "reports_frozenschedule" does not exist
LINE 1: ..._file", "reports_frozenschedule"."abandoned" FROM "reports_f...
                                                             ^


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/management/base.py", line 342, in execute
    self.check()
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/management/base.py", line 374, in check
    include_deployment_checks=include_deployment_checks,
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/management/base.py", line 361, in _run_checks
    return checks.run_checks(**kwargs)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/checks/registry.py", line 81, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/checks/urls.py", line 14, in check_url_config
    return check_resolver(resolver)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/core/checks/urls.py", line 24, in check_resolver
    for pattern in resolver.url_patterns:
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/utils/functional.py", line 35, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/urls/resolvers.py", line 313, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/utils/functional.py", line 35, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/urls/resolvers.py", line 306, in urlconf_module
    return import_module(self.urlconf_name)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/importlib/__init__.py", line 109, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 2254, in _gcd_import
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1129, in _exec
  File "<frozen importlib._bootstrap>", line 1471, in exec_module
  File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
  File "/home/jwe/Documents/piesup2/piesup2/urls.py", line 9, in <module>
    from reports.api import viewsets
  File "/home/jwe/Documents/piesup2/reports/api/viewsets.py", line 77, in <module>
    class ActiveServerViewSet(ReadOnlyModelViewSet):
  File "/home/jwe/Documents/piesup2/reports/api/viewsets.py", line 78, in ActiveServerViewSet
    queryset = Server.active.all()
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/models/manager.py", line 160, in all
    return self.get_queryset()
  File "/home/jwe/Documents/piesup2/reports/models.py", line 550, in get_queryset
    fqdn__in=active_server_fqdns()
  File "/home/jwe/Documents/piesup2/reports/models.py", line 541, in active_server_fqdns
    f.deserialize.servers for f in FrozenSchedule.objects.active()
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/models/query.py", line 256, in __iter__
    self._fetch_all()
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/models/query.py", line 1087, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/models/query.py", line 54, in __iter__
    results = compiler.execute_sql()
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 835, in execute_sql
    cursor.execute(sql, params)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/backends/utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/home/jwe/Documents/piesup2/venv/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: relation "reports_frozenschedule" does not exist
LINE 1: ..._file", "reports_frozenschedule"."abandoned" FROM "reports_f...
                                                             

在堆栈跟踪之后,当 Djagno 扫描我的 urls.py 文件时,中断开始,如下所示:

from django.conf.urls import include, url
from rest_framework import routers

from reports.api import viewsets

router = routers.DefaultRouter()
router.register(r'active-servers', viewsets.ActiveServerViewSet)
...

urlpatterns = [
    url(r'^api/', include(router.urls)),
    url(r'^', include('reports.urls')),
]

它到达from reports.api import viewsets 并导入以下内容:

viewsets.py

from reports.models import Server

...

class ActiveServerViewSet(ReadOnlyModelViewSet):
    queryset = Server.active.all()
    ...

它到达Server.active.all(),它依赖于此处显示的自定义模型管理器:

models.py

class ActiveServerManager(models.Manager):
    def get_queryset(self):
        return super(ActiveServerManager, self).get_queryset().filter(
            fqdn__in=active_server_fqdns()
        )


class Server(models.Model):
    fqdn = models.CharField(....)
    ....
    objects = models.Manager()
    active = ActiveServerManager()

我的服务器模型管理器调用函数active_server_fqdns(),如下所示:

def active_server_fqdns():
    # Returns flat set of FQDN's associated with all currently active Schedules
    return {server.fqdn for servers in [
        f.deserialize.servers for f in FrozenSchedule.objects.active()
    ] for server in servers}

这个函数在集合理解中调用 FrozenSchedule.objects.active(),这就是一切爆炸的地方

自定义查询集FrozenSchedule.objects.active()如下所示。

这里 Django 抱怨 the relation "reports_frozenschedule" does not exist

class FrozenScheduleQuerySet(models.QuerySet):

    def active(self):
        return self.filter(
            Q(start_date__lte=Now()), Q(end_date__gte=Now()) | Q(end_date=None)
        )

class FrozenSchedule(models.Model):
      ....
      objects = FrozenScheduleQuerySet.as_manager()

为什么我的模型管理器会导致我的数据库迁移失败?

似乎 urls.py 发生了某种竞争条件,导致查询集在表实际注册之前被评估。

更新修复

修复是,如建议摆脱模块级查询。我已经把它移到了 DjangoRestFramework 提供的 get_queryset() 方法中,比如:

class ActiveServerViewSet(ReadOnlyModelViewSet):
    serializer_class = serializers.LinkedServerSerializer
    ...

    def get_queryset(self):
        return Server.active.all()

由于它不再具有查询集属性,我不得不向我的视图集添加一个 base_name 参数

router.register(r'active-servers', viewsets.ActiveServerViewSet, 'Server')

【问题讨论】:

    标签: python django migration


    【解决方案1】:

    这是由模块级代码与数据库交互引起的:

    class ActiveServerViewSet(ReadOnlyModelViewSet):
        queryset = Server.active.all()
    

    当您运行 makemigrations 时,此代码在模块加载时运行,并且正如您所布置的,当相应的数据库表不存在时,会导致 FrozenSchedule QuerySet 被评估 (for f in FrozenSchedule.objects.active())还没有。

    您通常应该避免让模块级代码(包括方法之外的类定义)执行数据库查询,因为此代码是在模块导入时执行的,这通常是在您启动服务器或运行管理命令时。

    例如见this ticket

    你不应该在模块级别执行查询

    【讨论】:

    • 您可以通过覆盖get_queryset 方法而不是queryset 并在其中移动Server.active.all() 查询来解决此问题。
    猜你喜欢
    • 2018-05-23
    • 2017-06-26
    • 1970-01-01
    • 2013-06-01
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 2014-07-11
    相关资源
    最近更新 更多