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 }}">
Related
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.
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.
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.
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.
I'm trying to build a simple website with login functionality very similar to the one here on SO.
The user should be able to browse the site as an anonymous user and there will be a login link on every page. When clicking on the login link the user will be taken to the login form. After a successful login the user should be taken back to the page from where he clicked the login link in the first place.
I'm guessing that I have to somehow pass the url of the current page to the view that handles the login form but I can't really get it to work.
EDIT:
I figured it out. I linked to the login form by passing the current page as a GET parameter and then used 'next' to redirect to that page. Thanks!
EDIT 2:
My explanation did not seem to be clear so as requested here is my code:
Lets say we are on a page foo.html and we are not logged in. Now we would like to have a link on foo.html that links to login.html. There we can login and are then redirected back to foo.html.
The link on foo.html looks like this:
<a href='/login/?next={{ request.path }}'>Login</a>
Now I wrote a custom login view that looks somewhat like this:
def login_view(request):
redirect_to = request.REQUEST.get('next', '')
if request.method=='POST':
#create login form...
if valid login credentials have been entered:
return HttpResponseRedirect(redirect_to)
#...
return render_to_response('login.html', locals())
And the important line in login.html:
<form method="post" action="./?next={{ redirect_to }}">
So yeah thats pretty much it, hope that makes it clear.
You do not need to make an extra view for this, the functionality is already built in.
First each page with a login link needs to know the current path, and the easiest way is to add the request context preprosessor to settings.py (the 4 first are default), then the request object will be available in each request:
settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
)
Then add in the template you want the Login link:
base.html:
Login
This will add a GET argument to the login page that points back to the current page.
The login template can then be as simple as this:
registration/login.html:
{% block content %}
<form method="post" action="">
{{form.as_p}}
<input type="submit" value="Login">
</form>
{% endblock %}
To support full urls with param/values you'd need:
?next={{ request.get_full_path|urlencode }}
instead of just:
?next={{ request.path }}
This may not be a "best practice", but I've successfully used this before:
return HttpResponseRedirect(request.META.get('HTTP_REFERER','/'))
Django's built-in authentication works the way you want.
Their login pages include a next query string which is the page to return to after login.
Look at http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.login_required
I linked to the login form by passing the current page as a GET parameter and then used 'next' to redirect to that page. Thanks!
I encountered the same problem. This solution allows me to keep using the generic login view:
urlpatterns += patterns('django.views.generic.simple',
(r'^accounts/profile/$', 'redirect_to', {'url': 'generic_account_url'}),
)
In registration/login.html (nested within templates folder) if you insert the following line, the page will render like Django's original admin login page:
{% include "admin/login.html" %}
Note: The file should contain above lines only.
See django docs for views.login(), you supply a 'next' value (as a hidden field) on the input form to redirect to after a successful login.
You can also do this
<input type="hidden" name="text" value="{% url 'dashboard' %}" />