Как обслуживать, ссылаться и сохранять файлы stati c в файлах Flask / python app.py или views.py, избегая ошибок FileNotFound - PullRequest
0 голосов
/ 31 января 2020

Похоже на этот вопрос, но у меня есть кое-какие дополнительные наблюдения. Как обслуживать stati c файлы в Flask

Основная проблема, с которой я столкнулся при адаптации решения из предыдущего поста, заключается в том, что мне нужно использовать функцию .savefig() matplotlib , который просто принимает строку для пути для сохранения. Поэтому мне нужно знать, какую строку передавать.

Я помню, что у меня было много проблем с файлами stati c, когда я впервые работал над приложением Flask локально. Я не знаю, как правильно указать путь к файлам stati c из моего файла app.py / views.py python. Я понимаю, что использование url_for() в html, а что нет, но обращение к файлам stati c в python не было таким простым.

Мое flask приложение имеет следующую структуру. Каталог root называется Neuroethics_Behavioral_Task

enter image description here

У меня есть файл с именем experiment.py, и этот файл в основном является views.py или app.py файл. Но я не могу найти правильный способ ссылки на файлы stati c.

Вот код experiment.py.

import functools

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for, Flask
)
from werkzeug.security import check_password_hash, generate_password_hash


app = Flask(__name__, instance_relative_config=True, static_url_path='') # creates the flask instance

bp= Blueprint('experiment', __name__)


@app.route('/')
@app.route('/index')
def index():
    return render_template("welcome.html")

@app.route('/experiment_app', methods=['GET', 'POST'])
def exp_js_psych_app():
    total_trials = int(request.form['trials'])
    import random 
    import string 
    user_id = ''.join([random.choice(string.ascii_letters 
                + string.digits) for n in range(8)]) 
    user_id = str(user_id)
    stim_metadata = generate_stim_for_trials(total_trials, user_id)
    return render_template("index.html", data={"user_id": str(user_id), "total_trials":total_trials, "lst_of_stim_jsons":stim_metadata})
def generate_stim_for_trials(trials, uid):
    import matplotlib
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import json
    import os
    json_dict = {}
    json_dict["stimuli path info"] = []
    lst_of_jsons_for_stimuli = []
    # Unused but problematic static reference.
    stim_metadata_path = "/static/textdata/stimulus_data" + str(uid) + ".json"
    # Another problematic static reference.
    stimuli_path = '/static/images/stimulus_img' + 1 + ".png"
    df = pd.DataFrame({"Risk":[25], "No Effect" : [50], "Gain":[25]}) 
    df.plot(kind='barh', legend = False, stacked=True, color=['#ed713a', '#808080', '#4169e1'])
    plt.axis('off')
    plt.gca().set_axis_off()
    plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
    plt.ylim(None,None)
    plt.autoscale()
    plt.margins(0,0)

    plt.savefig(stimuli_path) # this fails

Следует отметить, что передача в * Параметр 1030 * для вызова Flask() на самом деле не работает должным образом, я подумал, что это установит sh root в качестве места для первого поиска моих файлов stati c, но тот факт, что этот код все еще ошибка говорит об обратном, поскольку мой каталог /static/ явно находится в папке root.

В противном случае, как я уже сказал ... Мне нужно использовать plt.savefig(), который принимает путь, и я все еще могу Не могу понять, как я должен написать этот путь.

Мне удалось заставить stati c файлы работать до этого момента. Мне пришлось изменить две вышеупомянутые ссылки на файлы stati c на следующие:

stimuli_path = 'Neuroethics_Behavioral_Task/static/images/stimulus_img' + "1" + ".png"
stim_metadata_path = "Neuroethics_Behavioral_Task/static/textdata/stimulus_data" + str(uid) + ".json"

Другими словами, я мог получить файлы stati c ранее, включив каталог root в путь ,

и это по крайней мере заставило меня ... Пока я не попытался развернуть свое приложение на Heroku. Затем я начал получать ошибки, которые Neuroethics_Behavioral_Task/static/images/ не были распознанным путем ... так что теперь я чувствую, что мне действительно нужно начать ссылаться на файлы stati c правильно, и я не уверен, как это сделать.

Я пробовал варианты (например: images/stimulusimg1.png) и пытался изменить параметр static_url_path (например: static_url_path='/static/), но все это только привело к тому, что мое приложение Flask взломало sh локально говоря FileNotFound.

У меня есть еще один важный вопрос ...

Это приложение будет в онлайн-эксперименте, размещенном на Amazon Mechanical Turk. Изображения должны быть предварительно загружены фреймворком, который я использую под названием JsPsych. Но посты, подобные этой и той, которую я впервые включил, побуждают вас обслуживать файлы, используя NGINX Что именно означает «показывать файлы c»?

Понятия не имею, как Nginx или работа wsgi. Стоит ли мне пытаться доставить все мои файлы stati c таким образом, и если да, то как мне начать?

Итак, вкратце я задаю следующие вопросы:

  • Мне нужно сохранить файлы stati c в моем файле experiment.py / views.py / apps.py и поэтому мне нужно выяснить строку, которая фактически обозначает путь к моим изображениям stati c. Как я могу это сделать? Нужно ли мне на самом деле изменять переменную static_url_path?
  • Почему ранее я успешно обращался к каталогу root в моих путях к файлам * * c
  • Предполагается, что мои изображения быть загруженным javascript фреймворком, который я использую, JsPsych. Должен ли я все еще go из-за проблем с настройкой nginx и конфигурацией wsgi?

edit: url_for() тоже не работает.

stimuli_in_static = "images/stimulus_img" + "1" + ".png"
stimuli_path = url_for('static', filename=stimuli_in_static)

Это все еще приводит к ошибке FileNotFound при вызове .savefig(),

FileNotFoundError: [Errno 2] No such file or directory: '/static/images/stimulus_img1.png'

Ответы [ 2 ]

1 голос
/ 01 февраля 2020

Рассмотрим flask документацию по организации flask приложений. Первая файловая структура, о которой они говорят, это шаблон «простого модуля», вторая структура файла - более сложный шаблон «пакета». https://flask.palletsprojects.com/en/1.1.x/tutorial/layout/

Первоначально я пытался реализовать шаблон "простого модуля". Содержимое app_dir было пустым. Вместо этого Neuroethics_Behavioral_Task был моим каталогом проекта, и на 1 уровне внутри него находились мои папки experiment.py, __init__.py, /static/ и /templates/.

Мне удалось обойти эти ошибки FileNotFound переключившись на шаблон организации на основе пакета для моего приложения flask. Таким образом, файловая структура теперь выглядит как

enter image description here

В этой схеме Neuroethics_Behavioral_Task по-прежнему является каталогом проекта. Но теперь есть app_dir, который содержит __init.py__, experiment.py, /static/ и /template/ в одном каталоге.

Когда я внес это изменение, я смог переписать большинство операторов save () в experiment.py примерно так:

# inside of generate_stim_for_trials()
csv_path = "app_dir/static/textdata/" + subj_id + "_data.csv"

Другими словами, все, что мне нужно было do использовал префикс app_dir.

Это сработало как на моей локальной машине, так и сработало, как только я отправил код в heroku. Я все еще использую этот шаблон организации, основанный на «пакетах», так как это то, что flask по крайней мере рекомендует для сложных приложений.

С другой стороны, V25 сделал очень важный вывод о том, что Heroku не предоставляет постоянного хранилища, что может полностью изменить ваши планы, если вы размещаете свои вещи на героку, как я. Я рад, что упомянул, что намеревался это сделать.

1 голос
/ 31 января 2020

Согласно моим комментариям, поскольку Heroku не поддерживает постоянное хранение в файловой системе, рекомендуется использовать S3. Для этого вам нужно будет plot.fig записать в буфер байтов ввода-вывода, а затем загрузить его на S3. Этот процесс описан в этой SO-теме .

В любом случае, вот ответ на материал файла stati c независимо от того, что допустимо в Heroku при условии, что файлы stati c находятся в Вопрос на самом деле является частью репо (файлы javascript и CSS, и т. д. c) ...


static_url_path предназначен для изменения URL-адреса, к которому относятся файлы stati c можно купить в. Это влияет на внешний URL-адрес путь, по которому доступны файлы stati c.

Со следующей конфигурацией stati c файлы доступны на http://example.com/apple/ независимо от того, где они находятся на файловая система:

app = Flask(__name__, static_url_path='apple')

Если вы хотите определить, где Flask просматривает файловую систему для файлов * stati c, используйте static_folder. В вашем случае приложение находится на app_dir/experiment.py, а ваша директория static находится на верхнем уровне. Поэтому вам нужно предоставить этот аргумент относительно того, где experiment.py:

app = Flask(__name__, static_folder='../static')

Лично я пытаюсь избавиться от всей этой головной боли и создать структуру каталогов, которая соответствует тому, что Flask ожидает по умолчанию:

project
├── experiment.py
├── static
│   └── some.js
└── templates
    └── some.html

Еще один совет относительно вашего кода:

def generate_stim_for_trials(trials, uid):
    import matplotlib
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import json
    import os

Это неэффективно, поскольку все эти операции импорта будут загружаться при каждом вызове функции.

Вы действительно хотите поместить эти импорты вверху файла, чтобы они загружались при запуске сервера приложений, и только потом.:

import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import json
import os

# ...

def generate_stim_for_trials(trials, uid):
    json_dict = {}
    # ...
...