Django #login_required causing issue when submitting forms - django

I am passing a some information to view function by submitting a form and view requires has #login_required decorator. Here is the template where i'm passing email ID using a from
<form action="{% url 'become_booster' %}" method="post">
{% csrf_token %}
<input type="hidden" name="email" value="{{ profile_user.email }}" />
<div class="form-group">
<div class="col-md-12 col-sm-12">
<input type="submit" class="btn btn-success btn-sm" value="Become a Booster">
</div>
</div>
</form>
Here is the view function
#login_required
def become_booster(request):
if request.method == "POST":
email = request.POST.get('email')
user = CustomUser.objects.filter(email= email)[0]
tiers = Tiers.objects.filter(user=user)
form = SubscriptionForm
return render(request,'select_tier.html',{'tiers':tiers,'form':form})
This is working fine when the user logedin already. When user didn't login, #login_required sends them to login and when it comes back request.method is no longer POST. It no longer has that email info. Can someone help me with this. Thanks!

#login_required decorator is used to available that function to registered user who are actually logged in or send to login page or registration page. if you remove #login_required from your view function it will be available for unregistered and all type of users.

Try this,
from django.shortcuts import *
#login_required(login_url='become_booster')
def become_booster(request):
if request.method == "POST":
email = request.POST.get('email')
user = get_object_or_404(CustomUser, email=email)
tiers = get_list_or_404(Tiers, user=user)
form = SubscriptionForm
return render(request,'select_tier.html',{'tiers':tiers,'form':form})
elif request.method == 'GET':
# Something like this
# return render(request, 'your_template.html', {'profile_user': user_object_which_contains_email})

Related

Django redirect after login is going in infinite loop

This question has been answered earlier but those solutions are not working for me.
My setup have Django 1.9 and MongoDB as backend. I have custom user model.
I have a webpage where I am uploading files. that flow is working fine. Now I have to make that page login protected.
When I am trying to access https://localhost/upload it is redirecting me to https://localhost/login_page?next=/upload
I am giving correct credentials and I can see in django logs that it is redrecting me to /upload but again pointing me back to login page.
models.py
class Uploadedfiles(models.Model):
docfile = models.FileField(upload_to='uploads')
views.py
from django.contrib.auth.decorators import login_required
import os
#login_required()
def uploadfiles(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Uploadedfiles(docfile=request.FILES['docfile'])
newdoc.save()
return HttpResponseRedirect(reverse('upload'))
else:
form = UploadForm() # A empty, unbound form
ufiles = Uploadedfiles.objects.all()
return render(
request,
'upload.html',
{'ufiles': ufiles, 'form': form}
login_page.html
<div>
{% if next %}
<form action="/login/?next={{ request.path }}" method="post" >
{%else%}
<form action="/login_page/" method="post" >
{% endif %}
{% csrf_token %}
<p><b>Login</b></p>
<input type="email" name="email" placeholder="Email" required="">
<input type="password" name="password" placeholder="Password" id="new_password">
<span id="message"></span>
<button type="submit" name="Login"><b>Login</b></button>
</form>
</div>
login view
def LoginPage(request,*args, **kwargs):
msg = "Please provide details"
next = request.POST.get('next', request.GET.get('next', ''))
if 'email' in request.POST and 'password' in request.POST:
password=request.POST['password']
email=request.POST['email']
payload = { "password" : password, "email":email}
response = requests.post('https://localhost/login',data=payload)
code=response.status_code
if '200'==code or 200==code:
msg="Your are login successfully"
print(msg)
return HttpResponseRedirect("/upload")
else:
msg=response[msg]
response = render(request,'login_page.html',{'msg':msg})
else:
response = render(request,'login_page.html',{})
return response
urls.py
urlpatterns = [
url(r'^login$', Login.as_view(), name=r"login"), #This is REST API
url(r'^login_page/$', LoginPage, name=r"loginpage")
]
Please someone help me..
EDIT 1 : adding custom user model
from mongoengine import fields, Document
class User(Document):
id = fields.IntField(primary_key=True)
username = fields.StringField(required=True)
email = fields.EmailField()
name = fields.StringField()
password = fields.StringField(required=True)
A simple solution would be to use LOGIN_REDIRECT_URL
LOGIN_REDIRECT_URL = '/upload'

User can post comment only after login in Django

I'm coding a news website,I want the user can submit the comment of the news only after they have logged in,if not,the website will return to login.html.
Now I have made it that only the user who have logged in can submit a comment,the issue is once I log off and submit a comment the error says:
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x10fed10b8>>": "NewsComments.user" must be a "UserProfile" instance.
Note:I have rewrote the User models and rename it UserProfile .It works very well.
Here is my news/views.py:
def newsDetailView(request, news_pk):
news = News.objects.get(id=news_pk)
title = news.title
author = news.author_name
add_time = news.add_time
content = news.content
category = news.category
tags = news.tag.annotate(news_count=Count('news'))
all_comments = NewsComments.objects.filter(news=news)
comment_form = CommentForm(request.POST or None)
if request.method == 'POST' and comment_form.is_valid():
comments = comment_form.cleaned_data.get("comment")
comment = NewsComments(user=request.user, comments=comments, news=news)
comment.save()
return render(request, "news_detail.html", {
'title': title,
'author': author,
'add_time': add_time,
'content': content,
'tags': tags,
'category': category,
'all_comments': all_comments,
'comment_form': comment_form
})
Here is my news.detail.html
<form method="POST" action="">{% csrf_token %}
<div class="form-group">
<label for="exampleFormControlTextarea1"><h5>评论 <i class="fa fa-comments"></i></h5></label>
<textarea id="js-pl-textarea" class="form-control" rows="4"
placeholder="我就想说..." name="comment"></textarea>
<div class="text-center mt-3">
<input type="submit" id='js-pl-submit' class="btn btn-danger comment-submit-button" value='Submit'>
</input>
</div>
</div>
</form>
Here is my urls.py:
path('-<int:news_pk>', newsDetailView, name="news_detail"),
You could use djangos login-required-decorator.
#login_required
def newsDetailView(request, news_pk):
...
EDIT to expand the idea from my comments.
You could have two views, one with the login_required decorator. (You could also use class-based-views (CBV) if you prefer)
def view_news_details(request, news_pk):
...
#login_required
def post_comments(request, news_pk):
...
Each view would have their own url:
url(r'^(?P<news_pk>[0-9]+)/$', views.view_news_details, name='view-details'),
url(r'^(?P<news_pk>[0-9]+)/comment/$', views.post_comments, name='comment'),
Then you can have only one template but with conditional rendering. This template will be rendered by the view views.view_news_details, but the form will send its data to the other view (note the forms action attribute).
... display the news details here ...
{% if request.user.is_authenticated %}
<form method="POST" action="{% url 'comment' news_instance.pk %}">
... here goes the content of the form ...
</form>
{% endif %}
Redirect the user to your login view before let him submit any data in your views.py :
# Codes here
if request.method == 'POST': # We separe those two "if statements", because
# We want to redirect the user to login even if the form is not valid, User can bypass your security concern
# For Django < 2.0, use it with () if request.user.is_authenticated():
if request.user.is_authenticated:
return redirect("login_url_name") # Or HttpResponseRedirect("login_url")
if comment_form.is_valid():
comments = comment_form.cleaned_data.get("comment")
# Rest of codes
Important
In your template, give access to the form to only authenticated users
{% if request.user.is_authenticated %}
<form method="POST" action="">{% csrf_token %}
<div class="form-group">
<label for="exampleFormControlTextarea1"><h5>评论 <i class="fa fa-comments"></i></h5></label>
<textarea id="js-pl-textarea" class="form-control" rows="4"
placeholder="我就想说..." name="comment"></textarea>
<div class="text-center mt-3">
<input type="submit" id='js-pl-submit' class="btn btn-danger comment-submit-button" value='Submit' />
</div>
</div>
</form>
{% endif %}
You can check whether the requested user is logged-in or not by user.is_authenticated() method, which returns a boolean value.
Try the following snippet,
def newsDetailView(request, news_pk):
# code
if request.method == 'POST' and comment_form.is_valid():
if not request.user.is_authenticated():
return HttpResponse("Please do login")
comments = comment_form.cleaned_data.get("comment")
comment = NewsComments(user=request.user, comments=comments, news=news)
comment.save()
return render(request, "news_detail.html", {
'title': title,
'author': author,
'add_time': add_time,
'content': content,
'tags': tags,
'category': category,
'all_comments': all_comments,
'comment_form': comment_form
})

Django add form on every page

In my application i need to add a form in base.html, which I've done. For this i used context_processors, now my problem is everytime i'm trying to post, i'm getting a blank page and this error: Method Not Allowed (POST)
In this form i want just to add a button where it will mark all current users notifications as read.
I know that you can use context_processors like this:
def my_context(request):
data = dict()
if request.user.is_authenticated:
data['notifications'] = Notification.objects.filter(user=request.user, read=False)
data['form'] = NotificationForm()
return data
But instead of adding the form i need these lines:
def my_context(request):
data = dict()
if request.user.is_authenticated:
data['notifications'] = Notification.objects.filter(user=request.user, read=False)
if request.method == 'POST':
if 'read-notifications' in request.POST:
for notification in data['notifications']:
notification.read = True
notification.save()
next = request.POST.get('next', '/')
return redirect(next)
return data
The form in base.html:
<form action="" method="POST">{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="btn m-btn--square btn-primary" name="read-notifications">Mark as read</button>
</form>
urls.py
url(r'^orders/create/$', views.admin_order_document, name='create_order'),
url(r'^orders/waiting/$', views.OrdersWaitingListView.as_view(), name='order_waiting_list'),
url(r'^orders/unallocated/$', views.OrdersUnallocatedListView.as_view(), name='order_unallocated_list'),
url(r'^orders/working/$', views.OrdersWorkingListView.as_view(), name='order_working_list'),
How can i show this form on every page without getting the above error?
So, I chose a different approach and i ended up with this solution:
base.html
<form action="{% url 'mark_read' %}" method="POST">{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="btn m-btn--square btn-primary" name="action" value="mark_read">Marchează ca citite</button>
</form>
views.py
#login_required()
def read_notifications(request):
if request.method == 'POST':
if 'action' in request.POST:
notifications = Notification.objects.filter(user=request.user, read=False)
for notification in notifications:
notification.read = True
notification.save()
next = request.POST.get('next', '/')
return redirect(next)
urls.py
url(r'^mark-read/$', views.read_notifications, name='mark_read'),
Basically, the form action will redirect me to the view function, it will do what it has to do and it will redirect me back on the same page.

How to redirect to previous page in Django after POST request

I face a problem which I can't find a solution for. I have a button in navbar which is available on all pages and it is a button responsible for creating some content.
View that links with button:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return HttpResponseRedirect('/', {'username': request.user.username, 'uw': uw})
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
If you can see now I always redirect to main page '/' after creating content but I want to go back to the page with which I launched the creation of content.
You can add a next field to your form, and set it to request.path. After you processed your form you can redirect to the value of this path.
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit">Let's Go</button>
</form>
views.py
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
This is roughly what django.contrib.auth does for the login form if I remember well.
If you pass through an intermediate page, you can pass the 'next' value via the querystring:
some_page.html
Go to my form!
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.GET.next }}">
<button type="submit">Let's Go</button>
</form>
You can use the HTTP_REFERER value:
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
Note that this will not work if the client disabled sending referrer information (for example, using a private/incognito browser Window). In such a case it will redirect to /.
You can use this
return redirect(request.META.get('HTTP_REFERER'))
Make sure to import this
from django.shortcuts import redirect
My favorite way to do that is giving the request.path as GET parameter to the form.
It will pass it when posting until you redirect.
In Class-Based-Views (FormView, UpdateView, DeleteView or CreateView) you can directly use it as success_url.
Somewhere i read that it's bad practise to mix GET and POST but the simplicity of this makes it to an exception for me.
Example urls.py:
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('user/update/', UserUpdateView.as_view(), name='user_update'),
]
Link to the form inside of the template:
Update User
Class-Based-View:
class UserUpdateView(UpdateView):
...
def get_success_url(self):
return self.request.GET.get('next', reverse('home'))
In your function based view you can use it as follows:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
next = request.GET.get('next', reverse('home'))
return HttpResponseRedirect(next)
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
you could do this easily with a simple one-liner JS
<button onclick="history.back()">Go Back</button>
This will take you back to the previous page of your history list.
If you don't have a history
https://www.w3schools.com/jsref/met_his_back.asp
Use HTTP_REFERER value:
for use in func return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
for use in template Go Back
In case this helps someone I got this to work in class based UpdateView
template
<form class="form" method="POST">
{% csrf_token %}
<!-- hidden form field -->
<input type="hidden" id="previous_page" name="previous_page"
value="/previous/page/url">
<!-- any other form fields -->
{{ form.name|as_crispy_field }}
{{ form.address|as_crispy_field }}
<!-- form submit button -->
<button class="btn btn-primary" type="submit" id="submit">Submit</button>
</form>
<!-- JS to insert previous page url in hidden input field -->
<script>
prev = document.getElementById("previous_page");
prev.value = document.referrer;
</script>
views.py
class ArticleUpdateView(generic.UpdateView):
model = Article
form_class = ArticleForm
template_name = 'repo/article_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
# if form is valid get url of previous page from hidden input field
# and assign to success url
self.success_url = self.request.POST.get('previous_page')
return super().form_valid(form)
The view now redirects you back to the page where you had clicked the "Update/Edit" button. Any URL query parameters are also preserved.

Signup then login a user with Django

I have a simple signup form (in signup.html)
<form action="adduser" method="post">
{% csrf_token %}
Email Address: <input type="email" name="email" required autocomplete="on" placeholder="fr#star.com"/><br/>
Username: <input type="text" name="username" maxlength=25 required placeholder="JoyfulSophia"/><br/>
Password: <input type="password" name="password" maxlength=30 required placeholder="**********" /><br/>
<br/>
<input type="submit" value="Send" /> <input type="reset">
</form>
This redirects to the addUser view:
def adduser(request):
u = User.objects.create_user(request.POST['username'], request.POST['email'], password=request.POST['password'])
u.save()
a = Accounts(user=u)
p = Passwords(user=u)
a.save()
p.save()
return HttpResponseRedirect(reverse('OmniCloud_App.views.profile', args=(u.id,)))
Here is the profile:
#login_required
def profile(request, User_id):
u = get_object_or_404(User, pk=User_id)
a = get_object_or_404(Accounts, pk=User_id)
return render_to_response('profile.html', context_instance=RequestContext(request))
So they wouldn't be signed in, but that's okay because we can send you over to /accounts/login?next=/7/ since they are user 7 (Problems Ahead!)
def login(request):
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect("/account/profile/")
else:
return HttpResponseRedirect("/account/invalid/")
The request doesn't contain anything called username, but the one which was submitted to the addUser form does, so how can I shoot that bro over to login? I could have it parse the url (which contains the next=USER_ID) but that wouldn't work for someone that just types in base_url/login, and the user_id won't be part of the url forever. So what's a brother to do?
Post data exists only for one request. If you want to use it later you should save it somewhere else.
You could login the user right after registration, in adduser view, he just entered his username and password, he doesn't have to do it again.
And login view is a little off. This is just a "POST" part of the view. You need to check and see if it's GET request and if it is return template with form containing username and password fields and with target url that points to the same view. Something like this:
def login(request):
if request.method == 'GET':
return render_to_response('login.html',
{ 'form': LoginForm() },
context_instance=RequestContext(request))
elif request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect("/account/profile")
else:
return HttpResponseRedirect("/account/invalid/")
Where login.html is something like this:
{% extends "base_site.html" %}
{% block content %}
<form method="post" target="{% url login_view %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Login" />
</form>
{% endblock content %}
Also, you could return user to the same login form if username and password didn't match and add some message!
This is just from the top of my head, didn't try it, but it should work!
There is an extensible user-registration application for Django called django-registration that offers you a lot of functionality for creating and registering users. Setting it up is very simple, you can find a very good doc here