Changing variable value in base html template - python

I need to change value of variable login_status in base html template, which is used for every html page in
web application.
Tech: jinja2, python.
If login into personal account is successful, then login_status got string value 'logout',
if not - 'login'. But correct change happens only in one page of personal account, when I jump
to another pages this variable does not change and has default value.
Please help me to handle this, show the way to change this variable on every html page.
Thank you!
base.html
<li class="in">{{login_status | default('login')}}</li>
<form name="form_in" method = 'post' action = '/personal_account'>
<input type="text" placeholder="Login" id="log" name="login"/>
<input type="password" placeholder="Password" id="pass" name="password"/>
<input type="submit"/>
</form>
python function
#app.route('/personal_account', methods=['POST'])
def welcome():
login = request.form['login']
password = request.form['password']
login_status = u'logout'
if not validate_user(login, password):
login_status = u'login'
return u'Incorrect login!', login_status
data = get_user_data(login)
return render_template('private.html',
data=data,
login_status=login_status)
EDIT ONE
I would like to check the user's status just once, then save it and walk through pages of my application without sending check request to server each time I change html-page. The click event on the button "exit" causes changing user's status. Is it possible to do that? And if so then how?

There are many ways to actually accomplish that, and one of the simplest ways would be attaching a currently logged in user object to global application context:
#app.before_request
def before_request():
g.user = None
# Now this is your application specific code:
if 'user_id' in session:
g.user = load_user_info(user_id)
And now you can use this object in template:
{% if g.user %}
User is logged in
{% else %}
User not logged in
{% endif %}
[Edit]: Another option would be to rely on Flask's session which defaults to client-side cookies by default. Just go through the Sessions quickstart in docs.

Related

How to replace standard input () with flask form in Python

My question is similar to Replacing input() portions of a Python program in a GUI. But I need a web UI not desktop UI(tkinter based solution as answered in the link)
You will have to create a webpage with HTML/CSS and create a form in it to get the input. Then, you will have to link the webpage with your backend (flask) to receive the input in python and manipulate as needed. Following a tutorial like this one will help you get started. Or, you can simply search the web for "Form handling in Flask" to find something that suits your needs.
Say you're asking for the user to input their username. You'd do something like this.
HTML
<body>
<form action="" method="post">
<input type="text" placeholder="" value="{{ request.form.username }}">
<input type="submit" value="Submit">
</form>
</body>
Whatever you want to call your variable, you put after request.form
In your program (where you import flask), also import request from flask like so:
from flask import request
Under your route's function, check if the request's method is POST and assign your form variable:
def index():
if request.method == "POST":
foo = request.form["username"]
return render_template("index.html")
So when the user clicks the submit button, foo will be whatever they put in the text box.
You can see more at https://flask.palletsprojects.com/en/1.1.x/quickstart/#accessing-request-data

Flask werkzeug.routing.BuildError

I doing a flask app and when i try to put a link to redirect a user to his profile page by calling
BuildError: Could not build url for endpoint 'profile'. Did you forget
to specify values ['business_name']?
when i try to login a user.My app works fine the previous days with this same code i do not what has happen and i have tried all possible means to get this right but no way
#app.route('/profile/<business_name>')
#login_required
def profile(business_name):
user = User.query.filter_by(business_name=business_name).first()
if user == None:
flash('This Profile does not exist {}'.format(business_name))
return redirect(url_for('login'))
return render_template('profile.html',user=user)
(main.html)
<ul class="nav navbar-nav">
<li>Home</li>
{% if g.user.is_authenticated %}
<li>Your Profile</li>
<li>Logout</li>
Problem is you are not defining the route view function to take in the following form:
/profile/business_name
Hence you should send business_name in URL but you are sending arguments to the function. You should do the following:
<a href="/profile/{{business_name=g.user.business_name }}">

Flask app submit target="_blank" form only after wtforms validation

In my Flask app I have a form generated with wtforms and jinja templates. If validation passes I want to redirect in a new tab, else I want to stay on the same page and display the errors. However if I set target="_blank" in my form, it opens a new tab without validation passing and shows the errors there. Removing target="_blank" will not open a new tab. Is there a way of achieving this without rewriting the whole validation in js? Thanks!
Code:
from wtforms import Form, TextAreaField, StringField, validators
class SubmitForm(Form):
field1 = StringField(u'field1', [validators.DataRequired()])
field2 = TextAreaField(u'field2', [validators.DataRequired()])
#app.route('/', methods=['POST'])
def sub():
form = SubmitForm(request.form)
if request.method == 'POST' and form.validate():
# great success
return redirect('/my_redirect_uri')
return render_template('index.html', form=form)
#app.route('/')
def layout():
return render_template('index.html', form=SubmitForm())
index.html:
{% from "_formhelpers.html" import render_field %}
<form method=post action="/" target="_blank">
<dl>
{{ render_field(form.field1) }}
{{ render_field(form.field2) }}
</dl>
<p><input type=submit value=Submit>
</form>
_formhelpers.html(not that relevant but for the sake of completness):
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
Main issue
You must pass the form object as well as the variables representing your form fields namely field1 and field2.
Details
What is being stated above means that you need to change:
return redirect('/my_redirect_uri')
to
return redirect('/my_redirect_uri', form=form, field1=field1, field2=field2)
This also means that you have to adjustments to your view method:
#app.route('/', methods=['POST'])
def sub():
form = SubmitForm(request.form)
if request.method == 'POST' and form.validate():
# great success
field1 = form.field1.data
field2 = form.field2.data
return redirect('/my_redirect_uri', form=form, field1=field1, field2=field2)
return render_template('index.html', form=form)
Now there are some improvements to your program:
Code less principle:
Replace if request.method == 'POST' and form.validate(): by if form.validate_on_submit(): (you save one instruction)
Use url_for():
For the sake of scalability because in future versions and reedits of your program, any changes made to route names will be automatically available when using url_for() as it generates URLs using the URL map. You may use it in return redirect('/my_redirect_uri') (you may check the link I provided for further information)
Use sessions:
Sessions will make your application "able to remember" the form data being sent. In your example, you can use a session this way:
session['field1'] = form.field1.data
session['field2'] = form.field2.data
This means, for example, the above line:
return redirect('/my_redirect_uri', form=form, field1=field1, field2=field2)
must be changed to:
return redirect('my_redirect_uri', form=form, session.get('field1'), session.get('field2'))
Note that if you want to implement sessions, you will need to set a secret key because they are stored on the client side, so they need to be protected (cryptographically in Flask's philosophy). This mean you must configure your application this way:
app.config['SECRET_KEY'] = 'some secret phrase of your own'
Actually, the problem is not related to the browser tab you opened. The whole issue emanates from the redirect() statement.
More details on why it is good to use sessions? Check the next last section:
A word about redirect:
Your POST request is handled by redirect, consequently you loose access to the form data when the POST request ends.
The redirect statement is simply a response which, when received, the browser issues a GET request. This mechanism is there mainly for the following (and similar) situation:
If you try to refresh the same browser window on which you submitted the form data, the browser will prompt you a pop up window to confirm that you want to send the data (again) because the browser remembers, by design, the last request it performed. Of course, this is nasty for the user of your application. There we need sessions. This is also helpful for the browser tab to which you redirect.

Pyramid self.request.POST is empty - no post data available

I'm currently working on a pyramid project, however I can't seem to submit POST data to the app from a form.
I've created a basic form such as:
<form method="post" role="form" action="/account/register">
<div class="form-group">
<label for="email">Email address:</label>
<input type="email" class="form-control" id="email" placeholder="you#domain.com">
<p class="help-block">Your email address will be used as your username</p>
</div>
<!-- Other items removed -->
</form>
and I have the following route config defined:
# projects __init__.py file
config.add_route('account', '/account/{action}', request_method='GET')
config.add_route('account_post', '/account/{action}', request_method='POST')
# inside my views file
#view_config(route_name='account', match_param="action=register", request_method='GET', renderer='templates/account/register.jinja2')
def register(self):
return {'project': 'Register'}
#view_config(route_name='account_post', match_param="action=register", request_method='POST', renderer='templates/account/register.jinja2')
def register_POST(self):
return {'project': 'Register_POST'}
Now, using the debugger in PyCharm as well as the debug button in pyramid, I've confirmed that the initial GET request to view the form is being processed by the register method, and when I hit the submit button the POST request is processed by the *register_POST* method.
However, my problem is that debugging from within the *register_POST* method, the self.request.POST dict is empty. Also, when I check the debug button on the page, the POST request is registered in the list, but the POST data is empty.
Am I missing something, or is there some other way of access POST data?
Cheers,
Justin
I've managed to get it working. Silly me, coming from an ASP.NET background forgot the basics of POST form submissions, and that's each form field needs a name== attribute. As soon as I put them in, everything started working.
That does nothing, I belive.
return {'project': 'Register_POST'}
POST parameters are stored inside request, so you have to do something like this.
def register_POST(self, request):
return {'project': request.POST}
To access email input (which has to be named, for example: name="email"), use get() method:
request.POST.get('email')
<form method="post" role="form" action="/account/register"> {% csrf_token %}
Try using "csrf token". hope it works. remaining code looks fine.

django-registration and redirect containing hashtag

I'm using django-registration to handle login and user registration in my Django app. I'd like the user to get redirected to the url they requested after they have successfully logged on to the system (assuming they were logged off when they tried to access the url initially).
The login url looks like:
http://localhost:8000/accounts/login/?next=/#foo/bar/
However, after the login, the user always ends up at http://localhost:8000/. I've tried it with http://localhost:8000/accounts/login/?next=/abc/def/ and that worked fine, so I assume the hashtag is the problem. How can this be fixed (if at all)?
Update
I should have mentioned that it's a single page app, hence the hashtag...
I started stepping through the django-registration (actually django) code. I couldn't find anything django-registration is doing during the login, so I think it isn't even responsible for this part.
Django wraps #login_required view functions in _wrapped_view(request, *args, **kwargs) functions (django.contrib.auth.decorators) and performs an authentication test before executing the actual function. If found not logged in yet, it gets the current url as next (path = request.build_absolute_uri()) and starts the login. However, request.build_absolute_uri() doesn't even return the hashtag url. Extracting the #-appended part of the url does generally not seem to be possible.
Sorry for the necromancer, but I solved this issue with the following:
I have a template for the login form with the following:
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<input type="hidden" id="hidden_next" name="next" value="{{ next }}">
... the actual form here ...
</form>
and (I am using JQuery) the following script at the end:
<script>$(function () {
$("#hidden_next").attr(
"value", "{{ next }}" +
$(location).attr('hash'))
});
</script>
Note that I am repeating the {{ next }} just in case the user disables Javascript. In a single page app this is not an issue, as a user disabling javascript cannot use the page at all.

Categories

Resources