Adding User Registration to Django App - python

I'm following this tutorial and finished it: http://tutorial.djangogirls.org/en/django_forms/README.html
I want to continue and add a register button next to the login button which I did, but I couldn't get it to work. I tried doing action = "{% url blog.views.register %}" but I kept getting the error about no arguments found. I would post all my code but I'm almost sure it's way off base. I followed this idea: http://tea.cesaroliveira.net/archives/460 but I couldn't get it to work right. What is the best way to implement a registration form? Can someone give me an example or a guide, even just a short one written out to the approach I should take to add registration features. I had added a second form in login.html template with method=post and action = "{% url blog.views.register %}" with another template for registration, register.html
in blog/views.py
def register(request):
if request.method == "POST":
form = UserCreateForm(request.POST, instance=post)
return render(request, 'blog/register.html)
and in forms.py
from django import forms
from .models import Post
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class UserCreateForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ( "username", "email" )\
and in blog/urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from blog import views
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^accounts/login/$', 'django.contrib.auth.views.login'),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}),
url(r'', include('blog.urls')),
url(r'^accounts/register/$', views.register , {'next_page': '/'}),
)
As far as validation I can worry about that later, I just want to get it to work so that they can click register and bring up a registration template with a form to fill out. I'm not sure if this requires a new model or not as django has user capabilities built in already.
Any help/advice would be much appreciated. I just want to see what is wrong with my approach and what a better approach would be.

Alright, starting from the top and this is most likely the first reason why your code is probably falling apart, this {% url blog.views.register %} is wrong. Per the Django docs on reverse urls:
In order to perform URL reversing, you’ll need to use named URL patterns.
So, to create a reverse url for the registration view, your urlpattern should look like this:
urlpatterns = patterns('',
url(r'^accounts/register/$', views.register , name="register"),
)
I took out the 'next' item because you can redirect from the view. And in the template, you put:
{% url 'blog:register' %}, the left hand argument being the app name and the right-hand arg being the name of the url pattern.
Second, I'm not sure how you're handling the login form, but the register view is expecting an instance that doesn't exist. Instance is used when you're UPDATING an existing object. For your registration, that's not what's happening, so it doesn't need to be there.
def register(request):
if request.method == "POST":
form = UserCreateForm(request.POST) # filled form/i'm skipping validation for this example
return HttpResponseRedirect('/') # go to some other page if successfully saved
else:
form = UserCreateForm # if the user accessed the register url directly, just display the empty form
return render(request, 'blog/register.html', {'form': form})
But ultimately, I highly recommend django-allauth for user registration/profile stuff.
UPDATE: Fixed the reverse url for register.

Related

Why Django does not generate app name into href paths?

I hope someone could maybe help me, please :
I am quite new to Django and currently trying to implement a simple login/logout for a test page.
However, for some reasons, the Django does not generate the name of application in the href (so where it should be xxxx/main/register, it is only xxx/register).
But when I put the app name manually in the href in Pycharm, it generates it two times (so it becomes xxx/main/main/register).
So for this:
Logout
I got this url:
http://127.0.0.1:8000/logout/
If I write this:
Logout
I got this url:
http://127.0.0.1:8000/main/main/logout/
But I need to get this:
http://127.0.0.1:8000/main/logout/
It worked before, but from one minute to another, it suddenly stopped directing to the good path. And django does the same thing with every links in my site.
main/urls.py:
from django.urls import path
from . import views
app_name = 'main' # here for namespacing of urls.
urlpatterns = [
path("", views.homepage, name="homepage"),
path("register/", views.register, name="register" ),
path("logout/", views.logout_request, name="logout"),
]
main/views.py:
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import Tutorial
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import logout, authenticate, login
from django.contrib import messages
def homepage(request):
return render(request = request,
template_name='main/home.html',
context = {"tutorials":Tutorial.objects.all})
def register(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
username = form.cleaned_data.get('username')
messages.success(request, f"New account created: {username}")
login(request, user)
return redirect("main:homepage")
else:
for msg in form.error_messages:
messages.error(request, f"{msg}: {form.error_messages[msg]}")
else:
form = UserCreationForm
return render(request = request,
template_name='main/register.html',
context={"form":form})
def logout_request(request):
logout(request)
messages.info(request, "Logged out successfully!")
return redirect("main:homepage")
mysite/urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('main/', include('main.urls')),
path('admin/', admin.site.urls),
path('tinymce/', include('tinymce.urls')),
]
The behaviour you observe has nothing to do with Django, it's a basic html feature: in the first case you're using an absolute path (=> starting with a slash) for your href, in the second one you're using a relative path (NOT starting with a slash) so it's resolved relatively to the current url path whatever it is. You'd have the very same issue with plain static HTML.
This being said, in Django, you should never hardcode urls, but use the {% url <name> %} template tag (or the django.utils.reverse() function in Python code) instead, so you can change your urls in the urls.py files without breaking anything.
<a href="{% url 'logout' %}">
Use the name in href tag that will work.
You should use the url name in the template. It should solve the problem. Like this:
Logout
Or with namespacing:
Logout

Restrict access to reset_password form of Django in PasswordResetView in case the user is already logged in

Currently, I have a user model that can access account related information based on its session. I am using the django auth PasswordResetView. The password reset form is used to reset password if the user has forgotten his/her password. But this Django view is also accessed by the user when he is already logged in.
How can I restrict the user to access this page? I cannot find solution for this problem, since it's a total abstraction and nothing is present in my views.py file.
This is how my urls.py file looks like :
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
from users import views as user_views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('mainapp.urls')),
path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name="login"),
path('logout/', auth_views.LogoutView.as_view(template_name='users/logout.html'), name="logout"),
path('password_reset/', auth_views.PasswordResetView.as_view(template_name='users/password_reset.html'), name="password_reset"),
path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(template_name='users/password_reset_done.html'), name="password_reset_done"),
path('password_reset_confirm/<uidb64>/<token>', auth_views.PasswordResetConfirmView.as_view(template_name='users/password_reset_confirm.html'), name="password_reset_confirm"),
path('password_reset_complete/', auth_views.PasswordResetCompleteView.as_view(template_name='users/password_reset_complete.html'), name="password_reset_complete"),
path('change_password/', auth_views.PasswordChangeView.as_view(template_name='users/change_password.html', success_url="/"), name="password_change"),
# path('password_change_done/done/', auth_views.PasswordChangeDoneView.as_view(template_name='users/password_change_done.html'), name="password_change_done"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
A logged in user should not be able to access password_reset_form since it is only meant when the password is forgotten and when the user is already logged in, it does not make sense for the user to access password_reset.html.
Multiple ways to do this; However, I think that the quickest solution would be to write a decorator:
def login_forbidden(function=None):
actual_decorator = user_passes_test(
lambda u: u.is_anonymous,
)
if function:
return actual_decorator(function)
return actual_decorator
and use it to decorate the password reset views where you want to restrict access only to unauthenticated users:
path('password_reset/', login_forbidden(auth_views.PasswordResetView.as_view(template_name='users/password_reset.html')), name="password_reset")
Alternatively, you can override the actual views.
You can use {% if user.is_authenticated %} or {% if user.is_anonymous %} and put that directly in your template no need to modify the view:
{% if user.is_anonymous %}
# user is not logged in
...
{% else %}
# user is logged in
...
{% endif %}
If you really want to modify the default views, one option would be to subclass them and use them in your urls.
In your views (using UserPassesTestMixin class):
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth.views import PasswordResetView
class MyPasswordResetView(UserPassesTestMixin, PasswordResetView):
template_name = 'users/password_reset.html'
# https://docs.djangoproject.com/en/2.2/ref/contrib/auth/#django.contrib.auth.models.User.is_anonymous
def test_func(self):
return self.request.user.is_anonymous
In your urls:
from .views import MyPasswordResetView
...
path(
'password_reset/',
MyPasswordResetView.as_view(),
name="password_reset"),
...
You should learn to read the linked docs to find your answers. It says here:
[...] you can set any of the parameters of AccessMixin to customize the handling of unauthorized users [...]
And under AccessMixin you can find the attributes and methods to redirect unauthorized users.

How would I direct all traffic that hasn't logged in to django allauth login

Want to direct all un-logged in traffic to the login page. How would I go about doing this? I don't want visitors to be able to see the site at all if they are not logged in. As well, the allauth signup page should not be able to be accessed either. Users will be pre-registered for the site. I'm fairly new to django and would not want to abandon allauth as I have it working.
In your urls.py file import following:
from django.contrib.auth.decorators import login_required
Then all you have to do is to add this decorator before the views in your urlpatterns. Something like this:
urlpatterns = [
url(r'^index/$', login_required(views.index), name='home'),
url(r'^update-profile/$', login_required(views.update_profile), name="update_profile"),
]
As long as you have defined registration model and have login.html page setup, this should direct all the traffic to login page automatically.
You could also use something like this in your views.py
from django.http import HttpResponseRedirect
def index(request):
if request.user.is_authenticated():
return HttpResponseRedirect('/home/')
else:
return HttpResponseRedirect('/login/')

Add code to CreateView when using POST method in django

I'm using django's auth module to handle the user login, logout and registration in my website.
The problem is that when I register a new user, I new to redirect to another url, and then, since the model is created but doesn't automatically log in, I need to navigate to /login/ and log in manually.
I would like my site to do this automatically. By that I mean that:
You click on the register button in home.html. You fill in the form and submit it.
The website AUTOMATICALLY creates the new user model, logs you in and redirects you to home.html with the user logged in.
Urls.py looks like this:
from django.conf.urls import include, url, patterns
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.auth import views as auth_views
from django.contrib.auth.forms import UserCreationForm
from browse.views import UserCreate
urlpatterns = patterns('',
url(r'',include('browse.urls')),
url(r'^$', 'browse.views.home_page', name='home'),
url(r'^admin/', include(admin.site.urls)),
url(r'^login/$', auth_views.login, {'template_name': 'browse/login.html'}),
url(r'^register/$', UserCreate.as_view(
template_name='browse/register.html',
form_class=UserCreationForm,
success_url='/register/'
)),
url(r'^logout/$', 'browse.views.logout_view', name='logout'),
url(r'^', include('django.contrib.auth.urls')),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
and views.py looks like this:
from django.shortcuts import render, redirect
from django.views.generic.edit import CreateView
from browse.models import Pattern
from django.contrib.auth.models import User
from django.contrib.auth import logout, login
class PatternCreate(CreateView):
model = Pattern
fields = ['name','description','license', 'xcheme', 'pictures', 'video']
class UserCreate(CreateView):
model = User
def post(self, request):
return login(request, User.objects.get(username=request.POST['username']))
def home_page(request):
return render(request, 'browse/home.html')
def pattern_detail(request, pk):
pattern = Pattern.objects.get(pk=pk)
return render(request, 'browse/pattern_detail.html', {'pattern': pattern})
def logout_view(request):
logout(request)
return redirect('/')
As you can see, when you GET /register/ the register form is rendered. When I POST to /register/, I want to execute the view as normally AND then add:
def post(self, request):
return login(request, User.objects.get(username=request.POST['username']))
But It looks like I'm overriding the default view instead of adding to it, because the model doesn't get saved, since I get this error:
Exception Value:
User matching query does not exist.
How can I fix it?
Thank you so much beforehand.
Okay, it was a really dumb question. Luckily, I could figure it out:
def post(self, request):
super(UserCreate, self).post(request)
login_username = request.POST['username']
login_password = request.POST['password1']
created_user = authenticate(username=login_username, password=login_password)
login(request, created_user)
return redirect('/')
All I had to do is call super() and the parent function is executed. Then, my code executes.

Django - after login, redirect user to his custom page --> mysite.com/username

By default after login django redirects the user to an accounts/profile page or if you edit the LOGIN_REDIRECT_URL you can send the user to another page you specify in the settings.py.
This is great but I would like the user (after login) to be redirected to a custom page where the link to that page would look something like this: mysite.com/username. So the default accounts/profile or the LOGIN_REDIRECT_URL settings would not work in this case since both are somehow static. In my case the username section of the address changes for every user.
Any ideas how I can make it so when the user is logged in would go to a custom user page that has user's name in the address like: mysite.com/username?
A simpler approach relies on redirection from the page LOGIN_REDIRECT_URL. The key thing to realize is that the user information is automatically included in the request.
Suppose:
LOGIN_REDIRECT_URL = '/profiles/home'
and you have configured a urlpattern:
(r'^profiles/home', home),
Then, all you need to write for the view home() is:
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth.decorators import login_required
#login_required
def home(request):
return HttpResponseRedirect(
reverse(NAME_OF_PROFILE_VIEW,
args=[request.user.username]))
where NAME_OF_PROFILE_VIEW is the name of the callback that you are using. With django-profiles, NAME_OF_PROFILE_VIEW can be 'profiles_profile_detail'.
You can authenticate and log the user in as stated here: https://docs.djangoproject.com/en/dev/topics/auth/default/#how-to-log-a-user-in
This will give you access to the User object from which you can get the username and then do a HttpResponseRedirect to the custom URL.
Yes! In your settings.py define the following
LOGIN_REDIRECT_URL = '/your-path'
And have '/your-path' be a simple View that looks up self.request.user and does whatever logic it needs to return a HttpResponseRedirect object.
A better way might be to define a simple URL like '/simple' that does the lookup logic there. The URL looks more beautiful, saves you some work, etc.
If you're using Django's built-in LoginView, it takes next as context, which is "The URL to redirect to after successful login. This may contain a query string, too." (see docs)
Also from the docs:
"If login is successful, the view redirects to the URL specified in next. If next isn’t provided, it redirects to settings.LOGIN_REDIRECT_URL (which defaults to /accounts/profile/)."
Example code:
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
from account.forms import LoginForm # optional form to pass to view
urlpatterns = [
...
# --------------- login url/view -------------------
path('account/login/', auth_views.LoginView.as_view(
template_name='login.html',
authentication_form=LoginForm,
extra_context={
# option 1: provide full path
'next': '/account/my_custom_url/',
# option 2: just provide the name of the url
# 'next': 'custom_url_name',
},
), name='login'),
...
]
login.html
...
<form method="post" action="{% url 'login' %}">
...
{# option 1 #}
<input type="hidden" name="next" value="{{ next }}">
{# option 2 #}
{# <input type="hidden" name="next" value="{% url next %}"> #}
</form>
When using Class based views, another option is to use the dispatch method.
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/
Example Code:
Settings.py
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('login/', auth_views.LoginView.as_view(),name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
views.py
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.views.generic import View
from django.shortcuts import redirect
#method_decorator([login_required], name='dispatch')
class HomeView(View):
model = models.User
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect('login')
elif some-logic:
return redirect('some-page') #needs defined as valid url
return super(HomeView, self).dispatch(request, *args, **kwargs)
I actually prefer the method #Stu and others mentioned, which is to create a new URL and a view for it, so that we can put the redirection logic there. It's easier and users can also type example.com/profile in their browser to get redirected to their profile at example.com/profile/username/.
But another way that doesn't require defining an additional URL, is to extend the LoginView and redirect using the URL name for the profile view.
Let's say we have two apps named accounts and pages, and we have defined the profile page in the pages app, something like this:
# pages/views.py
from django.views.generic import View
from django.http import HttpResponse
class ProfileView(View):
def get(self, request, username):
return HttpResponse('Hello ' + username + '!')
and the URLConf:
# pages/urls.py
from django.urls import path
from .views import ProfileView
app_name = 'pages'
urlpatterns = [
path('profile/<str:username>/', ProfileView.as_view(), name='profile')
]
Note that we must define app_name = 'pages' as we'll need it to refer to the URLs for the pages app.
Then, in the accounts app, we extend the LoginView and add the redirection logic:
# accounts/views.py
from django.contrib.auth.views import LoginView
from django.urls import reverse
class CustomLoginView(LoginView):
def form_valid(self, form):
self.success_url_kwargs = {'username': form.cleaned_data['username']}
return super().form_valid(form)
def get_success_url(self):
# 'pages:profile' refers to the 'profile' path in the 'pages' app.
return reverse('pages:profile', kwargs=self.success_url_kwargs)
# accounts/urls.py
from django.urls import path, include
from .views import CustomLoginView
urlpatterns = [
path('accounts/login/', CustomLoginView.as_view(), name='login'),
path('accounts/', include('django.contrib.auth.urls'))
]
After a successful login, the form_valid() method gets called, and we add the parameters we want to use in the profile URL to the custom class attribute success_url_kwargs. Finally, calling return super().form_valid(form) first logs the user in, and then redirects to the URL given by the get_success_url() method (see the code), which we define to return the custom URL we want.
Note that we included the URLs for the auth app after our custom login URL, so that the first one is resolved when we want to access accounts/login/.
Also, if we needed to access the user model instance, we can call form.get_user(). This can be useful in certain cases. For example, if the user logs in with email and we need his username, we can do form.get_user().username.
Got into django recently and been looking into a solution to that and found a method that might be useful.
So for example, if using allouth the default redirect is accounts/profile.
Make a view that solely redirects to a location of choice using the username field like so:
def profile(request):
name=request.user.username
return redirect('-----choose where-----' + name + '/')
Then create a view that captures it in one of your apps, for example:
def profile(request, name):
user = get_object_or_404(User, username=name)
return render(request, 'myproject/user.html', {'profile': user})
Where the urlpatterns capture would look like this:
url(r'^(?P<name>.+)/$', views.profile, name='user')
Works well for me.
{% if redirect_field_value == None %}
<input type="hidden" name="{{ redirect_field_name }}" value="/LOGIN_REDIRECT_URL(instead of typing in settings.py file)/">
{% else %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{redirect_field_value}}">
{% endif %}
Place this code in the login html page. For Example if you are directly accessing the login page (Example: /accounts/login/) then the 'next' value will be None that is after login its doesnot know to which page you have to redirect until you specify 'LOGIN_REDIRECT_URL' in the settings page (Example: LOGIN_REDIRECT_URL=/dashboard/). If you specify 'LOGIN_REDIRECT_URL' in settings page as example shown then after login it will redirect to '/dashboard/' url.
If you need to access another url which also required login to view (For Example: /items/). So when you access 'localhost:8000/items/' as it required login to view it will redirect to 'localhost:8000/accounts/login/?next=/items/'. Here the value of 'next' will be 'items' but the problem is as you specified 'LOGIN_REDIRECT_URL' in settings page it always redirect to /dashboard/ url after login.
So the above html code helps to redirects if 'next' value is None then it will go to page that we specified as value for the hidden input. if 'next' is not None then it will redirects based on the value of 'next'

Categories

Resources