[Tourist Attraction Project] - Having trouble with one section

Jinja2 Templates and Forms.

Here is the project Tourist Attractions:
https://www.codecademy.com/paths/build-python-web-apps-flask/tracks/introduction-to-flask/modules/flask-templates-and-forms/projects/tourist-attractions-app

I have created the following code:

@app.route("/add_location", methods=[“POST”])
def add_location():
AddLocationForm = add_form()
if AddLocationForm.validate_on_submit():
name = add_form.name.data
description = add_form.description.data
category = add_form.category.data
visit.add(name, description, category)
return redirect(url_for(“locations”, category=category))

I am getting a NameError stating that add_form is not defined. I have completed the project and was going to just move on but I figured I’d play with it a little first. I tried to use the form and submit a location but got the NameError. Am I doing something wrong? I’d like to correct it so I can feel that I REALLY got the project completed, if you know what I mean. Any help would be greatly appreciated!

Hi there,

Did you define an add_form() function earlier in the code? If you didn’t that will throw an error. Maybe you did but you didn’t import the py file where you define your forms.

In the future, remember to format your code with the </> button before pasting as it’ll make it easier for people to troubleshoot.

The project instructions are:

Assign an AddLocationForm() instance to a variable add_form . Call validate_on_submit() using the instance. You can use the following syntax:

my_instance = MyForm()
  if my_instance.validate_on_submit():
    # more code

So my code looks like this:

@app.route("/add_location", methods=["POST"])
def add_location():
  ## Validate and collect the form data
  AddLocationForm = add_form()
  if AddLocationForm.validate_on_submit(): 
      name = add_form.name.data
      description = add_form.description.data
      category = add_form.category.data
      visit.add(name, description, category)

The question you asked makes total sense because after looking around I see no other instance of add_form anywhere in any of the files. I’m way past this project now but it is still bugging me since I feel like it was never fully completed.

I think roughly it should be

def AddLocationForm(FlaskForm):
  #code that defines the form

@app.route("/add_location", methods=["POST"])
def add_location():
  add_form = AddLocationForm()
  if add_form.validate_on_submit():
    #etc

Notice how the FlaskForm needs to be defined outside (and usually it’s useful to create a separate .py file just to hold your forms – with some exceptions).

You also got the names mixed by the instructions (the variable should be add_form and you have it the other way around).

1 Like

I actually tried that because logic would dictate for me to try to move things around and see where I screwed up and yet I got a similar error, but now with a different name.

What’s happening now? What’s the whole code look like? …maybe we can get to the bottom of it.

I made the change and reversed what you said to reverse and I get the error:

TypeError: validate_on_submit() missing 1 required positional argument: ‘self’

Here is my app.py:

from flask import Flask, render_template, request, redirect, url_for
from locations import Locations
from forms import AddLocationForm

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

visit = Locations()
categories = {"recommended": "Recommended", "tovisit": "Places To Go", "visited": "Visited!!!", }

UP_ACTION = "\u2197"
DEL_ACTION = "X"

@app.route("/<category>", methods=["GET", "POST"])
def locations(category):
  locations = visit.get_list_by_category(category)
  ## Check the request for form data and process
  if request.method == "POST":
    [(name, action)] = request.form.items()

    if action == UP_ACTION:
      visit.moveup(name)
    elif action == DEL_ACTION:
      visit.delete(name)
  ## Return the main template with variables
  return render_template("locations.html",category=category, categories=categories, locations=locations, add_location=AddLocationForm())

@app.route("/add_location", methods=["POST"])
def add_location():
  ## Validate and collect the form data
  add_form = AddLocationForm()
  if AddLocationForm.validate_on_submit(): 
      name = add_form.name.data
      description = add_form.description.data
      category = add_form.category.data
      visit.add(name, description, category)

  ## Redirect to locations route function
  return redirect(url_for("locations", category=category))

@app.route("/")
def index():

  ## Redirect to locations route function
  return redirect(url_for("location", category="recommended"))

If you would like the other files I can add them for you. I really appreciate all of the help. I have since finished that module and am now working on another but I would love to get this app working correctly.

if AddLocationForm.validate_on_submit():

should be

if add_form.validate_on_submit():

Technically speaking, invoking AddLocationForm is just invoking the function object and not what it does or any instantiation of it. Printing it would just yield something like: <function AddLocationForm at 0x7f92bda68830>.

We can think of it loosely like a class object even… – because without instantiating the form, it doesn’t help us much. So when we validate we want the particular instantiation, not the “class” scheme of it.

(This is a very loose analogy, someone please correct me if this is off-base).

1 Like

Sorry I did some more tweaking of the return command and now get a different error:
UnboundLocalError: local variable ‘category’ referenced before assignment

I have no idea what the heck is going on now.

from flask import Flask, render_template, request, redirect, url_for
from locations import Locations
from forms import AddLocationForm

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

visit = Locations()
categories = {"recommended": "Recommended", "tovisit": "Places To Go", "visited": "Visited!!!", }

UP_ACTION = "\u2197"
DEL_ACTION = "X"

@app.route("/<category>", methods=["GET", "POST"])
def locations(category):
  locations = visit.get_list_by_category(category)
  ## Check the request for form data and process
  if request.method == "POST":
    [(name, action)] = request.form.items()

    if action == UP_ACTION:
      visit.moveup(name)
    elif action == DEL_ACTION:
      visit.delete(name)
  ## Return the main template with variables
  return render_template("locations.html",category=category, categories=categories, locations=locations, add_location=AddLocationForm())

@app.route("/add_location", methods=["POST"])
def add_location():
  ## Validate and collect the form data
  add_form = AddLocationForm()
  if add_form.validate_on_submit(): 
      name = add_form.name.data
      description = add_form.description.data
      category = add_form.category.data
      visit.add(name, description, category)

  ## Redirect to locations route function
  return redirect(url_for("locations", category=category, _external=True, _scheme='https'))

@app.route("/")
def index():

  ## Redirect to locations route function
  return redirect(url_for("location", category="recommended"))
1 Like

Yes, because category only exists in the conditional scope. So to compile flask needs a valid return.

Try something like this, you may have to tweak:

@app.route("/add_location", methods=["POST"])
def add_location():
  ## Validate and collect the form data
  add_form = AddLocationForm()
  if add_form.validate_on_submit(): 
      name = add_form.name.data
      description = add_form.description.data
      category = add_form.category.data
      visit.add(name, description, category)
      return redirect(url_for("locations", category=category, _external=True, _scheme='https'))

  ## Redirect to locations route function
  return redirect(url_for("locations", _external=True, _scheme='https'))

It doesn’t work this way.

It outputs an error:
“werkzeug.routing.BuildError: Could not build url for endpoint ‘locations’. Did you forget to specify values [‘category’]?”

For me, it also outputs that problem message if i write as the exercise describes:
“UnboundLocalError: local variable ‘category’ referenced before assignment”

Maybe it’s a bug?

UPD: also there is a wider discussion on this topic with Codecademy’s crew member being involved:

In a way this is a good “real-life” scenario where the code doesn’t behave like one imagines it’s going to. If you want to troubleshoot with us, you can share your entire code and specific error messages (so that regardless of the instructions we can get to the heart of the issue :slight_smile:).

Thank you, but i don’t think it’s necessary. I think i have exact the same problem as “appylpye” here:

And after that i haven’t seen that the problem was solved even with a codecademy crew member. So it seems like it’s some sort of deeply rooted issue.

Hi,

Declare the category before the if statement.
If the form not submitted then category can be empty. Otherwise, when submitted, category will be the chosen category.

add_location()
def add_location():
  ## Validate and collect the form data
  add_form = AddLocationForm()
  category = add_form.category.data

  if add_form.validate_on_submit():
      name = add_form.name.data
      description = add_form.description.data
      visit.add(name, description, category)

  ## Redirect to locations route function
  return redirect(url_for(
    "locations",
    _external = True,
    category = category,
    _scheme = "https"
  ))