0

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

That1Guy
  • 7,075
  • 4
  • 47
  • 59
Soask
  • 691
  • 12
  • 21
  • 1
    error message says everything: variable 'username' is not defined. Post code of your views.py, pls – yedpodtrzitko Jun 28 '12 at 09:03
  • The code exists in its [Github](http://github.com/bread-and-pepper/django-userena/blob/master/userena/views.py) . I will update th post with more info .. for example, look at the line 142, "activate" function .. – Soask Jun 28 '12 at 09:15

2 Answers2

2

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
yedpodtrzitko
  • 9,035
  • 2
  • 40
  • 42
  • you are right.. I don't have username, that's why I said a chain of problems... If I edit and remove username from view/urls/templates, etc, then I got stuck in next views and urls as well ... Do you recommend to remove "all" of username fields within the code base ?? Or there is a better and cleaner way ? – Soask Jun 28 '12 at 09:24
  • 1
    addition: you have username (at least you have it declared). I bet if you add it into template output ({{ user.username }}), you will see it, so you will see it is not the real problem. – yedpodtrzitko Jun 28 '12 at 09:43
  • Yes, it's been produced randomly. OK, you suggest _Simple pass arguments as kwargs_ .. yes, you are right, but the relevant URL for 'userena_activate', accepts "kwargs". So why it complains ? Sorry for asking too much questions, This is so vital for me .. – Soask Jun 28 '12 at 10:22
  • 1
    If you mean 'function accepts kwargs' as 'function expects kwargs', then I agree. If you mean 'function accepts kwargs' as 'I already pass kwargs to function', then no. Let's take 'activation_email_message.txt' as an example, line 8 looks like this: `{% url userena_activate user.username activation_key %}`. These are just `args`. `Kwargs` looks this way: `}{% url userena_activate username=user.username activation_key=activation_key %}`. Do you see the difference? `Kwargs` have there `argument name` as well. – yedpodtrzitko Jun 28 '12 at 10:31
  • Yes,sure I know. I changed to `{% url userena_activate username=user.username activation_key=activation_key %}`. heh .. Now it complains on `Reverse for 'userena_activate' with arguments '()' and keyword arguments '{'username': 'lap10@lap.com', 'activation_key': 'c53fc7756b685d5c1557e9cd45a8dda809503ab0'}' not found.` it tries to put the email as username as its view function expects.. I'm so lost on this – Soask Jun 28 '12 at 10:50
  • maybe because this regexp: `(?P[\.\w]+)` in urls doesn't match email address, which you submit .) – yedpodtrzitko Jun 28 '12 at 11:16
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13159/discussion-between-yedpodtrzitko-and-soask) – yedpodtrzitko Jun 28 '12 at 11:49
0

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.

RichVel
  • 7,030
  • 6
  • 32
  • 48