Authentication using Email and resulted routing problems - django

I'm trying to Authenticate users by their emails instead of username.
I'm using django-userena and by using its Docs,etc. I set almost anything that is needed. Something like : USERENA_WITHOUT_USERNAMES = True in its setting, etc.
But after signing up, I've faced a chain of problems. like trying to pass my username in the url for authentication, signup completion problems, etc.
I changed some view functions that need username as an argument, but this method neither solved my problem , nor is a correct (and maybe secure) way to do it.
for instance, by routing to this URL http://127.0.0.1:8000/accounts/signup/complete/ (after $ ./manage.py check_permissions ) I get this error:
global name 'username' is not defined
/userena/views.py in directto_user_template
user = get_object_or_404(User, username_iexact=username)
Is there anything that I'm missing ??
UPDATE:
Here is the output that I get:
Caught NoReverseMatch while rendering: Reverse for 'userena_activate'
with arguments '('xyz#xyz.com',
'70b60d1d97015e03ba8d57f31e4c7ff14d6ab753')' and keyword arguments
'{}' not found.
It's clear that userena tries to the email as username with URL :
userena/templates/userena/emails/activation_email_message.txt, error at line 8
1 {% load i18n %}{% autoescape off %}
2 {% if not without_usernames %}{% blocktrans with user.username as username %}Dear {{ username }},{% endblocktrans %}
3 {% endif %}
4 {% blocktrans with site.name as site %}Thank you for signing up at {{ site }}.{% endblocktrans %}
5
6 {% trans "To activate your account you should click on the link below:" %}
7
8 {{ protocol }}://{{ site.domain }}{% url userena_activate user.username activation_key %}
9
10 {% trans "Thanks for using our site!" %}
11
12 {% trans "Sincerely" %},
13 {{ site.name }}
14 {% endautoescape %}
UPDATE 2:
Alright . by reading the source code for SignupFormOnlyEmail class form, it says that a random username is generated automatically.
class SignupFormOnlyEmail(SignupForm):
"""
Form for creating a new user account but not needing a username.
This form is an adaptation of :class:`SignupForm`. It's used when
``USERENA_WITHOUT_USERNAME`` setting is set to ``True``. And thus the user
is not asked to supply an username, but one is generated for them. The user
can than keep sign in by using their email.
"""
def __init__(self, *args, **kwargs):
super(SignupFormOnlyEmail, self).__init__(*args, **kwargs)
del self.fields['username']
def save(self):
""" Generate a random username before falling back to parent signup form """
while True:
username = sha_constructor(str(random.random())).hexdigest()[:5]
try:
User.objects.get(username__iexact=username)
except User.DoesNotExist: break
self.cleaned_data['username'] = username
return super(SignupFormOnlyEmail, self).save()
UPDATE :
I finally solved the problem. I was also using django-email-as-username beside to django-userena. This was the cause of my problem. Apparently, they have some conflicts. WATCH OUT

You've defined url route userena_activate with keyword arguments (username and activation_key), but you call it just with arguments, change template to keyword arguments:
{% url userena_activate username=user.username activation_key=activation_key %}
edit due to comment:
I'm not sure if I understand your problem corectly, but I think there's a problem elsewhere. Yours error message says:
Caught NoReverseMatch while rendering: Reverse for 'userena_activate' with arguments '('xyz#xyz.com', '70b60d1d97015e03ba8d57f31e4c7ff14d6ab753')' and keyword arguments '{}' not found.
It seems you pass valid arguments to function, but you pass them wrong way. URL route in urls.py is defined in a way to expect kwargs, but you pass just args, which mismatch its definition. That is why you get this error message. Simple pass arguments as kwargs (that means each argument is passed with its name and value as showed above).
urls.py difference between argument and keyword argument:
url(r'^(?P<username>[\.\w]+)/activate/(?P<activation_key>\w+)/$', userena_views.activate, name='userena_activate'),
^ this is _keyword argument_ 'username', expects value with argument value AND name
and
url(r'^page/(.*)/$', userena_views.ProfileListView.as_view(), name='userena_profile_list_paginated'),
^ this is argument, expects only value, not argument name

A really simple alternative for using the email address as the username (effectively) is django-easy-userena - this is an upward compatible fork of Userena that adds a few nice features:
use email address as effective user ID - generates a random numeric
username that is hidden in forms and confirmation emails
fewer dependencies - doesn't require django-guardian or easy-thumbnails
terms of service agreement field is built in, displays as checkbox
I've had good results with this - installation was manual for some reason (copy userena dir to site-packages) but it worked without hassles.
I like the overall Userena approach, but easy-userena is a better fit for what I need.

Related

how to pre-fill an HiddenInput in admin ModelForm

I'm sure that problems similar to the one I'm going to ask about were already discussed somewhere, but I always found very tricky solutions while I think there must be a very straightforward one (you can guess I'm a totally newbie).
I have a model (Lecturer) that is connected OneToOne to the User model:
class Lecturer(models.Model):
user = models.OneToOneField(User)
... other fiels follow ...
I'd like each user to create its own Lecturer object through the admin site.
My idea is to present to the user the add_view without the user field. Then I would something like obj.user=request.user when saving the model.
In other words, I don't want to give the user the possibility of selecting a different user among the registered for its own Lecturer object.
I modified the form by overriding the get_form method and by providing a custom form:
admin.py
class LecturerAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if is_lecturer(request.user):
kwargs['form'] = UserLecturerForm
return super(LecturerAdmin, self).get_form(request, obj, **kwargs)
class UserLecturerForm(forms.ModelForm):
class Meta:
model = Lecturer
fields = ('__all__')
widgets = {'user': forms.HiddenInput()}
I cannot just exclude the user field and give it a value at some other level (e.g. save_model or clean ...) because this raises an error at the template rendering level:
Django Version: 1.7.7
Exception Type: KeyError
Exception Value: u"Key 'user' not found in 'LecturerForm'"
Exception Location: /usr/lib/python2.7/dist-packages/django/forms/forms.py in getitem, line 147
Python Executable: /usr/bin/python
Python Version: 2.7.9
Error during template rendering
In template /usr/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 7
[...]
7 <div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
I tried making the user field hidden in the form. But then the problem came of how to give it the correct request.user value, given that the form doesn't know anything about the request. If I don't fill it with a valid value, the form will not validate (and I cannot use solutions involving the save_model as suggested here)
I found solutions that involve changing the view, but I wouldn't do it in the admin context.
An alternative would be to change the validation behavior, but still validation is a method of the form and the form doesn't know request.
Any help is appreciated.

django-profile passing variables to template

Django newbie at work and I could use some pointers. I'm using django-profile and have been working on my profile page which is handled by the views.profile_detail. The problem I am facing is I am unable to put another variable in my template by using this view. Here is my view function:
def profile_detail(request, username, public_profile_field=None,
template_name='profiles/profile_detail.html',
extra_context=None):
"""
Detail view of a user's profile.
If no profile model has been specified in the
``AUTH_PROFILE_MODULE`` setting,
``django.contrib.auth.models.SiteProfileNotAvailable`` will be
raised.
If the user has not yet created a profile, ``Http404`` will be
raised.
**Required arguments:**
``username``
The username of the user whose profile is being displayed.
**Optional arguments:**
``extra_context``
A dictionary of variables to add to the template context. Any
callable object in this dictionary will be called to produce
the end result which appears in the context.
``public_profile_field``
The name of a ``BooleanField`` on the profile model; if the
value of that field on the user's profile is ``False``, the
``profile`` variable in the template will be ``None``. Use
this feature to allow users to mark their profiles as not
being publicly viewable.
If this argument is not specified, it will be assumed that all
users' profiles are publicly viewable.
``template_name``
The name of the template to use for displaying the profile. If
not specified, this will default to
:template:`profiles/profile_detail.html`.
**Context:**
``profile``
The user's profile, or ``None`` if the user's profile is not
publicly viewable (see the description of
``public_profile_field`` above).
**Template:**
``template_name`` keyword argument or
:template:`profiles/profile_detail.html`.
"""
user = get_object_or_404(User, username=username)
# accuracy = ''
try:
profile_obj = user.get_profile()
accuracy = str(profile_obj.number_of_answers / profile_obj.number_of_answers) + '%'
except ObjectDoesNotExist:
raise Http404
if public_profile_field is not None and \
not getattr(profile_obj, public_profile_field):
profile_obj = None
if extra_context is None:
# extra_context = {'accuracy': potato}
extra_context = {}
context = RequestContext(request)
# context['accuracy'] = 'potato'
for key, value in extra_context.items():
context[key] = callable(value) and value() or value
return render_to_response(template_name,
{'profile': profile_obj},
# { 'profile': profile_obj, 'accuracy': accuracy},
# locals(),
context_instance=context)
and here is my template:
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<p><strong>Level:</strong><br>{{ profile.level }}</p>
<p><strong>Bounty Points:</strong><br>{{ profile.total_bounty_points }}</p>
<p><strong>Number of questions:</strong><br>{{ profile.number_of_questions_asked }}</p>
<p><strong>Number of replies:</strong><br>{{ profile.number_of_replies }}</p>
<p><strong>Number of answers:</strong><br>{{ profile.number_of_answers }}</p>
<p><strong>Accuracy:</strong><br>{{ accuracy }}</p>
<p><strong>Number of times reported:</strong><br>{{ profile.reported_by_others }}</p>
{% endblock %}
May I know where the value profile is being passed from? Is it from the dictionary {'profile': profile_obj} or is it from the context? I tried commenting out both but the template still renders fine.
I have also tried to create a new variable called accuracy in my template but I am unable to get it to render and the template simply fails silently. I then added TEMPLATE_STRING_IF_INVALID = '%s' to my settings file which allowed me to see that the accuracy variable is not found. May I know what I did wrong?
Any advice would be greatly appreciated! Thanks :)
Argh I found the problem! I was changing the wrong file >_< as my python install had written to the default directory.

Django: Calling views.py with argument

Hi Stackoverflow people,
I am using the Userena package for my user registration site. The package allows to change the template or the form class during with the call of the views.py function "def profile_edit" (if I understood it correctly).
The full header of the view function is:
def profile_edit(request, username, edit_profile_form=EditUserProfileForm,
template_name='userena/profile_form.html', success_url=None,
extra_context=None):
The demo project calls the view function in the template through the urls.py with the statement
{% url userena_profile_edit user.username %}
When I try to change the form parameter for example with
{% url userena_profile_edit user.username edit_profile_form=EditUserProfileForm %}
I get the follow error, which does not make sense to me:
Caught ValueError while rendering: Don't mix *args and **kwargs in call to reverse()!
I have also tried to the specify the kwargs dict, but it did not work either.
{% url userena_profile_edit user.username kwargs={'edit_profile_form':EditUserProfileForm} %}
How can I correctly call the function? I am confused why the last statement would not work.
Thank you for your help!
That's because your mixing args and kwargs. You can't do that in a reverse call. user.username is an arg, try using it as a kwarg:
{% url userena_profile_edit username=user.username edit_profile_form=EditUserProfileForm %}
how about making the username a kwarg?
{% url userena_profile_edit username=user.username edit_profile_form=EditUserProfileForm %}

django problem:captured parameter in parent urlconf , incude() and reverse match

If I want to have django app called app1 which can make a user post note to someone's page and to his/her own page.
And if the user is logged in ,no need using username in the url, but the username parameter in url can be acts as parent in urlconf.
To make it clear:
urls.py
urlpatterns = patterns(''
#if registered user/anonymous user visit someone's page
url(r'^/foo/users/(?P<username>\w+)/app1/',include('app1.urls', namespace='myapp1')),
#if user is logged in in his own page
url(r'^app1/', include('app1.urls', namespace='myapp1')),
...
)
app1/urls.py
urlpatterns = patterns('',
# I expect this pattern receives the username parameter from above
url(r'^note/add/$', app1_views.add_note,
name='add_note'),
url(r'^note/add/$', app1_views.add_note,
{ 'username':None}, name='add_note_own'),
...
...
)
app1/views.py
def add_note(request, username=None):
...
...
First question:
Now for example john is logged in and on jack's notes page john want to post a note.
I want to be able to do something like this or near something like this:
template app1/notes.html
{% if request.user.is_authenticated %}
{%if in his/her own note page %}
add note Expected generated url: www.domain.com/app1/add
{%else}
add note Expected generated url: www.domain.com/foo/jack/app1/add
{%endif%}
{% endif %}
Is this possible?
Another thing,
if john wrote note in jack's page, and django gives the note id == 3,
so to show that note, only these urls are valid:
www.example.com/foo/jack/app1/3
www.example.com/foo/app1/3 (if jack logged in)
Second question:
what I want to achieve is reverse match can accept captured parameter up to to the
parent urlconf when include() is involved in url configuration.
Can this be done?
Or if you get what I mean and can provide simpler solution , please do so :)
sorry if this post is confusing, I am confused myself.
Thanks alot for the patience.
I'm using django 1.2.5
you need to pass an username, one example can be:
urlpatterns = patterns('',
# I expect this pattern receives the username parameter from above
url(r'^note/add/(?P<username>[^/]+)/$', app1_views.add_note, name='add_note'),
# username is an optional argument, so no need to pass it
url(r'^note/add/$', app1_views.add_note, name='add_note_own'),
)
and then in template:
{% if request.user.is_authenticated %}
{% if page.owner == request.user %}
add note
{% else %}
add note to {{ request.user.username }}
{% endif %}
{% endif %}

How to override password_change error messages in django?

I have spent ages on the net how to override the change_password form in django. I created my forms and I can change my passwords successfully. Now I want to display my errors on the page.
(By the way , I'm using django-registration.)
The problem is I have to change error messages. In my template I'm using this function to display new_password1 errors :
{% if form.new_password1.errors %}
<div class="error_message"> blah </div>
{% endif %}
However it consists of all new_password1 errrors. I looked the docs and found that CharField has only reqiured, max and min. I need to check each specific errors of the new_password1 (confirmation error, required error and min,max) and produce my own error messages. Since the CharField doesn't have a matching keyword, I tried a clean method in my forms.py which looks like :
def clean_password2(self):
if 'new_password1' in self.cleaned_data and 'new_password2' in self.cleaned_data:
if self.cleaned_data['new_password1'] != self.cleaned_data['new_password2']:
raise forms.ValidationError(_(u"no match"))
return cleaned_data
Finally, I don't know how to check this clean_password2 validation in my template. When I wrote registration page, I can display all my errors by looping the errors. And it displays all overrided errors. This time it doesn't work. So my quesstions are:
1- How can i check all specific errors in my templates ? like if form.new_password1.required.errors
2- How can i display this clean_password2 messages ?
In django-speak, what you're referring to as the view is the template. It's rather confusing!
Update: I see that you define a clean_password2 function but you are checking for fields called new_password1, new_password2.
Unless you have a field called password2, that is the field that your clean method is validating.
The messages for clean_password2 are generated in {{ form.password2.errors }}, but to me, it sounds like you don't have a field called password2.
Change the name of your method to clean_new_password2.
{% if form.new_password1.errors %}{{ form.new_password1.errors }}{% endif %}
{% if form.new_password2.errors %}{{ form.new_password2.errors }}{% endif %}
Form field cleaning works per field.. you access the errors off the field object.
Like #Yuji said, use {% if form.non_field_errors %} in your template.
Now just a bit on usability. Concerning the user registration and password change form, the only non-field errors your can expect will be mismatching passwords, so unlike other forms where your going to print the non-field errors at the very beginning, for these forms it makes sense to print the one non-field error before the two password input fields, possibly wrapping them in a parent that has a red background, to make it more obvious that the values of the two fields are related and that the error concerns the two fields, not the form in general.