提交另一个表单后,隐藏表单不会保留默认值

时间:2014-04-25 05:44:39

标签: python flask wtforms flask-wtforms

我在两个表单上有一个隐藏字段,它们在同一页面上呈现。此隐藏字段的default设置为true。我使用这个隐藏字段来测试已提交的表单;已提交的表单会将该字段的值设置为true,而另一个表单不会设置该字段。

但是,在提交一个表单(每个表单的操作是同一页面)后,另一个字段的隐藏字段的value将设置为空白。例如:

首先,渲染的字段是这样的(当然,每个字段都以它们自己的形式):

<input id="upload-submitted" name="upload-submitted" type="hidden" value="true">

...other stuff...

<input id="process-submitted" name="process-submitted" type="hidden" value="true">

如果我提交第一个表单,它会将我带到同一页面,并且字段呈现如下:

<input id="upload-submitted" name="upload-submitted" type="hidden" value="true">

...other stuff...

<input id="process-submitted" name="process-submitted" type="hidden" value="">

如果我提交第二个表单,那么除了第一个input没有value且第二个表单具有正确的值之外,一切都是相同的。

问题,具体而言,似乎是在提交后,data字段的submitted字段为空,如果该表单未提交 - 这是有道理的,&#39;什么让我确定哪个表格真的提交了。但是,字段不会重置以在HTML中呈现 - 这意味着我无法在提交另一个表单后提交其他表单。

如何防止这种情况发生?有没有更好的方法来确定在页面上提交了哪两种表单?

这是我的forms.py:

class HiddenSubmitted(object):
    """A mixin to provide a hidden field called "submitted" which has a default
    value of "true".
    """

    submitted = HiddenField(default="true")

class ButtonWidget(object):
    """A widget to conveniently display buttons.
    """
    def __call__(self, field, **kwargs):
        if field.name is not None:
            kwargs.setdefault('name', field.name)
        if field.value is not None:
            kwargs.setdefault('value', field.value)
        kwargs.setdefault('type', "submit")
        return HTMLString('<button %s>%s</button>' % (
            html_params(**kwargs),
            escape(field._value())
            ))

class ButtonField(Field):
    """A field to conveniently use buttons in flask forms.
    """
    widget = ButtonWidget()

    def __init__(self, text=None, name=None, value=None, **kwargs):
        super(ButtonField, self).__init__(**kwargs)
        self.text = text
        self.value = value
        if name is not None:
            self.name = name

    def _value(self):
        return str(self.text)

class MultiCheckboxField(SelectMultipleField):
    """
    A multiple-select, except displays a list of checkboxes.

    Iterating the field will produce subfields, allowing custom rendering of
    the enclosed checkbox fields.
    """
    widget = ListWidget(prefix_label=False)
    option_widget = CheckboxInput()

class DocumentUploadForm(Form, HiddenSubmitted):
    """This is a form to upload files to the server. It handles both XML
    and JSON files, and is used by the document_upload view.
    """

    upload_button = ButtonField(text="Upload", name="action")

    uploaded_file = FileField("File", validators=[
        FileRequired("You must select a file"),
        FileAllowed(app.config["ALLOWED_EXTENSIONS"])
        ])

class ProcessForm(Form, HiddenSubmitted):
    """
    Allows the user to select which objects should be
    processed/deleted/whatever.
    """

    PROCESS = "0"
    DELETE = "-1"

    files = MultiCheckboxField("Select", coerce=int, validators=[
        Required("You must select at least one item from the table.")
        ])
    process_button = ButtonField("Process", name="action", value=PROCESS)
    delete_button = ButtonField("Delete",  name="action", value=DELETE)

    def validate_files(form, field):
        if form.process_button.data == form.PROCESS:
            # There must be a JSON file selected
            struc_count = 0
            doc_count = 0
            for selected_file in form.files.data:
                unit = session.query(Unit).\
                    filter(Unit.id == selected_file).one()
                ext = os.path.splitext(unit.path)[1][1:]
                if ext in app.config["STRUCTURE_EXTENSION"]:
                    struc_count += 1
                else:
                    doc_count += 1
            if struc_count is not 1:
                raise ValidationError("Must be exactly one structure file")
            if doc_count < 1:
                raise ValidationError("At least one document must be selected")

和我的views.py:

def really_submitted(form):
    """ WTForms can be really finnicky when it comes to checking if a form
    has actually been submitted, so this method runs validate_on_submit()
    on the given form and checks if its "submitted" field has any data. Useful
    for pages that have two forms on them.

    :arg Form form: A form to check for submission.
    :returns boolean: True if submitted, false otherwise.
    """

    if form.submitted.data == "true":
        return form.validate_on_submit()
    return False

@app.route(app.config["PROJECT_ROUTE"] + "<project_id>",
    methods=["GET", "POST"])
def project_show(project_id):
    """
    Show the files contained in a specific project. It also allows the user
    to upload a new document, much like projects().

    :param int project_id: The ID of the desired project.
    """

    upload_form = forms.DocumentUploadForm(prefix="upload")
    process_form = forms.ProcessForm(prefix="process")

    # If I put a print statement here, I find that by now one field has no data for the hidden field, which makes sense

    # The template needs access to the ID of each file and its filename.
    process_form.files.choices = []
    file_objects = #blah blah database stuff

    # First handle the actions of the upload form
    if shortcuts.really_submitted(upload_form):
        uploaded_files = request.files.getlist("upload-uploaded_file")
        for uploaded_file in uploaded_files:
            filename = secure_filename(uploaded_file.filename)
            dest_path = os.path.join(app.config["UPLOAD_DIR"],
                str(project_id), filename)
            uploaded_file.save(dest_path)
            unit = Unit(path=dest_path, project=project_id)
            unit.save()
            process_form.files.choices.append((unit.id,
                os.path.split(dest_path)[1]))

    # Or what happened with the document selection
    elif shortcuts.really_submitted(process_form):
        files = request.form.getlist("process-files")
        if request.form["action"] == process_form.DELETE:
            # Delete every selected file, its database record, and item in
            # the listing
            for file_id in files:
                file_model = session.query(Unit).\
                    filter(Unit.id == file_id).one()
                file_name = os.path.split(file_model.path)[1]
                os.remove(file_model.path)
                session.delete(file_model)
                session.commit()
                process_form.files.choices.remove((int(file_id), file_name))

        elif request.form["action"] == process_form.PROCESS:
            #TODO: process these files.
            pass

    return render_template("document_list.html",
        upload_form=upload_form,
        process_form=process_form)

1 个答案:

答案 0 :(得分:0)

在呈现模板之前,至少有一个解决方案:

process_button.submitted.data = "true"
delete_button.submitted.data = "true"