I have built a custom password reset in Django, however, after putting the information in 'PasswordResetForm' I get a 404 page not found error.
This is my code for reset_password:
def reset_password(request,username):
if request.method == 'POST':
form = PasswordResetForm(data=request.POST, user=request.user)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
#added this to redirect user to custom url
username = request.user.username
return redirect(reverse('main:home', kwargs={'username': username}))
#return redirect(reverse('main:home'))
else:
return redirect(reverse('main:reset_password'))
else:
form = PasswordResetForm(user=request.user)
args = {'form': form}
return render(request, 'reset_password.html', args)
My urls at myapp/urls.py
urlpatterns=[
path('signup/',views.signup,name='signup'),
path('login',views.user_login,name='user_login'),
path('',views.main_page,name='main_page'),
path('<str:username>', views.home, name='home'),
#replace home/edit with below
path('<str:username>/edit', views.edit_profile, name='edit_profile'),
path('<str:username>/password-reset', views.reset_password, name='reset_password'),
]
and my form for password reset:
class PasswordResetForm(PasswordChangeForm):
class Meta:
model = Profile
fields = ('old_password','new_password1','new_password2')
What seems to be the problem here? I do not know why I am getting this error:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/main/test3-b/login.html?next=/main/test3-b/password-reset
This is my AbstractUser model in models.py (I do not have any other code in my models.py
class Profile(AbstractUser):
bio = models.TextField()
university = models.CharField(max_length=30)
def __str__(self):
return self.username
Your urls.py is not using the same path as the URL you are accessing. The URL example you have given is http://127.0.0.1:8000/main/test3-b/login.html but your password reset url is something like http://127.0.0.1:8000/example-user/password-reset where example-user is the username that you are trying to match inside your path.
Out of interest do you have the URL structure include a username? This is normally not wanted as you would be using request.user to access the current user. You also have the risk a users username could break your patterns as you are using str rather than slug which is safer in URL patterns as otherwise id a user was created with the username of "example/edit" then they would never be able to get to your homepage as it would match the edit_profile entry instead.
I saw that by the form rendered in HTML it does not reset the admin user password, however the others works. In my case, I just extended the user class and inserted the email field.
Related
I'm new to django and I'm stuck with a problem. Please help me solve it.
Problem:
Whenever I fill a form, be it a login form, edit post form, create post form, etc..If I press browser's back button, It takes me back to it.
What I want:
Redirect the user to somewhere else rather than taking them back to form. Is there any way to perform it in django ? I want to use CBVs only "and yes, I don't wanna disable browser back button !"
Code:
views.py
class CreatePost(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = Post
template_name = 'blog/create_post.html'
fields = ('category', 'title', 'meta_title', 'meta_description', 'content', 'image', 'image_alt_text')
user_check_failure_path = 'blog-home'
def test_func(self):
return self.request.user.is_superuser
def handle_no_permission(self):
if self.request.user.is_authenticated:
messages.error(self.request, 'Permission Denied ! Only SUPERUSER can access this page', 'danger h4')
return redirect('blog:blog-home')
else:
return redirect('accounts:login')
class Login(views.LoginView):
template_name = 'accounts/login.html'
redirect_authenticated_user = True
In your settings.py file, set: LOGIN_REDIRECT_URL = 'blog:blog-home'
This will redirect a user to the blog-home URL whenever they successfully login.
In your CreatePost view, you should include a success_url = 'blog:blog-home
Or in some cases you can even use: success_url = reverse_lazy('blog:blog-home')
This will redirect the user to the specified URL upon successfully submitting a form.
My Question is in two fold, both revolving round NoReverseMatch error. First Question: I keep getting NoReverseMatch error each time i call my url({% url 'dashboard' client.pk %}) in other pages that uses a different view(eg DashboardPilot: kindly check the url) but when i use it in dashboard.html it works fine.
Views.py
class Dashboard(LoginRequiredMixin, UserPassesTestMixin, DetailView):
model = ClientProfile
template_name ='accounts/dashboard.html'
context_object_name= 'client'
login_url= 'login'
fields = '__all__'
def test_func(self):
return self.request.user.role == 'client'
urls.py
urlpatterns =[
path('accounts/dashboard/client/<int:pk>', Dashboard.as_view(), name='dashboard'),
path('accounts/dashboard/pilot/<int:pk>', DashboardPilot.as_view(), name='pilot_dashboard'),
path('jobs', JobPage.as_view(), name='job_page'),
path('job/<int:pk>/details', JobDetails.as_view(), name='job_details'),
path('accounts/edit/job/<int:pk>', EditJob.as_view(), name='job_edit'),
]
**models.py**
class ClientProfile(models.Model):
username = models.ForeignKey(User, on_delete= models.CASCADE)
client_firstname=models.CharField(max_length=200)
client_lastname=models.CharField(max_length =150)
role =models.CharField(max_length =150)
client_phone_no=models.CharField(max_length =150)
client_email=models.EmailField(max_length=150)
Second Questions:
I have a view which redirect to a dashboard(uses pk) after signing but i keep getting a reverse error each time it tries to redirect to dashboard.
views.py
def signin (request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request,user)
messages.success(request, 'You are now logged in')
if user.role == 'client':
return redirect ('dashboard')
else:
return redirect ('pilot_dashboard')
else:
messages.error(request, 'Invalid Credentials')
return redirect ('login')
else:
return render (request, 'accounts/signin.html')
urls.py
urlpatterns =[
path('accounts/dashboard/client/<int:pk>', Dashboard.as_view(), name='dashboard'),
path('accounts/dashboard/pilot/<int:pk>', DashboardPilot.as_view(), name='pilot_dashboard'),
path('jobs', JobPage.as_view(), name='job_page'),
path('job/<int:pk>/details', JobDetails.as_view(), name='job_details'),
path('accounts/edit/job/<int:pk>', EditJob.as_view(), name='job_edit'),
]
I i have tried using return redirect(reverse('dashboard', args=[str(self.id)])) but i keep getting self not defined. I will be immensely grateful for any help rendered.
For the first problem, please make sure that client object is present in the template. Most probably, your template context does not have the object client which is why url is not resolved. You can test this by including {{ client }} in the templates that are giving you the error.
Basically you need to pass the correct object inside the url template tag.
You can add the client object in the context if your view does not have the ClientProfile in your template's context.
For the second problem, call the reverse with the kwargs keyword. Also, you are not passing the correct value to resolve the url.
Use:
return redirect(reverse('dashboard', kwargs={"pk": user.clientprofile.id}))
I'm currently trying to figure out how to customize the base Django user login functionality to add e.g. simple-captcha.
I have subclassed "AuthenticationForm" from django.contrib.auth.forms
which first looks great, but if I try to login I'm simply not able to.
I have already debugged the code and currently i only get:
response = wrapped_callback(request, *callback_args,
**callback_kwargs) TypeError: init() takes 1 positional argument but 2 were given
accounts/views.py:
...
login form of django.contrib.auth.forms subclassed (copied) to accounts/forms.py:
...
urls.py:
...
when I used vorujack's way made mistake,finally I found a solution
in url
from django.contrib.auth import views as auth_views
path('login/',auth_views.LoginView.as_view(form_class=forms.new_login_form,
template_name='login.html'), name='login'),
in forms
from django.contrib.auth.forms import AuthenticationForm
class new_login_form(AuthenticationForm):
captcha = CaptchaField(label='验证码', error_messages={"invalid": "验证码错误"})
class Meta:
model = User
fields = ('username', 'password',)
There are a few things wrong here.
The main one is that you've overridden the class init signature so that the first positional argument is request, but you pass the POST data in that position; therefore, the form will never be bound and never valid.
Secondly if the form is invalid you re-instantiate it for some reason, so the template will never show any validation errors. Unless you have a really good reason you should stick to the standard form handling structure shown in the docs.
Putting those together:
def login (request):
if request.method == 'POST':
form = LoginForm(request, data=request.POST)
if form.is_valid():
form.save()
messages.add_message(request, messages.INFO, "You are now logged-In, welcome")
return redirect(reverse('post_list'))
else:
form = LoginForm(request)
args = {'form': form}
return render(request, 'registration/login.html', args)
Note, you don't need the second else block; the single one here is aligned with the first if, and the final return is hit in both cases.
as Daniel said you update init method of form. so you must pass request as first argument.
second is when you validate user info you must authenticate user with authentication backend. but you saved form data.
if you only want to change authentication form you can use login view like this
see below:
urls.py
....
url(r'^accounts/login/$', auth.login, {'authentication_form': LoginForm}, name='login'),
....
if you use django2 and above you can change it like this. first of all create a class based view extended from django.contrib.auth.views.LoginView in this class set form_class to it like this.
from django.conrtib.auth.views import LoginView
...
class NewLoginView(LoginView):
form_class = LoginForm
then you must update your urls like this:
...
url(r'^accounts/login/$', NewLoginView.as_view, name='login'),
...
you must extend your form from django.contrib.auth.forms.AuthenticationForm or you must implement all needed function of this form class.
I finally found a solution, puuh :D
views.py:
...
from django.contrib.auth import update_session_auth_hash, authenticate, login as customlogin
...
def login(request):
if request.method == 'POST':
form = LoginForm(request.POST, request.POST)
if form.is_valid():
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
customlogin(request, user)
# Redirect to a success page.
return redirect(reverse('post_list'))
return render(request, 'registration/login.html', {'form': form})
else:
return render(request, 'registration/login.html', {'form': LoginForm()})
simply extend the form (Thanks to vorujack)
...
from django.contrib.auth.forms import AuthenticationForm
...
class LoginForm (AuthenticationForm):
captcha = CaptchaField()
urls.py:
url(r'^accounts/login/$', views_accounts.login, name='login'),
the really stupid part is at the login function "form = LoginForm(request.POST, request.POST)"
You really need to pass this for the password and user and beside that and secondary request.POST for the captcha form... Oookay. Anyways, now it works.
Thanks anybody for your help, i really appreciate this community
I'm building a login form in Django and as far as my research about it goes is seems like I can't create login form that is "independent" from admin interface. What I mean by that is visitor doesn't have to be registered user in admin. Much like in PHP and MySQL. I can do what I need with PHP but I've been learning Python for some time now and would like to build it in Python. Is Django really gives me this only option ?
Makes me wonder how Pinterest did this. I know vast majority of the app is built in Django and people are presented with a login form on front page.
You might find this helpful. I didn't write all the explanations, so please search for the attributes or methods you're not familiar with. Also, this implementation lacks many options like confirming the email address, etc. (these are secondary priority at the moment for me).
'password' and 'username' are model attributes in this case.
Here, after the user creates an account with a username and password, it is authenticated and the user is logged in then redirected to the specified url.
For html/bootstrap login forms, have a look at these:
https://bootsnipp.com/search?q=login
For the django implementation you can follow these steps:
1) After making the html form, add proper url line the urls.py
2) Make a new file in your project and name it forms.py, then:
from django import forms
class UserForm(forms.ModelsForm):
email = form.CharField(widget=forms.EmailInput)
password = form.CharField(widget=forms.PasswordInput)
class Meta:
model = *write your model name here*
fields = *fields from your model. The model design should include
username email password and other attributes*
3) In views.py:
from django.views.generic import View
from django.shortcuts import render, redirect
class UserFormView(View):
form_class = UserForm
template_name = *your html template*
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form':form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
login(request, user)
return redirect(*your redirecting url*)
return render(request, self.template_name, {'form':form})
There are in fact login views that are independent from the admin login, as per the docs:
Add the following to your urls.py:
urlpatterns = [
url('^', include('django.contrib.auth.urls')),
]
This will include the following URL patterns:
^login/$ [name='login']
^logout/$ [name='logout']
^password_change/$ [name='password_change']
^password_change/done/$ [name='password_change_done']
^password_reset/$ [name='password_reset']
^password_reset/done/$ [name='password_reset_done']
^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ [name='password_reset_confirm']
^reset/done/$ [name='password_reset_complete']
These provide views to login/change and reset passwords/etc for non-admin users. As a safety precaution, they do not include a sign-up view by default (since many implementations may want to not allow random people to sign up).
I'm trying to use django's supposed PasswordResetForm functionality to create a password reset form for my django application.
I created a form, in forms.py
class UserForgotPasswordForm(PasswordResetForm):
email = forms.EmailField(required=True,max_length=254)
class Meta:
model = User
fields = ("email")
I'm having trouble setting up the view in views.py to utilize this form, I currently have:
def UserResetPassword(request):
form = UserForgotPasswordForm(None, request.POST)
if request.method == 'POST':
if form.is_valid():
email = request.POST.get('email', '')
user =
my urls.py
urlpatterns = patterns('',
(r'^ForgotPassword/$',UserResetPassword),
)
I'm at a bit of a loss of how to use this further as the documentation I've found is scarce and often not directly using PasswordResetForm django functionality.
Can someone lend a hand?
Thank you.
I believe all you need to do is call form.save() and the PasswordResetForm will generate a onetime use link and email it to the user for you. It looks up the user matching the e-mail entered into the form.
So it would be something like:
def UserResetPassword(request):
form = UserForgotPasswordForm(None, request.POST)
if request.method == 'POST':
if form.is_valid():
form.save(from_email='blah#blah.com', email_template_name='path/to/your/email_template.html')
If you don't specify an email template name, django will just use the default one.