Django registration | change behaviour - python

After succesfully registering, the user is redirected to the template 'registration_done.html'.
Is there any way of changing this behaviour to redirecting the user to the registration page and displaying a message?
I tried these code below also tried different ways to change it but have different types of errors in different cases.
urls.py
url(r'^register/$',
views.register,
{
'success_url': '/accounts/register/?success=true'
},
name='register'),
view.py
def register(request):
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid():
# Create a new user object but avoid saving it yet
new_user = user_form.save(commit=False)
# Set the chosen password
new_user.set_password(user_form.cleaned_data['password'])
# Save the User object
new_user.save()
success = request.GET.get('success', None)
return render(request, {'new_user': new_user, 'success': success})
else:
user_form = UserRegistrationForm()
return render(request, 'account/register.html', {'user_form': user_form})
registration.html:
{% if success %}
<p>{% trans 'Successfull registration!' %}</p>
{% endif %}
Whats wrong I did?!
Error:
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\django\core\handlers\base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
TypeError: register() got an unexpected keyword argument 'success_url'
[18/Aug/2016 14:17:55] "GET /en/account/register/ HTTP/1.1" 500 59886

You are trying to pass the kwarg success_url to your function register
Your function register(request) only accepts one argument: 'request'.
So you can accept a second argument, say success_url, like so
def register(request, success_url):
...
But there is no point doing that if success_url is a constant. In that case, just define it your register function
def register(request):
success_url = 'foo'
Another point here is that you want to reverse that url, rather than hard code it.
Also I'm not sure why you would want to use this:
success = request.GET.get('success', None)
Any user could submit their own success variable in the GET request. Are you expecting this from a form? Looks like a security vulnerability if the user can just say that the were successful in request.GET.
Right now you aren't actually rendering a template on success anyway because you are missing a template name/path from your first render call.
So either add a template or redirect them to another page.

Related

How to solve a attribute error in Django?

I am trying to log in a user but I am getting an attribute error.
Here is my forms.py:
class Login(forms.Form):
email = forms.EmailField(max_length=250)
password = forms.CharField(widget=forms.PasswordInput)
def login_user(self):
email = self.cleaned_data['email']
password = self.cleaned_data.get('password')
user = authenticate(email=email, password=password)
if user in User.objects.all():
login(self, user)
else:
return render(self, 'todoapp/waiting_2.html')
Here is my views.py:
def login_user(request):
if request.method == 'POST':
login_form = Login(request.POST)
if login_form.is_valid():
login_form.login_user()
login_form.save()
return HttpResponseRedirect(reverse('dashboard'))
else:
return render(request, 'todoapp/waiting_2.html')
return render(request, 'registration/login.html', {'form': Login()})
When I fill in the fields and try to log in, I am getting the error:
AttributeError at /login/
'Login' object has no attribute 'session'
Traceback:
File "/home/gblp250/PycharmProjects/practice/venv/local/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/home/gblp250/PycharmProjects/practice/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/home/gblp250/PycharmProjects/practice/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/gblp250/PycharmProjects/assignment/todoapp/views.py" in login_user
48. login_form.login_user(request)
File "/home/gblp250/PycharmProjects/assignment/todoapp/forms.py" in login_user
27. login(self, request, user)
File "/home/gblp250/PycharmProjects/practice/venv/local/lib/python2.7/site-packages/django/contrib/auth/__init__.py" in login
126. if SESSION_KEY in request.session:
Exception Type: AttributeError at /login/
Exception Value: 'Login' object has no attribute 'session'
There are a few errors here. The main one of attempting to render within the form login_user method. Apart from anything else, you attempt to pass the self as the request parameter to render, which mages no sense.
Remove all of that if/else. You don't need to render; but also note that your if condition is needlessly inefficient. If you get a user, it's necessarily a User.
Finally, the actual cause of your error, where again you are trying to pass self in the place of a request, but this time as the parameter to login. That code belongs in the view.
And finally, the form is not a ModelForm, so there is no save method.
So, form:
def login_user(self):
email = self.cleaned_data['email']
password = self.cleaned_data.get('password')
return authenticate(email=email, password=password)
and view:
if login_form.is_valid():
user = login_form.login_user()
if user:
login(request, user)
return HttpResponseRedirect(reverse('dashboard'))
Although at this point you may as well move all that logic to the view.
login() get as first argument the request, you call it with the form as first argument.
https://docs.djangoproject.com/en/2.1/topics/auth/default/#django.contrib.auth.login

'SearchForm' object has no attribute 'get'

I'm trying to create SearchForm with DateField, but form don't see attribute 'get', when I send data method="post". Where is error?
forms.py
class SearchForm(forms.Form):
datee = forms.DateField(input_formats=['%Y-%m-%d'],
widget=forms.widgets.DateInput(format="%Y-%m-%d"))
views.py
def index(request):
search_form = search(request)
context = {'search_form': search_form}
return render(request, 'name/index.html', context)
def search(request):
if request.method == 'POST':
form = SearchForm(data=request.POST)
if form.is_valid():
#Do something for examlpe
HttpResponseRedirect(reverse("name:second"))
else:
form = SearchForm()
search_form = form
return search_form
index.html
<form method="post" action="{% url 'name:search' %}">
{% csrf_token %}
{{ search_form.as_p }}
<button name="submit">Search</button>
</form>
But I'm getting this log and don't understand where is error:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/search_result
Django Version: 2.0.5
Python Version: 3.6.3
Traceback:
File "C:\Users\Александр\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\exception.py" in inner
35. response = get_response(request)
File "C:\Users\Александр\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\deprecation.py" in __call__
97. response = self.process_response(request, response)
File "C:\Users\Александр\AppData\Local\Programs\Python\Python36\lib\site-packages\django\middleware\clickjacking.py" in process_response
26. if response.get('X-Frame-Options') is not None:
Exception Type: AttributeError at /search_result
Exception Value: 'SearchForm' object has no attribute 'get'
Django view should return httpresponse object. But your search view return form object instead. You can rewrite serch view to something like this to fix error:
def search(request):
if request.method == 'POST':
form = SearchForm(data=request.POST)
if form.is_valid():
#Do something for examlpe
HttpResponseRedirect(reverse("name:second"))
else:
form = SearchForm()
context = {}
context['search_form'] = form
return render(request, 'name/index.html', context)
Form isn't supposed to have get, post or other dispatched HTTP verb-matching methods as it's there to represent data and operate on it (more specifically, conveniently delegate any meaningful actions -- i.e. any other than validation and cleaning -- to the underlying infrastructure), not fulfill HTTP request -- the latter is the view's responsibility.
Your search view returns a SearchForm instance when it must return an HttpResponse-compatible object instead, and this is where the error comes from (note the if response.get('X-Frame-Options') is not None:).
To fix this, make sure to return render(request, 'name/index.html', {'form': search_form}) from the search view.

Django testing: AssertionError: The form 'form' was not used to render the response

So I'm writing a test to check for the occurrence of Validation Errors in my Form.
In doing so I'm getting the following error:
======================================================================
FAIL: test_start_date_before_end_date_errors (reports.tests.ScheduleValidation)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jwe/piesup2/reports/tests.py", line 61, in test_start_date_before_end_date_errors
self.assertFormError(response, 'form', None, 'End Date Must Be Greater Than Start Date')
File "/home/jwe/piesup2/venv/lib/python3.4/site-packages/django/test/testcases.py", line 467, in assertFormError
" response" % form)
AssertionError: The form 'form' was not used to render the response
----------------------------------------------------------------------
The validation error I'm specifically checking for is raised in models.py inside the clean() method
class Schedule(models.Model)
...
...
def clean(self):
if self.start_date and self.end_date:
# Ensure that the end_date occurs after the start_date
if self.end_date <= self.start_date:
err = "End Date Must Be Greater Than Start Date"
raise ValidationError(err)
The Generic CreateView I am testing looks as follows:
class ScheduleCreate(SuccessMessageMixin, FetchURLMixin, CreateView):
model = Schedule
form_class = ScheduleCreateForm
template_name_suffix = '_create_form'
I'm able to render the errors in the view's template as non_field_errors as so:
{% for error in form.non_field_errors %}
<label>{{ error }}</label>
{% endfor %}
My test looks as follows:
class ScheduleValidation(RequiresLogin, TestCase):
def setUp(self):
self._client = client_fixture.create()[0]
# Setup some example sample data for a Schedule
self.data = {
'client': self._client.id,
'schedule_date': today,
'billing_period': 'Q',
'schedule_type': 'SE',
}
def test_start_date_before_end_date_errors(self):
self.data['start_date'] = today
self.data['end_date'] = yesterday
response = self.client.post('reports:schedule-create', self.data, follow=True)
# Check if redirects back to the page
with self.assertTemplateUsed('schedule_create_form.html'):
self.assertFormError(response, 'form', None, 'End Date Must Be Greater Than Start Date')
I'm setting the field type of None in order to access non_field_errors as detailed by the documentation here
What I've Tried Already
Inside my template for the view, I'm able to reference the form using {{ form }}, including any non_field_errors. Which means it is being passed to the template properly.
I can check the context data inside the view itself.
def get_context_data(self, **kwargs):
context = super(ScheduleCreate, self).get_context_data(**kwargs)
assert False
return context
From the breakpoint here I'm able to log the contents of the context variable with Werkzeug in the browser, which shows that 'form' is actually passed to the template
[console ready]
>>> context
{'form': <ScheduleCreateForm bound=True, valid=False, fields=(client;schedule_date;start_date;end_date;billing_period;schedule_type)>, 'view': <reports.views.ScheduleCreate object at 0x7f256eb6c908>}
Which begs the question why am I getting this error, and how would I fix this?
When you use client.post(), you should use the actual URL, not the name of the url.
You could either hardcode it, for example:
response = self.client.post('/reports/schedules/create/, self.data, follow=True)
Or you could reverse the url:
from django.core.urlresolvers import reverse
url = reverse('reports:schedule-create')
response = self.client.post(url, self.data, follow=True)

Django redirect, but pass form error context

I have a very simple index page view, from which the user can fill in a login popup, which sends a post request to /login
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
context['login_form'] = LoginForm()
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
If the login is valid it simply redirects to the index page, this works fine.
The login view looks as follows:
def user_login(request):
form = LoginForm(request.POST)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context all over again
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
However when the login is incorrect I want to be able to refresh the page and show the login form errors inside the index template (in the login popup)
I'm actually able to achieve this with the above code, but I'm unhappy with the solution for the following reasons:
I have to manually fetch the context all over again, e.g user/student forms and studyspaces, this goes against the DRY principle
When the page is refreshed the url is localhost:8000/spacefinder/login
Screenshot of behaviour here
I'm wondering if there's somehow a way to use redirect to reload the index page and somehow pass errors from my login_form, e.g. something like:
return redirect('spacefinder:index', {'login_form': form})
I've looked into using messages to pass form validation errors, but struggled to get this working since Validation Errors are thrown inside forms.py, and I'm unable to fetch the request instance from inside a ModalForm to properly create a message
You are doing it the wrong way around.
Consider these prerequisites:
entry point to your page is the index view
the index view must only be accessible by authenticated users
the login view allows both methods GET and POST and is accessible to anonymous users only
The reason to use Django is to make use of all the features that it offers, and that includes handling of the above (because that is what most pages need, not only you).
To set it up correctly you need to define your urls.py like this:
from django.contrib.auth.decorators import login_required
urlpatterns = [
....
url('^login/$', user_login, 'login'),
url('^/$', login_required(index), 'index'),
....
]
In your settings/base.py (or settings.py if you have no environment differentiation) tell Django how to redirect users:
LOGIN_URL = reverse_lazy('login')
LOGIN_REDIRECT_URL = reverse_lazy('index')
https://docs.djangoproject.com/en/1.9/ref/settings/#login-url
https://docs.djangoproject.com/en/1.9/ref/settings/#login-redirect-url
Simplify your index view:
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
return HttpResponseForbidden() # prevented by Django, should never happen
return render(request, 'spacefinder/index.html', context)
Let the user_login view deliver the empty login form:
#require_http_methods(["GET", "POST"])
def user_login(request):
params = getattr(request, request.method)
form = LoginForm(params)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context for new form or form with errors
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
You have not presented any code that handles the UserForm or the StudendForm. You would need to add that to the user_login view, as well - if this is something that all users should fill in every time they login. Otherwise use a different view.
It's worth looking at modules like allauth. They might spare you some work when it comes to allowing users to login with their e-mail addresses, ascertain that e-mail addresses are unique in the system etc.

View didn't return an HttpResponse object. It returned None instead

The view below is gives me the error when using the POST method. I'm trying to load the model data into a form, allow the user to edit, and then update the database. When I try to Save the changes I get the above error.
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
context = {'form': rating}
if request.method == "POST":
form = RatingForm(request.POST)
if form.is_valid():
form.save()
return redirect('home.html')
else:
return render(
request,
'ratings/entry_def.html',
context
)
Here is the trace from the terminal.
[15/Apr/2016 22:44:11] "GET / HTTP/1.1" 200 1554
[15/Apr/2016 22:44:12] "GET /rating/edit/1/ HTTP/1.1" 200 919
Internal Server Error: /rating/edit/1/
Traceback (most recent call last):
File "/Users/michelecollender/ENVlag/lib/python2.7/site-packages/django/core/handlers/base.py", line 158, in get_response
% (callback.__module__, view_name))
ValueError: The view ratings.views.edit didn't return an HttpResponse object. It returned None instead.
You are redirecting if form.is_valid() but what about the form is invalid? There isn't any code that get's executed in this case? There's no code for that. When a function doesn't explicitly return a value, a caller that expects a return value is given None. Hence the error.
You could try something like this:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
context = {'form': rating}
if request.method == "POST":
form = RatingForm(request.POST)
if form.is_valid():
form.save()
return redirect('home.html')
else :
return render(request, 'ratings/entry_def.html',
{'form': form})
else:
return render(
request,
'ratings/entry_def.html',
context
)
This will result in the form being displayed again to the user and if you have coded your template correctly it will show which fields are invalid.
I was with this same error, believe it or not, was the indentation of Python.
your error to the indentation of a Python file. You have to be careful when following tutorials and/or copy-pasting code. Incorrect indentation can waste lot of valuable time.
You Should Return What file you are rendering instead of Direct Render.
def index(request):
return render(request, 'index.html')
def login(request):
return render(request,'login.html')
def logout(request):
return render(request,'index.html')

Categories

Resources