lph*_*lph 5 html python sqlalchemy nginx flask
首先,我对大部分内容都很陌生。我在远程(Linode)Ubuntu 服务器上运行 Flask 应用程序。该应用程序与 Gunicorn 和 Nginx 一起提供。
在最近的更新之后,当我提交表单时,重定向开始表现得很奇怪。
具体来说,我重定向到的 url 如下所示: mywebsite.com%2Cmywebsite.com/my/expected/redirect
(要明确,mywebsite.com是基本 url。在提交表单时,我希望被重定向到mywebsite.com/my/expected/redirect,但是我被重定向到mywebsite.com%2Cmywebsite.com/my/expected /重定向)
这仅发生在服务器上。在我的本地主机上,不存在此问题。
这仅在我提交表单(通过 wtf 表单)时发生。提交表单并获取虚假 url 后,如果我手动输入预期的 url,我会得到预期的页面 - 即表单提交成功的确认闪存消息,并且数据库包含通过表单提交的新数据。所以我猜 db.commit() 正在工作。
并非所有链接都已损坏,我可以成功地从一个页面导航到另一个页面。例如,我可以通过站点范围的 html 头文件中的这个链接从我的导航栏导航回家。
href="{{ url_for('home') }}"
Run Code Online (Sandbox Code Playgroud)
我可以通过这些按钮导航到其他页面(我知道我应该使用 url_for 而不是硬链接)
<button class="w3-bar-item w3-button w3-black" onclick="window.location.href = '/recipes';">Recipes</button>
<button class="w3-bar-item w3-button w3-yellow" onclick="window.location.href = '/newrecipe';">Add New Recipe</button>
Run Code Online (Sandbox Code Playgroud)
有三种不同的形式都会产生此错误。在下面你会看到它们是NewRecipe、NewBoilAddition、StartBoilTimer
我曾尝试将 _external=True 添加到 url_for 重定向,但是由于内部服务器错误而失败
从研究中,我知道 %2C 是一个未转义的逗号,因此我怀疑正在将 URL 列表传递给重定向,或者我遗漏了一些其他编码问题。我特别困惑,因为这个错误才刚刚出现,当时所有这些代码都可以正常工作。我已尝试回滚到我确定此错误不存在的提交,并且该错误在该提交中是可重现的。这让我怀疑是 gunicorn/nginx 负责,因为应用程序代码本身很好。
我超级困惑。任何想法表示赞赏!
初始化.py
from flask import Flask, render_template, request, g, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.hybrid import hybrid_property
from forms import NewRecipe, NewBoilAddition, StartBoilTimer
# import sqlite3
import pandas as pd
import json
import sys
import os
import config
from datetime import datetime, timedelta
from flask_gtts import gtts
app = Flask(__name__)
app.config['SECRET_KEY'] = 'xxx'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
bevvys = db.relationship('Bevvy_list', backref='brewer', lazy=True)
def __repr__(self):
return f"User('{self.id}', '{self.username}')"
class Bevvy_list(db.Model):
__tablename__ = 'bevvy_list'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True, nullable=False) #Optimise length of this string
style = db.Column(db.String(60), nullable=False) #TODO add in validated field, linking to style guide
abbreviation = db.Column(db.String(60), nullable=False)
iteration = db.Column(db.Integer, nullable=False)
iteration_of = db.Column(db.Integer, nullable=False) #id of parent beer, if iteration = 1 then this equals self.id
batch_size = db.Column(db.Integer, nullable=False)
brewday_date = db.Column(db.DateTime, nullable=False)
url = db.Column(db.String(20), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
boils = db.relationship('Boil', backref='bevvy', lazy=True)
def __repr__(self):
return f"Bevvy_list('{self.id}', '{self.name}')"
class Boil(db.Model):
__tablename__ = 'boil'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(100), unique=False, nullable=False)
time = db.Column(db.Integer, unique=False, nullable=False)
end_datetime = db.Column(db.DateTime, unique=False, nullable=True)
brew_id = db.Column(db.Integer, db.ForeignKey('bevvy_list.id'), nullable=False)
def __repr__(self):
return f"Boil('{self.id}', '{self.description}', '{self.time}', '{self.end_datetime}')"
##########################################
# Build app
##########################################
# home navigation
@app.route("/")
def index():
return render_template("home.html")
@app.route("/recipes")
def recipes():
bevs_data = Bevvy_list.query.all()
return render_template("recipes.html", bevs_data=bevs_data)
@app.route("/edit/<int:recipe_id>", methods=['GET', 'POST'])
def edit(recipe_id):
form = NewBoilAddition(brew_id=recipe_id)
if form.validate_on_submit():
boil_addition = Boil(description=form.description.data, \
time=form.time.data, \
brew_id=recipe_id,\
end_datetime=None)
db.session.add(boil_addition)
db.session.commit()
flash(f'Boil Addition {form.description.data} successfully added ', 'success_boil')
return redirect(url_for('edit', recipe_id=recipe_id))
start_timer_form = StartBoilTimer()
boil_additions = Boil.query.filter(Boil.brew_id == recipe_id).all()
if start_timer_form.validate_on_submit():
end_all_timers = datetime.now() + timedelta(minutes=max_value(Boil.query.filter(Boil.brew_id == recipe_id).with_entities(Boil.time).all()))
for addition in boil_additions:
addition_end_datetime = end_all_timers - timedelta(minutes=addition.time)
db.session.query(Boil).filter(Boil.id == addition.id).update({'end_datetime':addition_end_datetime})
db.session.commit()
return redirect(url_for('edit', recipe_id=recipe_id))
recipe = Bevvy_list.query.filter(Bevvy_list.id == recipe_id).all()
return render_template("edit.html", recipe=recipe[0], form=form, \
boil_additions=boil_additions, start_timer_form=start_timer_form)
# add a new recipe
@app.route("/newrecipe", methods=['GET', 'POST'])
def new_recipe():
form = NewRecipe()
recipe_list = Bevvy_list.query.all()
list_recipes =[]
for recipe in recipe_list:
row = [recipe.id, recipe.name, recipe.brewday_date.strftime("%-d %b %y")]
list_recipes.append(row)
if form.validate_on_submit():
recipe = Bevvy_list(name=form.name.data, style=form.style.data, abbreviation=form.abbreviation.data, iteration=form.iteration.data, \
iteration_of=form.iteration_of.data, batch_size=form.batch_size.data, brewday_date=form.brewday_date.data, user_id=form.user_id.data)
db.session.add(recipe)
href = "/edit/" + str(db.session.query(Bevvy_list).order_by(Bevvy_list.id.desc()).first().id)
db.session.query(Bevvy_list).order_by(Bevvy_list.id.desc()).first().url = href
db.session.commit()
flash(f'Recipe for {form.name.data} successfully added', 'success')
return redirect(url_for('home'), _external=True)
return render_template("new_recipe.html", title="New Recipe", form=form, modal=list_recipes)
if __name__ == "__main__":
app.run()
Run Code Online (Sandbox Code Playgroud)
new_recipe.html
<!DOCTYPE html>
{% extends "layouts.html" %}
{% block content %}
<!-- Modal -->
<div class="modal fade" id="exampleModalLong" tabindex="-1" role="dialog" aria-labelledby="exampleModalLongTitle" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">All Recipes</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<table class="table table-striped table-hover table-sm">
<thead class="thead-dark">
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Brewday Date</th>
</tr>
</thead>
<tbody>
{% for list in modal %}
<tr>
{% for item in list %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Add New Recipe</legend>
<div class="form-group">
{{ form.name.label(class="form-control-label") }}
{% if form.name.errors %}
{{ form.name(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.name.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.name(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.style.label(class="form-control-label") }}
{% if form.style.errors %}
{{ form.style(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.style.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.style(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.abbreviation.label(class="form-control-label") }}
{% if form.abbreviation.errors %}
{{ form.abbreviation(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.abbreviation.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.abbreviation(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.iteration.label(class="form-control-label") }}
{% if form.iteration.errors %}
{{ form.iteration(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.iteration.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.iteration(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.iteration_of.label(class="form-control-label") }}
{% if form.iteration_of.errors %}
{{ form.iteration_of(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.iteration_of.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.iteration_of(class="form-control form-control-lg") }}
{% endif %}
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModalLong">
Show recipe list for reference
</button>
</div>
<div class="form-group">
{{ form.batch_size.label(class="form-control-label") }}
{% if form.batch_size.errors %}
{{ form.batch_size(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.batch_size.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.batch_size(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.brewday_date.label(class="form-control-label") }}
{% if form.brewday_date.errors %}
{{ form.brewday_date(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.brewday_date.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.brewday_date(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-check">
{% if form.user_id.errors %}
{% for subfield in form.user_id %}
<table>
<td>
<tr>{{ subfield(class="form-check-input is-invalid") }}</tr>
<tr>{{ subfield.label(class="form-check-label") }}</tr><br>
</td>
</table>
{% endfor %}
<div class="invalid-feedback">
{% for error in form.user_id.errors%}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{% for subfield in form.user_id %}
<table>
<td>
<tr>{{ subfield(class="form-check-input") }}</tr>
<tr>{{ subfield.label(class="form-check-label") }}</tr><br>
</td>
</table>
{% endfor %}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
Run Code Online (Sandbox Code Playgroud)
编辑:
/etc/systemd/system/MYAPP.service
[Unit]
Description=Gunicorn instance to serve zym_app
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/home/lph/zym_app/zym
Environment="PATH=/home/lph/zym_app/zym/zym_app_env/bin"
ExecStart=/home/lph/zym_app/zym/zym_app_env/bin/gunicorn --workers 3 --bind unix:zym_app.sock -m 007 wsgi:app
[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)
和 /etc/nginx/sites-available/MYAPP
server {
listen 80;
server_name mywebsite.com www.mywebsite.com;
location / {
include proxy_params;
proxy_pass http://unix:/home/lph/zym_app/zym/zym_app.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Run Code Online (Sandbox Code Playgroud)
编辑 2:看起来我错过了 2 并直接进入了 3 ..
编辑 3:解决了这个问题后,我现在认为这是 nginx 配置的问题。我在服务器上测试了烧瓶应用程序
gunicorn --bind 0.0.0.0:5000 wsgi:app
Run Code Online (Sandbox Code Playgroud)
这解决了观察到的重定向问题,应用程序按预期工作。因此,烧瓶和 gunicorn 部分工作正常 - 只留下 nginx。
我还从 .validate_on_submit() 条件中删除了重定向行,这也解决了通过 nginx 运行时的问题。新的表单数据已成功发布到数据库,但是由于缺少重定向,我不得不在提交后手动刷新页面。
这是我的 nginx 配置文件。/etc/nginx/sites-available/zym_app
server {
listen 80;
server_name mywebsite.com www.mywebsite.com;
location / {
include proxy_params;
proxy_pass http://unix:/home/ZYM/zym/zym_app.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_headers_hash_max_size 512;
proxy_headers_hash_bucket_size 128;
}
}
Run Code Online (Sandbox Code Playgroud)
编辑 4:在尝试了许多 nginx 配置参数更改后,我注释掉了有问题的重定向行,现在当我单击导航栏链接到主页时出现问题。这是该链接中的 html。
href="{{ url_for('home') }}"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
174 次 |
| 最近记录: |