【问题标题】:Confusion regarding Flask & importing modules关于 Flask 和导入模块的困惑
【发布时间】:2014-11-30 01:55:29
【问题描述】:

我有一个烧瓶应用程序,我正在尝试组织并遵循许多教程中显示的正确烧瓶文件夹结构和模块/导入。

我实际上不知道为什么我在做某些事情,这绝不是一件好事。

我的烧瓶应用布局如下:

/steam
    run.py
    /steamapp
        __init__.py
        config.py
        tasks.py
        views.py
        /static
           css.css
        /templates
           template.html

运行.py

from steamapp import app
app.run()

初始化.py

from celery import Celery
from config import secrets, constants
from flask.ext.openid import OpenID
from flask import Flask
import praw

def make_celery(app):
    celery = Celery(app.import_name, backend='amqp', broker='amqp://guest@localhost//')
    celery.conf.update(app.config)
    TaskBase = celery.Task
    class ContextTask(TaskBase):
        abstract = True
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)
    celery.Task = ContextTask
    return celery


app = Flask(__name__)
oid = OpenID(app)
app.debug = True
app.secret_key = secrets.APP_SECRET_KEY
celery = make_celery(app)


reddit = praw.Reddit(constants.USERAGENT)
reddit.login(constants.USERNAME, secrets.PASSWORD)

rs = praw.Reddit(constants.USERAGENT)
rs.set_oauth_app_info(secrets.CLIENT_ID, secrets.CLIENT_SECRET, constants.REDIRECT_URL)


from steamapp import views
from steamapp import tasks

views.py

from steamapp import app, oid, celery
from tasks import get_auth_url, check_reddit_oauth, request_steam_api
from flask import Flask, session, redirect, request, render_template, url_for
from config import secrets, constants
from flask.ext.openid import OpenID
import os
import praw
import time
import json
import string
import random
import requests


def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))


def check_session_id(session):
    if "id" in session:
        return True
    else:
        return render_template("error.html", error=["?", "No session ID found."])


@app.route("/register")
def register():
    if "id" not in session:
        session["id"] = id_generator()
    authorize_url = get_auth_url(session)
    return redirect(authorize_url)


@app.route("/redirect/")
@oid.loginhandler
def redirect_to_steam_oauth():
   ...
   ...
   ...
   etc

tasks.py

from steamapp import celery, rs
from config import secrets, constants
import requests

@celery.task
def get_auth_url(session):
    return rs.get_authorize_url(session["id"], scope="identity")

当我运行 run.py 时,我得到以下回溯:

Traceback (most recent call last):
  File "/home/andy/Desktop/Python Projects/Finished/steam/run.py", line 1, in <module>
    from steamapp import app
  File "/home/andy/Desktop/Python Projects/Finished/steam/steamapp/__init__.py", line 25, in <module>
    from steamapp import views
  File "/home/andy/Desktop/Python Projects/Finished/steam/steamapp/views.py", line 20, in <module>
    @app.route("/register")
NameError: name 'app' is not defined

我无法理解整体上发生了什么 - 我需要解释我为什么要做我正在做的事情:

  1. app.run() 分离成一个从steamapp 导入应用程序的单独python 文件的原因是什么?为什么 app.run() 不在 views.py 中?

  2. init.py 的实际用途是什么?为什么要导入views.pytasks.py

  3. 为什么从__init__.py导入“app”时没有定义回溯?

感谢任何可以提供澄清的人,因为我已经不知道发生了什么。


编辑:

在阅读@dreamriver 的回复后,我已经用我所做的更改更新了 OP 中的代码。

我还是有点困惑——为什么我要在views.py 中导入flask,而它已经在init.py 中导入了,而views 是从哪里导入并可能从哪里运行的?

另外,芹菜工人如何与此联系起来?将 tasks.py 设置为 Celery worker 会返回以下回溯:

 celery -A tasks worker --loglevel=info
Traceback (most recent call last):
  File "/usr/local/bin/celery", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/dist-packages/celery/__main__.py", line 30, in main
    main()
  File "/usr/local/lib/python2.7/dist-packages/celery/bin/celery.py", line 81, in main
    cmd.execute_from_commandline(argv)
  File "/usr/local/lib/python2.7/dist-packages/celery/bin/celery.py", line 769, in execute_from_commandline
    super(CeleryCommand, self).execute_from_commandline(argv)))
  File "/usr/local/lib/python2.7/dist-packages/celery/bin/base.py", line 305, in execute_from_commandline
    argv = self.setup_app_from_commandline(argv)
  File "/usr/local/lib/python2.7/dist-packages/celery/bin/base.py", line 465, in setup_app_from_commandline
    self.app = self.find_app(app)
  File "/usr/local/lib/python2.7/dist-packages/celery/bin/base.py", line 485, in find_app
    return find_app(app, symbol_by_name=self.symbol_by_name)
  File "/usr/local/lib/python2.7/dist-packages/celery/app/utils.py", line 229, in find_app
    sym = symbol_by_name(app, imp=imp)
  File "/usr/local/lib/python2.7/dist-packages/celery/bin/base.py", line 488, in symbol_by_name
    return symbol_by_name(name, imp=imp)
  File "/usr/local/lib/python2.7/dist-packages/kombu/utils/__init__.py", line 92, in symbol_by_name
    module = imp(module_name, package=package, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/celery/utils/imports.py", line 101, in import_from_cwd
    return imp(module, package=package)
  File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/home/andy/Desktop/Python Projects/Finished/steam/steamapp/tasks.py", line 1, in <module>
    from steamapp import celery, rs
ImportError: No module named steamapp

【问题讨论】:

  • 你能显示views.py吗?
  • 完成 - 我已根据@dreamweavers 的回复更新了代码,但还有一些我不确定的新问题。 (我应该发一个新的 SO 帖子询问芹菜问题吗?)
  • 关于第一个问题——您是否对为什么要将应用程序导入views.py 或为什么要将视图导入__init__.py 感到困惑?无论如何,我现在都会更新我的答案。
  • @AndyB 我已经编辑了我的回复来回答你所有的问题,这一切有意义吗?
  • 感谢@dreamriver,你确实帮助我消除了很多困惑——我并不完全 100% 了解它,但它确实给了我一个继续前进的地方。跨度>

标签: python python-2.7 module flask


【解决方案1】:

您必须将应用显式导入到您的视图文件中。错误说app没有在views.py中定义,那是因为你需要这样做:

from steamapp import app

在视图文件的顶部。

__init__.py 将您的目录转换为 python 包。在解释器启动时,$PYTHONPATH 上带有__init__.py 的所有目录都添加到sys.path,这使得它们可以导入。 __init__.py 在它定义的模块被导入时也会被执行。这使得它非常有用,例如导出代码的主要 api。如果您查看flask 的源代码,您会看到您所做的所有代码

from flask import Flask, request etc

实际上是在单独文件中的较小功能块中定义的,然后将有趣的部分暴露在__init__.py中。

正如here 所说,Python 不希望包中的模块成为启动文件。 Python 打包有点乱。 This stackoverflow 的回答帮助我理解了其中一些问题,即相对导入完全中断,因为它们是根据 __name__ 计算的导入。

除了像__name____package__ 这样的一些模块级全局变量之外,没有任何东西被显式导入到顶级命名空间中。这就是为什么 app 在您的 views.py 文件中隐式不可用的原因。

这能回答你的问题吗?

编辑

您需要将views.py 导入__init__.py,否则您的views.py 文件将不会被执行,并且您的任何路由等都不会被定义。您需要将 app 导入 views.py,因为 app 不在您的 views.py 文件能够访问的命名空间中。这种两个文件相互导入的模式称为循环导入,可能会很棘手,但在这里没问题。您应该知道,模块加载后会被缓存,因此再次导入时不会重新执行。

在我看来,您的 celery 问题在 celery 启动时没有在 sys.path 上看到您的应用程序。此时显示sys.path 的输出会很有帮助。我的猜测是,如果您将工作目录添加到 $PYTHONPATH,问题将得到解决。当您使用 pip 等安装东西时,这些包会被添加到默认情况下 python 知道如何找到它们的地方。

【讨论】:

  • 我仍然对为什么我正在做某些事情的细节感到相当困惑,但我现在确实了解了该结构的工作原理。我已经用两个我无法弄清楚答案的问题更新了 OP。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-30
  • 2011-12-13
  • 2017-12-04
相关资源
最近更新 更多