gouthambs / Flask-Blogging

A Markdown Based Python Blog Engine as a Flask Extension.
http://flask-blogging.readthedocs.org/en/latest/
MIT License
692 stars 160 forks source link

Cannot build url for endpoint 'blogging.page_by_id'. Forgetting post_id? #83

Closed cisko3000 closed 7 years ago

cisko3000 commented 7 years ago

Hi, not sure if this is a flask-blogging issue or something else. I am getting the following when making a post on production server. In development, I am not able to recreate the problem. I managed to debug a bit on production (it is a pain because I have to restart everything for a new printing of value). I found out that post_id is None right before url_for() is called at line 248:

return redirect(url_for("blogging.page_by_id", post_id=pid,
                                            slug=slug))

If it makes a difference, I just made my website use HTTPS a while ago. Though I don't remember if this problem existed before that. I think it did.

File "/home/deploy/mywebsite/env/local/lib/python2.7/site-packages/werkzeug/routing.py", line 1758, in build
    raise BuildError(endpoint, values, method, self)
BuildError: Could not build url for endpoint 'blogging.page_by_id' with values ['slug']. Did you forget to specify values ['post_id']?
gouthambs commented 7 years ago

This error is probably because there is some error when the post is stored on this line just above the one you are quoting:

 pid = _store_form_data(form, storage, current_user, post)

Does this always occur, or only once? If there was more info to be able to help out.

cisko3000 commented 7 years ago

It always occurs on the live website. If I copy the sqlite database to my dev environment, it works fine. If there was a way to use the debugger that comes with flask, I would be able to figure this out quick. Do you think it's because of my tables? I don't have a user table. I have the tables: post, tag, tag_posts, user_posts. I don't know why this would be a problem, both live and dev flask-blogging extensions are same version.

Right now I'm updating my blog posts by creating the new post on test and copying the database over to live.

If you have time, I can provide you with any information you need.

gouthambs commented 7 years ago

Well, I am trying to understand what is different about your production? What is your database setup? Do you have a User's model as described in the docs?

cisko3000 commented 7 years ago

Looks like both are the same. Does production being HTTPS matter?

gouthambs commented 7 years ago

I think the HTTPS case would need to be correctly handled. Some pages that discuss are here:

http://stackoverflow.com/questions/14810795/flask-url-for-generating-http-url-instead-of-https http://flask.pocoo.org/snippets/35/

Let me take a look. If you get this working, I will take a pull request. :)

threemonks commented 7 years ago

I am getting this error in my dev env, though without any https involved. I run on a port different from 80.

scottkleinman commented 7 years ago

I'm also encountering this problem. Flask-Blogging worked for me using the example. However, when I tried to switch to Flask-Sqlalchemy to handle user authentication, that's when I started encountering this error (the list of blog posts also does not work, though that seems to be caused by the fact that I can't create any posts).

Perhaps this is not an actual bug and the problem could be cleared up by separating out the code for Flask-Sqlalchemy a little more in the docs or maybe providing a working example which uses it?

Speedy1991 commented 7 years ago

@scottkleinman Flask-Sqlalchemy isn't used for authentication directly. I think u mean Flask-Login/Flask-Security? I protect parts of my blog with flask-security and can share some code if this is your problem.

scottkleinman commented 7 years ago

@Speedy1991 Yes, I realise that was a little unclear. I actually meant using Flask-Sqlalchemy for the database. I tried to implement it as I was integrating the login system here. Everything went well until I got the error mentioned in this issue, and I had also identified pid = _store_form_data(form, storage, current_user, post) as the likely place where the problem was occurring. So I thought it was worth asking if it was Flask-Sqlalchemy issue.

I haven't tried using using Flask-Security, but, if you have working code to share, that might help me see where I'm going wrong. Thanks!

Speedy1991 commented 7 years ago

I don't see anywhere flask-blogging in your link - im not sure if this problem depends on this repository. Anyway, setting up a user loader is as easy as this:

@blog_engine.user_loader
def load_user(user_id):
    return db.session.query(User).get(user_id)

If you have acces to _currentuser u can return this user too.

scottkleinman commented 7 years ago

I tried to add flask-blogging to the code there. I'm pasting below the way I did it. Lines with # after them are my additions to try to get the blog engine to work. There's probably some redundancy or conflict here in the way that I am implementing the blog_engine to work with flask_sqlalchemy. If I comment out @login_manager.user_loader and uncomment @blog_engine.user_loader, I get an error saying "No user_loader has been installed for this LoginManager".

from flask_blogging import SQLAStorage, BloggingEngine #
from sqlalchemy import create_engine, MetaData #
from flask_sqlalchemy  import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
...
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/blog.db'
db = SQLAlchemy(app)
...
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(15), unique=True)
    email = db.Column(db.String(50), unique=True)
    password = db.Column(db.String(80))

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
db.create_all()
metadata = MetaData() #
storage = SQLAStorage(db=db, metadata=metadata) #
blog_engine = BloggingEngine() #
blog_engine.init_app(app, storage) #

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(user_id)

#@blog_engine.user_loader #
#def load_user(user_id): #
#    return db.session.query(User).get(user_id) #
Speedy1991 commented 7 years ago

U need both methods @login_manager.user_loader Loads the user for for _loginmanager @blog_engine.user_loader Loads (not loged in) users for the blog (e.g. seeing the author names)

Just copy User.query.get(user_id) to your 2nd loader and it should work (i didnt test it)

scottkleinman commented 7 years ago

I thought that would work too, but no luck. I'm guessing it is because there are two load_user functions (?), but I'm not sure how to get around that.

Speedy1991 commented 7 years ago

well i just tested it, my gist is running just fine. Check it out here Edit: You can access /editor after /login and get a redirect to login if u try to access after you visited /logout

scottkleinman commented 7 years ago

That helps a lot. I got it working for my site. Thanks! It turns out that I had a dashboard with the @login_required decorator, which was causing problems. So I imported current_user from flask_login, and {% if current_user.is_authenticated %} works in the template, so I can use that.

My login function is now something like:

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user:
            # hashed password check to be added here.
        login_user(user, remember=form.remember.data)
        return redirect(url_for('blog'))
    return render_template('blogging/login.html', form=form)

Now I'm encountering a couple of other problems which I think are related. When I create a blog post and view it (using the page.html template), the username following "Posted by" is something like '<__main__.User object at 0x0000000003D77E10>'. Also, when I use the index.html template to view all blog posts, I get an error because the meta object has not been passed to the template. If I comment out all the references to meta, I don't get any posts displayed. I've tried to figure out how to import MetaData and insert meta = MetaData() in my app.py file, but nothing seems to work. Any chance you could show how that works in your gist?

gouthambs commented 7 years ago

For the blog to have a readable display name, the User class must implement either the get_name method or the __str__ method.

gouthambs commented 7 years ago

@scottkleinman Just to give you a sense of what is going on:

@login_manager.user_loader
def load_user_flask_login(user_id):
    return User.query.get(user_id)

@blog_engine.user_loader #
def load_user_flask_blogging(user_id): #
    return User.query.get(user_id) 
...
metadata = MetaData() #
db = SQLAlchemy(app, metadata=metadata)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(15), unique=True)
    email = db.Column(db.String(50), unique=True)
    password = db.Column(db.String(80))

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

storage = SQLAStorage(db=db, metadata=metadata) #
...

# the create_all after all the database hooks.
scottkleinman commented 7 years ago

Thanks, @gouthambs. That did the trick! Just to clarify for anyone trying to follow this exchange, it appears that I accidentally deleted the following from the Quickstart Example:

def get_name(self):
    return "Paul Dirac"  # typically the user's name

Once I restored that, the "Posted by" name rendered correctly in the template.

I'm not sure if any of this solves the endpoint building problem that originally prompted this issue, but it worked for me.

dtrimarco commented 7 years ago

Was the original issue ever resolved? I am getting the exact same error under very similar conditions to @cisko3000.

cisko3000 commented 7 years ago

Hi, sorry, i am not sure. i gave up on using flask blogging, currently learning wagtail cms on django

Francisco Barcena http://www.LosAngelesCoder.com

On Aug 5, 2017 9:10 PM, "dtrimarco" notifications@github.com wrote:

Was the original issue ever resolved? I am getting the exact same error under very similar conditions to @cisko3000 https://github.com/cisko3000 .

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/gouthambs/Flask-Blogging/issues/83#issuecomment-320484498, or mute the thread https://github.com/notifications/unsubscribe-auth/AHlAS3TRTVteYxZsVSYFiq5Dm-zWzMwAks5sVTyagaJpZM4LJyxn .

gouthambs commented 7 years ago

@dtrimarco If you can post a sample of your code, I might be able to provide some feedback. It is unclear why some experience this issue, while others don't. I have a site up on HTTPS as well, and haven't run into this issue.

To me it seems like a combination of better documentation + improved error messsages is required.