使用多个表格中的选项创建瓶子表格 [英] Creating flask form with selects from more than one table

查看:242
本文介绍了使用多个表格中的选项创建瓶子表格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看过大量的教程,用flask和flash-wtf显示登录表单,但是没有多个选择框从数据库表值中填充。



是我正在尝试做的:

一个简单的注册表单:


名字





地址行1



地址行2



城市



状态标识(从状态库查询Id,状态填充)



国家ID(由国家图书馆查询国家ID填入)

示例代码或链接通过将不胜感激。

解决方案

我试图找到解释如何做到这一点并找不到一个。所以我要在这里写一个。这是我的工作方式,可能有更好的方法去做。



源代码



你可以在我的github账户上下载这个教程的完整源代码。我几乎从源代码复制和粘贴,但只是如果有一天github会死在这里我们就去。

$ h
$ b

需要配置我们的应用程序和数据库连接。在大多数情况下,
可能需要从配置文件中加载所有这些。



在本教程中,我们将使用基本的sqlalchemy测试数据库。

$ $ $ $ c $ app $ Flags(__ name__)
app.config ['SECRET_KEY'] ='Insert_random_string_here'

如果您想查看所有生成的SQL,请将此配置设置为True。

  app.config ['SQLALCHEMY_ECHO'] = False 
app.config ['SQLALCHEMY_DATABASE_URI'] ='sqlite://// tmp / test。 db'

WTForms配置字符串

  app.config ['WTF_CSRF_ENABLED'] = True 

重要。阅读更多关于他们在这里,
https://www.owasp.org/index。 php / Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
$ b

  app.config ['WTF_CSRF_SECRET_KEY'] ='Insert_random_string_here'
db = SQLAlchemy(app)


$ b

SQLALchemy模型



接下来,我们需要创建我们的模型类,这些类将在创建数据库
期间以及我们想要操作数据库时使用。这应该是
通常是它自己的单独的文件。

我从这里是重要的,就好像这是它自己的单独的文件。



正常情况下,您必须从应用程序导入数据库中导入类似
的数据。

  class RegisteredUser(db.Model):

在注册后加载并推送注册的用户数据

SQLalchemy用于加载的ORM表对象,将数据从
服务器内存范围推送到数据库作用域,并从数据库作用域中获取数据。

__tablename__ =RegisteredUser

#列在数据库中。
registered_id = db.Column(db.Integer,primary_key = True)
first_name = db.Column(db.String(70))
last_name = db.Column(db.String(70 ))
address_line_one = db.Column(db.String(256))
address_line_two = db.Column(db.String(256))
city = db.Column(db.String( 50))


现在我们要创建RegisteredUser
表的所有外键db.relationship部分允许我们轻松自动地
使用registeredUser连接其他表,如果您尝试访问State或Country表中的列,Join只会发生


有关使用SQLAlchemy的外键的更多信息,请转至to

state_id = db.Column(
db.Integer,
db.ForeignKey('State.state_id'),
nullable = False)
#为显示目的返回用户名称。
state_by = db.relationship(
'State',
foreign_keys = [state_id],
backref = db.backref('State',lazy ='dynamic'))
country_id = db.Column(
db.Integer,
db.ForeignKey('Country.country_id'),
nullable = False)
#retrives用户名显示目的。
country_by = db.relationship(
'Country',
foreign_keys = [country_id],)

#this是我选择的方法和函数样式行太长
def __init __(
self,
first_name,
last_name,
address_line_one,
address_line_two,
city,

country_id)

用于在python服务器范围内创建一个RegisteredUser对象

我们将每次调用这些init函数使用
RegisteredUser()作为函数调用,它会为我们创建一个SQLalchemy的ORM
对象

self.first_name = first_name
self .last_name = last_name
self.address_line_one = address_line_one
self.address_line_two = address_line_two
self.city = city
self.state_id = state_id
self.country_id = country_id


class State(db.Model):#pylint:disable-msg = R0903

保存数据库的状态名称以在注册页面中加载。

SQLalchemy ORM表对象,用于将数据从
服务器内存范围加载到数据库作用域,或从数据库作用域中将数据从其中推送。

__tablename__ =State

state_id = db.Column(db.Integer,primary_key = True)
state_name = db.Column(db。 String(10),unique = True)
$ b $ def __init __(self,state_name):

用于在python服务器范围中创建一个State对象

self.state_name = state_name


class国家(db.Model):#pylint:disable-msg = R0903

在注册页面中保存数据库的国家名称。

SQLalchemy ORM表对象,用于将数据从
服务器内存范围加载到数据库作用域,或从数据库作用域中将数据从其中推送。

__tablename__ =国家

country_id = db.Column(db.Integer,primary_key = True)
#longest国家长度目前是163个字母
country_name = db.Column(db.String(256),unique = True)
$ b $ def __init __(self,country_name):

用于在python服务器范围内创建一个Country对象

self.country_name = country_name

$ b def create_example_data():

生成本教程后面将要使用的所有演示数据。这是
,我们如何使用我们的ORM对象将数据推送到数据库。

注意:在文件的最底部调用create_example_data。

#创建一堆状态模型并将它们添加到当前会话中
#注意,这不会将行添加到数据库中,我们将在稍后提交。 b $ b state_model = state(state_name =WA)
db.session.add(state_model)
state_model = State(state_name =AK)
db.session.add(state_model )
state_model = State(state_name =LA)
db.session.add(state_model)
#正常情况下,我从非常大的CVS或json文件加载这个数据并运行这个
$#
$ b country_model = Country(USA)
db.session.add(country_model)
country_model = Country(Some_Made_Up_Place)
db.session.add(country_model)
#有趣的注意事项:从添加
#的时候起,将会以相反的顺序进行提交
try:
db。 session.commit()
,除了IntegrityError,作为e:
print(试图将数据推送到数据库,不是第一次运行。 ng



WT​​Form



现在我们要创建我们的WTForms对象。这些将从数据库中获得数据
,然后我们将它们传递给我们的模板文件
,在这里我们将呈现它们。



我从这里很重要,就好像这是它自己的单独文件。

pre $ import $ wtforms $ b $ import wtforms.validators作为validators
from flask.ext.wtf import Form
$ b $ class RegistrationForm(Form):

这个Form类包含所有构成的文件我们的注册
表格。

#获取所有的文本字段。
first_name_field = wtforms.TextField(
label =First Name,
validators = [validators.Length(max = 70),validators.Required()])
last_name_field = wtforms.TextField(
label =Last Name,
validators = [validators.Length(max = 70),validators.Required()])
address_line_one_field = wtforms.TextField(
label =Address,
validators = [validators.Length(max = 256),validators.Required()])
address_line_two_field = wtforms.TextField(
label =Second Address ,
validators = [validators.Length(max = 256),])
city_field = wtforms.TextField(
label =City,
validators = [validators.Length (max = 50),validators.Required()])
#现在让我们设置所有的选择字段。
state_select_field = wtforms.SelectField(label =State,coerce = int)
country_select_field = wtforms.SelectField(label =Country,coerce = int)



视图



导入烧瓶

  def populate_form_choices(registration_form):

从数据库中提取选项以填充我们的选择字段。

states = state.query.all()
countries = Country.query.all()
state_names = []
用于状态状态:
state_names.append(state.state_name )
#choices需要以枚举列表的形式出现
#example [('cpp','C ++'),('py','Python'),('text ''''纯文本')]
state_choices = list(enumerate(state_names))
country_names = []
国家的国家:
country_names.append(country.country_name)
country_choices = list(enumerate(country_names))
#现在我们已经建立了我们的选择,我们需要设置它们。
registration_form.state_select_field.choices = state_choices
registration_form.country_select_field.choices = country_choices

@ app.route('/',methods = ['GET','POST'] )
def demo():

这将会显示一个模板,显示所有的表单对象,如果它是
a Get请求,如果使用正在尝试Post这个视图会将
的数据推送到数据库。

#这个部分有点难以理解。 flask-wtforms在每次创建表单对象时都会执行隐式
#call。它尝试查看在这个会话中是否有一个
#request.form对象,如果有,它将
#请求中的数据添加到表单对象中。
registration_form = RegistrationForm()
#在我们试图验证我们的表单数据之前,我们必须设置我们选择的
#字段选项。这只是你需要做的事情,如果你要去
#use WTForms,即使它看起来很傻。
populate_form_choices(registration_form)
#这意味着如果我们不发送一个post请求,那么这个if语句
#将永远失败。所以我们继续正常渲染模板。
如果flask.request.method =='POST'和registration_form.validate():
#如果我们正在发送一个请求,并且我们通过了所有的验证器,那么
#创建一个注册用户模型并将该模型推送到数据库。
registered_user = RegisteredUser(
first_name = registration_form.data ['first_name_field'],
last_name = registration_form.data ['last_name_field'],
address_line_one = registration_form.data ['address_line_one_field '],
address_line_two = registration_form.data ['address_line_two_field'],
city = registration_form.data ['city_field'],
state_id = registration_form.data ['state_select_field'],
country_id = registration_form.data ['country_select_field'],)
db.session.add(registered_user)
db.session.commit()
return flask.render_template(
template_name_or_list ='success.html',
registration_form = registration_form,)
return flask.render_template(
template_name_or_list ='registration.html',
registration_form = registration_form,)



runserver.py



最后,这仅用于开发目的。我通常有一个名为RunServer.py的
文件。为了实际上交付你的应用程序,你应该在某种类型的web服务器(Apache,Nginix,Heroku)后面运行


  if __name__ =='__main__':
db.create_all()
create_example_data()
app.run(debug = True)
pre>

模板



在macros.html



<$ (字段(** kwargs)){$ field_label}
< dd> {{字段(** kwargs) | safe}}
{%if field.errors%}
< ul class = errors>
{%for field.errors%}
< li> {{error}}< / li>
{%endfor%}
< / ul>
{%endif%}
< / dd>
{%endmacro%}

{%macro render_data(field)%}
< dt> {{field.label}}
< dd> { {field.data | safe}}
{%if field.errors%}
< ul class = errors>
{%for field.errors%}
< li> {{error}}< / li>
{%endfor%}
< / ul>
{%endif%}
< / dd>
{%endmacro%}

在registration.html中

  {%frommacros.htmlimport render_field%} 
< form method = post action =/>
{{registration_form.hidden_​​tag()}}
< dl>
{{render_field(registration_form.first_name_field)}}
{{render_field(registration_form.last_name_field)}}
{{render_field(registration_form.address_line_one_field)}}
{{render_field registration_form.address_line_two_field)}}
{{render_field(registration_form.city_field)}}
{{render_field(registration_form.state_select_field)}}
{{render_field(registration_form.country_select_field)}}
< / dl>
< p><输入类型=提交值=注册>
< / form>

最后,在success.html

  {%frommacros.htmlimport render_data%} 
< h1>这个数据被保存到数据库! < / H1>
< form method = post action =/>
{{registration_form.hidden_​​tag()}}
< dl>
{{render_data(registration_form.first_name_field)}}
{{render_data(registration_form.last_name_field)}}
{{render_data(registration_form.address_line_one_field)}}
{{render_data registration_form.address_line_two_field)}}
{{render_data(registration_form.city_field)}}
{{render_data(registration_form.state_select_field)}}
{{render_data(registration_form.country_select_field)}}
< / dl>
< p><输入类型=提交值=注册>
< / form>


I have seen a large number of tutorials that show login forms with flask and flash-wtf but none where multiple select boxes are populated from database table values.

This is what I am trying to do:

A simple registration form:

First name

Last name

Address Line 1

Address Line 2

City

State Id (populated from states library query of Id,state)

Country Id (populated from countries library query of country, id)

Sample code or a link to a walk through would be greatly appreciated.

解决方案

I tried to find a explanation for how to do this and couldn't find one. So I'm going to write one here. This is how I do things, there's probably better ways to go about it.

Source Code

You can download the full source code for this tutorial on my github account. I'm pretty much copying and pasting from the source code, but just in case github dies some day here we go.

Configuration

Need to configure our application and the database connection. In most cases you probably want to load all of this from a configuration file.

In this tutorial we're just going to use a basic sqlalchemy test database.

app = Flask(__name__)
app.config['SECRET_KEY'] = 'Insert_random_string_here'

Set this configuration to True if you want to see all of the SQL generated.

app.config['SQLALCHEMY_ECHO'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'

WTForms configuration strings

app.config['WTF_CSRF_ENABLED'] = True

CSRF tokens are important. Read more about them here, https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet

app.config['WTF_CSRF_SECRET_KEY'] = 'Insert_random_string_here'
db = SQLAlchemy(app)

SQLALchemy Model

Next we need to create our model classes that will be used during the creation of the database and also when we want to manipulate the database. This should normally be it's own seperate file.

I'm importanting from here as if this was it's own seperate file.

Normally you'd have to import doing something like from application import db

class RegisteredUser(db.Model):
    """
    loads and pushes registered user data after they have signed up.

    SQLalchemy ORM table object which is used to load, and push, data from the
    server memory scope to, and from, the database scope.
    """
    __tablename__ = "RegisteredUser"

    #all of the columns in the database.
    registered_id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(70))
    last_name = db.Column(db.String(70))
    address_line_one = db.Column(db.String(256))
    address_line_two = db.Column(db.String(256))
    city = db.Column(db.String(50))

    """
    Now we're going to create all of the foreign keys for the RegisteredUser
    table. The db.relationship section allows us to easily and automatically
    join the other tables with registeredUser. The Join will only take place
    if you attempt to access columns from the State or country table.

    For more on Foreign keys using SQLAlchemy go to
    """
    state_id = db.Column(
            db.Integer,
            db.ForeignKey('State.state_id'),
            nullable=False)
    #retrives the users name for display purposes.
    state_by = db.relationship(
            'State',
            foreign_keys=[state_id],
            backref=db.backref('State', lazy='dynamic'))
    country_id = db.Column(
            db.Integer,
            db.ForeignKey('Country.country_id'),
            nullable=False)
    #retrives the users name for display purposes.
    country_by = db.relationship(
            'Country',
            foreign_keys=[country_id],)

    #this is the method and function style I've chosen when lines are too long
    def __init__(
            self,
            first_name,
            last_name,
            address_line_one,
            address_line_two,
            city,
            state_id,
            country_id):
        """
        Used to create a RegisteredUser object in the python server scope

        We will be calling these init functions every time we use
        RegisteredUser() as a 'function' call. It will create a SQLalchemy ORM
        object for us.
        """
        self.first_name = first_name
        self.last_name = last_name
        self.address_line_one = address_line_one
        self.address_line_two = address_line_two
        self.city = city
        self.state_id = state_id
        self.country_id = country_id


class State(db.Model):  # pylint: disable-msg=R0903
    """
    Holds State names for the database to load during the registration page.

    SQLalchemy ORM table object which is used to load, and push, data from the
    server memory scope to, and from, the database scope.
    """
    __tablename__ = "State"

    state_id = db.Column(db.Integer, primary_key=True)
    state_name = db.Column(db.String(10), unique=True)

    def __init__(self, state_name):
        """
        Used to create a State object in the python server scope
        """
        self.state_name = state_name


class Country(db.Model):  # pylint: disable-msg=R0903
    """
    Holds Country names for the database to load during the registration page.

    SQLalchemy ORM table object which is used to load, and push, data from the
    server memory scope to, and from, the database scope.
    """
    __tablename__ = "Country"

    country_id = db.Column(db.Integer, primary_key=True)
    #longest country length is currently 163 letters
    country_name = db.Column(db.String(256), unique=True)

    def __init__(self, country_name):
        """
        Used to create a Country object in the python server scope
        """
        self.country_name = country_name


def create_example_data():
    """
    Generates all of the demo data to be used later in the tutorial. This is
    how we can use our ORM objects to push data to the database.

    NOTE: create_example_data is called at the very bottom of the file.
    """
    #Create a bunch of state models and add them to the current session.
    #Note, this does not add rows to the database. We'll commit them later.
    state_model = State(state_name="WA")
    db.session.add(state_model)
    state_model = State(state_name="AK")
    db.session.add(state_model)
    state_model = State(state_name="LA")
    db.session.add(state_model)
    #Normally I load this data from very large CVS or json files and run This
    #sort of thing through a for loop.

    country_model = Country("USA")
    db.session.add(country_model)
    country_model = Country("Some_Made_Up_Place")
    db.session.add(country_model)
    # Interesting Note: things will be commited in reverse order from when they
    # were added.
    try:
        db.session.commit()
    except IntegrityError as e:
        print("attempted to push data to database. Not first run. continuing\
                as normal")

WTForm

Now we're going to make our WTForms objects. These will have the data aquired from the database placed on them, then we will pass them to our template files where we will render them.

I'm importanting from here as if this was it's own seperate file.

import wtforms
import wtforms.validators as validators
from flask.ext.wtf import Form

class RegistrationForm(Form):
    """
    This Form class contains all of the fileds that make up our registration
    Form. 
    """
    #Get all of the text fields out of the way.
    first_name_field = wtforms.TextField(
            label="First Name",
            validators=[validators.Length(max=70), validators.Required()])
    last_name_field = wtforms.TextField(
            label="Last Name",
            validators=[validators.Length(max=70), validators.Required()])
    address_line_one_field = wtforms.TextField(
            label="Address",
            validators=[validators.Length(max=256), validators.Required()])
    address_line_two_field = wtforms.TextField(
            label="Second Address",
            validators=[validators.Length(max=256), ])
    city_field = wtforms.TextField(
            label="City",
            validators=[validators.Length(max=50), validators.Required()])
    # Now let's set all of our select fields.
    state_select_field = wtforms.SelectField(label="State", coerce=int)
    country_select_field = wtforms.SelectField(label="Country", coerce=int)

Views

import flask

def populate_form_choices(registration_form):
    """
    Pulls choices from the database to populate our select fields.
    """
    states = State.query.all()
    countries = Country.query.all()
    state_names = []
    for state in states:
        state_names.append(state.state_name)
    #choices need to come in the form of a list comprised of enumerated lists
    #example [('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')]
    state_choices = list(enumerate(state_names))
    country_names = []
    for country in countries:
        country_names.append(country.country_name)
    country_choices = list(enumerate(country_names))
    #now that we've built our choices, we need to set them.
    registration_form.state_select_field.choices = state_choices
    registration_form.country_select_field.choices = country_choices

@app.route('/', methods=['GET', 'POST'])
def demonstration():
    """
    This will render a template that displays all of the form objects if it's
    a Get request. If the use is attempting to Post then this view will push
    the data to the database.
    """
    #this parts a little hard to understand. flask-wtforms does an implicit
    #call each time you create a form object. It attempts to see if there's a
    #request.form object in this session and if there is it adds the data from
    #the request to the form object.
    registration_form = RegistrationForm()
    #Before we attempt to validate our form data we have to set our select
    #field choices. This is just something you need to do if you're going to 
    #use WTForms, even if it seems silly.
    populate_form_choices(registration_form)
    #This means that if we're not sending a post request then this if statement
    #will always fail. So then we just move on to render the template normally.
    if flask.request.method == 'POST' and registration_form.validate():
        #If we're making a post request and we passed all the validators then
        #create a registered user model and push that model to the database.
        registered_user = RegisteredUser(
            first_name=registration_form.data['first_name_field'],
            last_name=registration_form.data['last_name_field'],
            address_line_one=registration_form.data['address_line_one_field'],
            address_line_two=registration_form.data['address_line_two_field'],
            city=registration_form.data['city_field'],
            state_id=registration_form.data['state_select_field'],
            country_id=registration_form.data['country_select_field'],)
        db.session.add(registered_user)
        db.session.commit()
        return flask.render_template(
            template_name_or_list='success.html',
            registration_form=registration_form,)
    return flask.render_template(
            template_name_or_list='registration.html',
            registration_form=registration_form,)

runserver.py

Finally, this is for development purposes only. I normally have this in a file called RunServer.py. For actually delivering your application you should run behind a web server of some kind (Apache, Nginix, Heroku).

if __name__ == '__main__':
    db.create_all()
    create_example_data()
    app.run(debug=True)

Templates

in macros.html

{% macro render_field(field) %}
  <dt>{{ field.label }}
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}

{% macro render_data(field) %}
  <dt>{{ field.label }}
  <dd>{{ field.data|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}

In registration.html

{% from "macros.html" import render_field %}
<form method=post action="/">
    {{registration_form.hidden_tag()}}
  <dl>
    {{ render_field(registration_form.first_name_field) }}
    {{ render_field(registration_form.last_name_field) }}
    {{ render_field(registration_form.address_line_one_field) }}
    {{ render_field(registration_form.address_line_two_field) }}
    {{ render_field(registration_form.city_field) }}
    {{ render_field(registration_form.state_select_field) }}
    {{ render_field(registration_form.country_select_field) }}
  </dl>
  <p><input type=submit value=Register>
</form>

Finally, in success.html

{% from "macros.html" import render_data %}
<h1> This data was saved to the database! </h1>
<form method=post action="/">
    {{registration_form.hidden_tag()}}
  <dl>
    {{ render_data(registration_form.first_name_field) }}
    {{ render_data(registration_form.last_name_field) }}
    {{ render_data(registration_form.address_line_one_field) }}
    {{ render_data(registration_form.address_line_two_field) }}
    {{ render_data(registration_form.city_field) }}
    {{ render_data(registration_form.state_select_field) }}
    {{ render_data(registration_form.country_select_field) }}
  </dl>
  <p><input type=submit value=Register>
</form>

这篇关于使用多个表格中的选项创建瓶子表格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆