Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community!

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Build a Social Network with Flask Tacocat Challenge Tacocat!

Gavin Ralston
Gavin Ralston
28,770 Points

My Tacocat Triple-Fail (Logging in, Logging out, and Redirects)

Having difficulty with the following testing failures in workspaces. Both of the redirects from login and logout have similar error messages, where it's expecting the index page at localhost but getting something sliiightly different:

FAIL: test_logout (__main__.UserViewsTestCase)                                               
----------------------------------------------------------------------                       
Traceback (most recent call last):                                                           
  File "app_tests.py", line 109, in test_logout                                              
    self.assertEqual(rv.location, 'http://localhost/')                                       
AssertionError: 'http://localhost/login?next=%2Flogout' != 'http://localhost/'               
- http://localhost/login?next=%2Flogout                                                      
+ http://localhost/                                                                          


----------------------------------------------------------------------  

Using the following code:

@app.route('/logout')
@login_required
def logout():
    logout_user()
    #flash("Quitting?  BYE.", "success")
    return redirect(url_for('index'))

Tried SO, plain google searches, etc, but I'm not finding the answer I'm looking for. Interestingly, I tried this challenge over a week ago and didn't get these errors with the redirects, but my workspaces crashed and disappeared on me, so I've got no idea what subtle differences in my code there were. That looks right to me, though. I'm not exactly sure how to change the behavior of the flask redirect function, or why url_for would tack the query onto the end.

The second error I'm struggling with is the following:

FAIL: test_logged_in_menu (__main__.UserViewsTestCase)                                       
----------------------------------------------------------------------                       
Traceback (most recent call last):                                                           
  File "app_tests.py", line 121, in test_logged_in_menu                                      
    self.assertIn("add a new taco", rv.get_data(as_text=True).lower())                       
AssertionError: 'add a new taco' not found in... 

...and then it lists the html for my index page, generated from layout and all the other requisite blocks.

Here's the thing, I've got "add a new taco" in there, just like I have the logout link, which passes:

          <nav class="menu">
          {% if current_user.is_authenticated() %}
                <a href="{{ url_for('taco') }}">add a new taco</a>
                <a href="{{ url_for('logout') }}">Log Out</a>
                {% else %}
                <a href="{{ url_for('login') }}">Log In</a>
                <a href="{{ url_for('register') }}">Sign Up</a>
                {% endif %}
          </nav>

Any ideas where I might start looking, or if more code is needed?

The second error message seems straightforward, and I'm just not sure how that text ISN'T appearing right alongside the logout link. (and yes, def taco() is the function for the taco route, if that helps)

That's it. Those are the only failures at the moment, everything else passes. :/

Oziel Perez
Oziel Perez
61,321 Points

Did you ever get this fixed? I have the same problem and have no idea what it is, nor does Kenneth's answer work for me; I rewrote that view like he did it and it still didn't work. Additionally, I have a third Fail which tells me that the logged out menu doesn't appear at all (although the login menu does)

2 Answers

Kenneth Love
STAFF
Kenneth Love
Treehouse Guest Teacher

This is my passing version of the login view:

@app.route('/login', methods=('GET', 'POST'))
def login():
    form = forms.LoginForm()
    if form.validate_on_submit():
        try:
            user = models.User.select().where(
                models.User.email**form.email.data
            ).get()
            if check_password_hash(user.password, form.password.data):
                login_user(user)
                flash("You're now logged in!")
                return redirect(url_for('index'))
            else:
                flash("Email or password is invalid")
        except models.DoesNotExist:
            flash("Email or password is invalid")
    return render_template('login.html', form=form)

Those error messages make me think there's some weird @login_required somewhere in the mix.

Gavin Ralston
Gavin Ralston
28,770 Points

I couldn't imagine what I would be doing to my instance of LoginManager that would cause this mess, but at this point it's the only thing I can think of. I knew it had to be something with the login manager, but the only two functions which required login were the only ones decorated as such (taco() and logout())

So I grabbed the flask login docs and tried all kinds of clever ways to suppress a behavior that didn't manifest itself in other scripts...because I tend to miss the obvious things. Like, it only manifests in THIS script, even though I've used that very same setup in other scripts. :)

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

So pretty standard, right? So when I changed the login_view to something else, it was still appending that query string to the url, and more importantly the base url was set to whatever I set login_view, to as if the login wasn't actually successful. I mean, it IS successful, in that it redirects successfully when the login form is filled out, it just didn't redirect to the right place. So I'm like, it HAS to be the login process, but I could not for the life of me figure out what the hell it was.

So it turns out when you do something really dumb like this:

@login_manager.user_loader
def load_user(userid):
    try:
        return models.User.get(models.User.email == userid)
    except models.DoesNotExist:
        return None

Then your login process works exactly as directed, but definitely not as intended :)

Replaced with the following, and it passes all tests:

@login_manager.user_loader
def load_user(userid):
  try:
    return models.User.get(models.User.id == userid)
  except models.DoesNotExist:
    return None

I copied/pasted your first code set to my tacocat app and the test passed. Not fully sure what might be going in with yours, unless it's actually an issue with the code right before your logout view.

For your second issue. The only difference between yours and mine is I have "Add A New Taco!", where you have "add a new taco". Not sure if the capitalization and punctuation matter, as I don't think it should, might be worth a shot.

Scratch the 'add a new taco' bit, I modeled my code exactly as you posted and mine still passes all tests.

Gavin Ralston
Gavin Ralston
28,770 Points

Thanks, I'll relaunch the workspace and see if I get the errors again, and then perhaps I'll try moving things around.

Capitalization shouldn't matter, given the test module checks against lower case strings. That's what really confused me about that particular error.

I know the capitalization shouldn't matter, but your code passing on my system exactly as written is a bit boggling.

Gavin Ralston
Gavin Ralston
28,770 Points

I'm boggled, too, because the errors I'm looking at and the code I wrote seem to be at odds (particularly the text being on the page that's being tested for presence...)

I'll follow up in a minute once I run the workspace after it got a good night's sleep. :)

Gavin Ralston
Gavin Ralston
28,770 Points

Looking more closely they're all predicated on a problem with the login function:

FAIL: test_taco_create (__main__.TacoViewsTestCase)
---------------------------------------------------------------------- 
Traceback (most recent call last):                                                           
  File "app_tests.py", line 145, in test_taco_create                                         
    self.assertEqual(rv.location, 'http://localhost/')                                       
AssertionError: 'http://localhost/login?next=%2Ftaco' != 'http://localhost/'                 
- http://localhost/login?next=%2Ftaco                                                        
+ http://localhost/  


FAIL: test_taco_create (__main__.TacoViewsTestCase)
----------------------------------------------------------------------                       
Traceback (most recent call last):                                                           
  File "app_tests.py", line 109, in test_logout                                              
    self.assertEqual(rv.location, 'http://localhost/')                                       
AssertionError: 'http://localhost/login?next=%2Flogout' != 'http://localhost/'               
- http://localhost/login?next=%2Flogout                                                      
+ http://localhost/       

Here's the login route. Maybe there's just some subtle thing I keep skipping over. :/ I have absolutely no idea why this is redirecting to a url with a GET query string instead of just redirecting to index.

@app.route('/login', methods=('GET', 'POST'))
def login():
  form = forms.LoginForm()
  if form.validate_on_submit():
    try:
      user = models.User.get(models.User.email == form.email.data)
    except models.DoesNotExist:
      flash("Email or password doesn't match")
    else:
      if check_password_hash(user.password, form.password.data):
        login_user(user)
        return redirect(url_for('index'))
  return render_template('login.html', form=form)
Gavin Ralston
Gavin Ralston
28,770 Points

Brandon Meredith thanks for the assist on this. Once I knew I wasn't going crazy or having some unique workspaces issue it at least got me out of that rut and on a more productive path. :)