Making urls.py file for Flask like in Django - python

Maybe someone can help/explain me, how to create urls.py file for Flask like in Django?
main.py - main project file. It includes only app runner (app.run()).
urls.py is situated in the same directory and need to provide views
from views.py.

You can do this as is described in the Flask documentation, basically by calling app.add_url_rule to set your routes rather than using the decorator.

In addition to the Flask documentation, this can be solved like this:
When creating the Flask app load your 'urls.py' file
app.register_blueprint(apps.someapp.urls.mod)
Then structure urls.py as following:
from flask import Blueprint
from apps.someapp.views import SomeView
# set method as endpoint
view = SomeView.as_view('someview')
# Create the blueprint for this app
mod = Blueprint("payment_methods", __name__, url_prefix="/someapp/", template_folder="templates")
# Add the view as route; methods like GET, POST, PUT will automatically route to class methods with parameters
mod.add_url_rule('<int:id>/', view_func=view)

Related

Why is flask rendering the wrong template [duplicate]

I have a Flask app with blueprints. Each blueprint provides some templates. When I try to render the index.html template from the second blueprint, the first blueprint's template is rendered instead. Why is blueprint2 overriding blueprint1's templates? How can I render each blueprint's templates?
app/
__init__.py
blueprint1/
__init__.py
views.py
templates/
index.html
blueprint2/
__init__.py
views.py
templates/
index.html
blueprint2/__init__.py:
from flask import Blueprint
bp1 = Blueprint('bp1', __name__, template_folder='templates', url_prefix='/bp1')
from . import views
blueprint2/views.py:
from flask import render_template
from . import bp1
#bp1.route('/')
def index():
return render_template('index.html')
app/__init__.py:
from flask import Flask
from blueprint1 import bp1
from blueprint2 import bp2
application = Flask(__name__)
application.register_blueprint(bp1)
application.register_blueprint(bp2)
If I change the order the blueprints are registered, then blueprint2's templates override blueprint1's.
application.register_blueprint(bp2)
application.register_blueprint(bp1)
This is working exactly as intended, although not as you expect.
Defining a template folder for a blueprint only adds the folder to the template search path. It does not mean that a call to render_template from a blueprint's view will only check that folder.
Templates are looked up first at the application level, then in the order blueprints were registered. This is so that an extension can provide templates that can be overridden by an application.
The solution is to use separate folders within the template folder for templates related to specific blueprints. It's still possible to override them, but much harder to do so accidentally.
app/
blueprint1/
templates/
blueprint1/
index.html
blueprint2/
templates/
blueprint2/
index.html
Point each blueprint at its templates folder.
bp = Blueprint('bp1', __name__, template_folder='templates')
When rendering, point at the specific template under the templates folder.
render_template('blueprint1/index.html')
See Flask issue #1361 for more discussion.
I vaguely remember having trouble with something like this early on. You haven't posted all of your code, but I have four suggestions based on what you've written. Try the first, test it, and then if it still is not working, try the next ones, but independently test them to see if they work:
First, I cant't see your views.py file, so be sure you're importing the appropriate blueprint in your views.py files:
from . import bp1 # in blueprint1/views.py
from . import bp2 # in blueprint2/views.py
Second, you may need to fix the relative import statements in __init__.py as follows (note the period preceding the subfolders):
from .blueprint1 import blueprint1 as bp1
from .blueprint2 import blueprint2 as bp2
Third, since you're hardcoding the path to your templates in your render_template function, try removing template_folder='templates' from your blueprint definition.
Fourth, it looks like you named the url_prefix for your blueprint as "/bp1" when you registered it. Therefore, if the hard coded link to your file system still does not work:
render_template('blueprint1/index.html')
then also try this and see what happens:
render_template('bp1/index.html')
Again, I can't see your full code, but I hope this helps.

Blueprints in Flask

I am taking over a Flask application that has a user module, but does not have a landing page. The structure of the project is:
|-application.py
|-manage.py
|-settings.py
|-/templates
|----base.html
|----index.html
|----navbar.html
|----/user
|--------views.py
application.py:
from flask import Flask
....
def create_app(**config_overrides):
app = Flask(__name__)
app.config.from_pyfile('settings.py')
app.config.update(config_overrides)
db.init_app(app)
from user.views import user_app
app.register_blueprint(user_app)
return app
user/views.py:
from flask import Blueprint, render_template, request, redirect, session, url_for, abort
...
user_app = Blueprint('user_app', __name__)
#user_app.route('login', methods = ('GET','POST'))
def login():
....
I placed index.html in the templates folder.
Should I place a view.py in the root directory where I would put a route to an index.html?
You can add additional routes anywhere you want.
However, since the package uses a create_app() app factory, you can't register those routes with an #app.route() decorator; the app is not created in a way you can just import.
So yes, creating a views.py module is a good organisational idea, but do create a Blueprint() there too, and register that blueprint in the create_app() function with the Flask() app instance.
In views.py:
from flask import Blueprint
bp = Blueprint('main', __name__)
#main.route('/')
def index():
# produce a landing page
and in create_app() in application.py, add
import views
app.register_blueprint(views.bp)
(I use a convention of using the bp variable name for all my blueprints, and then just importing the module and registering the module.bp attribute).
It's the explicit import and app.register_blueprint() call that ties any of the blueprints used in a Flask project into the final app's routes. Blueprints can share the same prefix, including having no prefix.
One thing to note: here the views module is now a top-level module next to application.py. It'd be better if everything was moved into a package, giving you more freedom over what names you use. All modules in a single package are namespaced, kept separate from other top-level modules such as json and time and flask, so there won't be clashes when you want to use the same name for one of the additional modules in your project.
You'd move everything but manage.py into a subdirectory, with a project-appropriate name, and move application.py to __init__.py. Imports can then use from . import ... to import from the current package, etc.

Changing jinja_env from a blueprint

I am trying to register a new jinja global on my blueprint using the Blueprint object. However, it appears that Blueprint objects do not have jinja_env attributes; how can I register a new jinja global attributes?
Here's the __init__.py of the blueprint, this does not work:
from flask import Blueprint, current_app
uploader = Blueprint('uploader', __name__, template_folder='templates')
from . import views
from . import models
current_app.jinja_env.globals['form_token'] = views.generate_form_token
Nor does this:
uploader.jinja_env.globals['form_token'] = views.generate_form_token
Use the Blueprint.app_template_global decorator to register a global function to the jinja env.
uploader.app_template_global(views.generate_form_token)
Or in views.py:
#uploader.app_template_global
def generate_form_token():
pass
I was facing similar problem using Flask with Jinja 2 and having trouble registering global template using app_template_global decorator.
I used add_app_template_global decorator described here Flask API refernce
The following code works for Flask (0.10.1) blueprints with Jinja2(2.8).
Here is the __init__.py file.
from flask import Blueprint
blueprint_name = Blueprint('blueprint_name', __name__)
from . import views
add the decorator to function in main file [in my case views.py] file.
Here is the views.py file.
from . import blueprint_name
#blueprint_name.add_app_template_global
def function_name():
pass
You can then call this function in Jinja2 template as
<h1>
{{ function_name() }}
<h1>

How can I use Flask Admin panel in a different package?

This is my application structure:
/blog
/blog
/app.py
models.py
views.py
/admin
__init__py
views.py
...
I want to use flask-admin extension in a different package.
in /admin/__init__.py I imported the app and flask-admin extension:
from flask.ext.admin import Admin
from app import app
then I initiate the admin app like that:
admin = Admin(app)
However, I get 404 error. Why? Should I use blueprint or what?
I assume you're trying to hit the default /admin routes within your Flask app for Flask admin?
My guess right now is that none of your code does import admin anywhere, which is probably good since admin's __init__.py will try to re-import your app.py all over again (from the from app import app reference) and you'll end up in a circular dependency.
What I'd do is alter app.py to contain the admin = Admin(app) and from flask.ext.admin import Admin code, and also do a from admin import views and empty out the admin/__init__.py file completely.

flask app object inside blueprint

I know the way to access the flask app obj is using current_app, however, everytime I run that code I get this error: NameError: global name 'current_app' is not defined
What am I doing wrong here?
My folder structure is this:
/project
gateway.py
settings.py
/app
views.py
models.py
I can't access the app object neither from views.py or models.py :S Always get the same error.
The views.py file declares a blueprint wich is registered in the gateway.py file.
I'm using current_app inside a view function inside the views.py file.
Hope you can help me.
Make sure you have
from flask import Flask, current_app
in your code and make sure you call it correctly, because in some context it doesn't get called.

Categories