用pythonanywhere部署时报错ModuleNotFoundError: No module named 'wsgi'?

楼主看的是flask web开发实践解析,现在到了部署阶段,因为先前用过helloflask上watchlist介绍的pythonanywhere部署,部署时遇到问题,打开部署网站的报错日志如下:
error.log

2023-11-19 07:54:16,271: Error running WSGI application
2023-11-19 07:54:16,273: ModuleNotFoundError: No module named 'wsgi'
2023-11-19 07:54:16,273:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-19 07:54:16,273:     from wsgi import bp as application
2023-11-19 07:54:16,273: ***************************************************
2023-11-19 07:54:16,273: If you're seeing an import error and don't know why,
2023-11-19 07:54:16,274: we have a dedicated help page to help you debug: 
2023-11-19 07:54:16,274: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-19 07:54:16,274: ***************************************************
2023-11-19 08:08:27,926: Error running WSGI application
2023-11-19 08:08:27,927: TypeError: 'Blueprint' object is not callable
2023-11-19 08:08:54,589: Error running WSGI application
2023-11-19 08:08:54,590: TypeError: 'Blueprint' object is not callable
2023-11-19 08:19:54,446: Error running WSGI application
2023-11-19 08:19:54,448: ModuleNotFoundError: No module named 'dotenv'
2023-11-19 08:19:54,448:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-19 08:19:54,450: we have a dedicated help page to help you debug: 
2023-11-19 08:19:54,450: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-20 11:11:09,496: Error running WSGI application
2023-11-20 11:11:09,502: ModuleNotFoundError: No module named 'dotenv'
2023-11-20 11:11:09,502:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-20 11:11:09,502:     from wsgi import bp as application
2023-11-20 11:11:09,503: 
2023-11-20 11:11:09,503:   File "/home/JTPing/microblog/./wsgi.py", line 3, in <module>
2023-11-20 11:11:09,503:     from dotenv import load_dotenv
2023-11-20 11:11:09,503: ***************************************************
2023-11-20 11:11:09,503: If you're seeing an import error and don't know why,
2023-11-20 11:11:09,504: we have a dedicated help page to help you debug: 
2023-11-20 11:11:09,504: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-20 11:11:09,504: ***************************************************
2023-11-20 11:11:10,446: Error running WSGI application
2023-11-20 11:11:10,447: ModuleNotFoundError: No module named 'dotenv'
2023-11-20 11:11:10,447:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-20 11:11:10,447:     from wsgi import bp as application
2023-11-20 11:11:10,447: 
2023-11-20 11:11:10,447:   File "/home/JTPing/microblog/./wsgi.py", line 3, in <module>
2023-11-20 11:11:10,447:     from dotenv import load_dotenv
2023-11-20 11:11:10,448: ***************************************************
2023-11-20 11:11:10,448: If you're seeing an import error and don't know why,
2023-11-20 11:11:10,448: we have a dedicated help page to help you debug: 
2023-11-20 11:11:10,448: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-20 11:11:10,448: ***************************************************
2023-11-21 11:01:33,951: Error running WSGI application
2023-11-21 11:01:33,955: ModuleNotFoundError: No module named 'dotenv'
2023-11-21 11:01:33,955:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-21 11:01:33,955:     from wsgi import bp as application
2023-11-21 11:01:33,955: 
2023-11-21 11:01:33,955:   File "/home/JTPing/microblog/./wsgi.py", line 3, in <module>
2023-11-21 11:01:33,955:     from dotenv import load_dotenv
2023-11-21 11:01:33,955: ***************************************************
2023-11-21 11:01:33,956: If you're seeing an import error and don't know why,
2023-11-21 11:01:33,956: we have a dedicated help page to help you debug: 
2023-11-21 11:01:33,956: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-21 11:01:33,956: ***************************************************
2023-11-21 11:01:38,492: Error running WSGI application
2023-11-21 11:01:38,493: ModuleNotFoundError: No module named 'dotenv'
2023-11-21 11:01:38,493:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-21 11:01:38,493:     from wsgi import bp as application
2023-11-21 11:01:38,493: 
2023-11-21 11:01:38,493:   File "/home/JTPing/microblog/./wsgi.py", line 3, in <module>
2023-11-21 11:01:38,493:     from dotenv import load_dotenv
2023-11-21 11:01:38,494: ***************************************************
2023-11-21 11:01:38,494: If you're seeing an import error and don't know why,
2023-11-21 11:01:38,494: we have a dedicated help page to help you debug: 
2023-11-21 11:01:38,494: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-21 11:01:38,494: ***************************************************
2023-11-21 11:01:53,633: Error running WSGI application
2023-11-21 11:01:53,634: ModuleNotFoundError: No module named 'dotenv'
2023-11-21 11:01:53,634:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-21 11:01:53,635:     from wsgi import bp as application
2023-11-21 11:01:53,635: 
2023-11-21 11:01:53,635:   File "/home/JTPing/microblog/./wsgi.py", line 3, in <module>
2023-11-21 11:01:53,635:     from dotenv import load_dotenv
2023-11-21 11:01:53,635: ***************************************************
2023-11-21 11:01:53,636: If you're seeing an import error and don't know why,
2023-11-21 11:01:53,636: we have a dedicated help page to help you debug: 
2023-11-21 11:01:53,636: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-21 11:01:53,636: ***************************************************
2023-11-21 11:02:21,901: Error running WSGI application
2023-11-21 11:02:21,902: ModuleNotFoundError: No module named 'dotenv'
2023-11-21 11:02:21,902:   File "/var/www/jtping_pythonanywhere_com_wsgi.py", line 44, in <module>
2023-11-21 11:02:21,902:     from wsgi import bp as application
2023-11-21 11:02:21,902: 
2023-11-21 11:02:21,902:   File "/home/JTPing/microblog/./wsgi.py", line 3, in <module>
2023-11-21 11:02:21,903:     from dotenv import load_dotenv
2023-11-21 11:02:21,903: ***************************************************
2023-11-21 11:02:21,903: If you're seeing an import error and don't know why,
2023-11-21 11:02:21,903: we have a dedicated help page to help you debug: 
2023-11-21 11:02:21,903: https://help.pythonanywhere.com/pages/DebuggingImportError/
2023-11-21 11:02:21,903: ***************************************************

但是楼主在pythonanywhere的bash console上python shell上试了下dotenv是存在的:

>>> from dotenv import load_dotenv
>>> import os

相关文件结构如下:

bluelog / wsgi.py
bluelog / app / main

wsgi.py:

import os

from dotenv import load_dotenv

dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

from app.main import bp

/var/www_my_domian_com_wsgi.py:

import sys

path = '/home/my_username/microblog'
if path not in sys.path:
    sys.path.append(path)

from wsgi import bp as application

/var/www_my_domian_com_wsgi.py 文件里的 path 变量里的 my_username 是不是需要改一下:

/home/my_username/microblog

有没有虚拟环境,venv之类的,是不是里面没装dotenv

我猜测问题似乎出自flask migrations上,就试着flask db migrate:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
ERROR [flask_migrate] Error: Target database is not up to date.

然后 在shell 里

>>>flask db heads
ceca8f22389c (head)
>>>flask db current
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
dc96d9b0d0bf

然后又运行了下面的:

flask db stamp heads
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running stamp_revision dc96d9b0d0bf -> ceca8f22389c

再次检查:

flask db current
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
ceca8f22389c (head)

然后尝试flask db upgrade:

flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.

确定了migration版本一致后,再迁移:

flask db migrate
    INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
    INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
    INFO  [alembic.autogenerate.compare] Detected added table 'message'
    INFO  [alembic.autogenerate.compare] Detected added index 'ix_message_timestamp' on '['timestamp']'
    INFO  [alembic.autogenerate.compare] Detected added table 'notification'
    INFO  [alembic.autogenerate.compare] Detected added index 'ix_notification_name' on '['name']'
    INFO  [alembic.autogenerate.compare] Detected added index 'ix_notification_timestamp' on '['timestamp']'
    INFO  [alembic.autogenerate.compare] Detected added table 'comment'
    INFO  [alembic.autogenerate.compare] Detected added index 'ix_comment_timestamp' on '['timestamp']'
    INFO  [alembic.autogenerate.compare] Detected added column 'post.language'
    INFO  [alembic.autogenerate.compare] Detected added column 'post.body_html'
    INFO  [alembic.autogenerate.compare] Detected type change from VARCHAR(length=140) to Text() on 'post.body'
    INFO  [alembic.autogenerate.compare] Detected added column 'user.token'
    INFO  [alembic.autogenerate.compare] Detected added column 'user.token_expiration'
    INFO  [alembic.autogenerate.compare] Detected added column 'user.last_message_read_time'
    INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_token' on '['token']'
      Generating /home/JTPing/microblog/migrations/versions/e6990a7ae19f_.py ...  done
    (venv) 01:50 ~/microblog (main)$ flask db upgrade
    INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
    INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
    INFO  [alembic.runtime.migration] Running upgrade 9d026987d6f2 -> e6990a7ae19f, empty message

现在网站可以访问注册,但是无法评论点击 文件 标签,报错就是和先前一样,就是:
发生意外错误,已通知管理员

我就cd migrations/versions一下,发现:

···05eba44a406a_notifications.py b07ebb5fe6e0_fix_markdown_rendering_of_comments.py
14ba2865cec3_add_comments.py ceca8f22389c_user_tokens.py
3d95c0a8c088_users_table.py dc96d9b0d0bf_followers.py
6ee987d26551_posts_table.py e6990a7ae19f_.py
92a7096214ea_new_fields_in_user_model.py e949960c17e9_let_user_be_able_to_follow_and_unfollow.py
9389414e987c_enable_markdown_editor.py f3cff53a2dcf_add_language_to_posts.py
9d026987d6f2_.py fd8a7d4f9bd4_private_messages.py
pycache···

发现多了两个 e6990a7ae19f_.py9d026987d6f2_.py.
后者就是先前报错无法登录时所处的版本,前者就是现在数据库所处的版本.

我猜问题出现在前者,刚才哪里操作不对, 就再后台cat了一下:

"""empty message
Revision ID: e6990a7ae19f
Revises: 9d026987d6f2
Create Date: 2023-12-09 01:50:25.406017
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e6990a7ae19f'
down_revision = '9d026987d6f2'
branch_labels = None
depends_on = None
def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('message',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('sender_id', sa.Integer(), nullable=True),
    sa.Column('recipient_id', sa.Integer(), nullable=True),
    sa.Column('body', sa.String(length=140), nullable=True),
    sa.Column('timestamp', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['recipient_id'], ['user.id'], ),
    sa.ForeignKeyConstraint(['sender_id'], ['user.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    with op.batch_alter_table('message', schema=None) as batch_op:
        batch_op.create_index(batch_op.f('ix_message_timestamp'), ['timestamp'], unique=False)
    op.create_table('notification',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=128), nullable=True),
    sa.Column('user_id', sa.Integer(), nullable=True),
    sa.Column('timestamp', sa.Float(), nullable=True),
    sa.Column('payload_json', sa.Text(), nullable=True),
    sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    with op.batch_alter_table('notification', schema=None) as batch_op:
        batch_op.create_index(batch_op.f('ix_notification_name'), ['name'], unique=False)
        batch_op.create_index(batch_op.f('ix_notification_timestamp'), ['timestamp'], unique=False)
    op.create_table('comment',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('body', sa.String(length=140), nullable=True),
    sa.Column('timestamp', sa.DateTime(), nullable=True),
    sa.Column('user_id', sa.Integer(), nullable=True),
    sa.Column('post_id', sa.Integer(), nullable=True),
    sa.Column('body_html', sa.Text(), nullable=True),
    sa.ForeignKeyConstraint(['post_id'], ['post.id'], ),
    sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    with op.batch_alter_table('comment', schema=None) as batch_op:
        batch_op.create_index(batch_op.f('ix_comment_timestamp'), ['timestamp'], unique=False)
    with op.batch_alter_table('post', schema=None) as batch_op:
        batch_op.add_column(sa.Column('language', sa.String(length=5), nullable=True))
        batch_op.add_column(sa.Column('body_html', sa.Text(), nullable=True))
        batch_op.alter_column('body',
               existing_type=sa.VARCHAR(length=140),
               type_=sa.Text(),
               existing_nullable=True)
    with op.batch_alter_table('user', schema=None) as batch_op:
        batch_op.add_column(sa.Column('token', sa.String(length=32), nullable=True))
        batch_op.add_column(sa.Column('token_expiration', sa.DateTime(), nullable=True))
        batch_op.add_column(sa.Column('last_message_read_time', sa.DateTime(), nullable=True))
        batch_op.create_index(batch_op.f('ix_user_token'), ['token'], unique=True)
       # ### end Alembic commands ###
def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('user', schema=None) as batch_op:
        batch_op.drop_index(batch_op.f('ix_user_token'))
        batch_op.drop_column('last_message_read_time')
        batch_op.drop_column('token_expiration')
        batch_op.drop_column('token')
    with op.batch_alter_table('post', schema=None) as batch_op:
        batch_op.alter_column('body',
               existing_type=sa.Text(),
               type_=sa.VARCHAR(length=140),
               existing_nullable=True)
        batch_op.drop_column('body_html')
        batch_op.drop_column('language')
    with op.batch_alter_table('comment', schema=None) as batch_op:
        batch_op.drop_index(batch_op.f('ix_comment_timestamp'))
    op.drop_table('comment')
    with op.batch_alter_table('notification', schema=None) as batch_op:
        batch_op.drop_index(batch_op.f('ix_notification_timestamp'))
        batch_op.drop_index(batch_op.f('ix_notification_name'))
    op.drop_table('notification')
    with op.batch_alter_table('message', schema=None) as batch_op:
        batch_op.drop_index(batch_op.f('ix_message_timestamp'))
    op.drop_table('message')
    # ### end Alembic commands ###

但是仍然查不出问题在哪儿里,希望大佬帮忙解惑!

代码有放到 GitHub 上吗?我周末去实际部署试一下

有的,地址 JTP-123/microblog (github.com)