This commit is contained in:
parent
5fb22329a1
commit
c433fcb3fb
|
@ -26,7 +26,7 @@
|
||||||
{% if user.is_active and user.is_staff %}
|
{% if user.is_active and user.is_staff %}
|
||||||
<div id="user-tools">
|
<div id="user-tools">
|
||||||
{% trans 'Welcome,' %}
|
{% trans 'Welcome,' %}
|
||||||
<strong>{% filter force_escape %}{% firstof user.get_short_name user.username %}{% endfilter %}</strong>.
|
<strong>{% filter force_escape %}{% firstof user.get_short_name user.get_username %}{% endfilter %}</strong>.
|
||||||
{% block userlinks %}
|
{% block userlinks %}
|
||||||
{% url 'django-admindocs-docroot' as docsroot %}
|
{% url 'django-admindocs-docroot' as docsroot %}
|
||||||
{% if docsroot %}
|
{% if docsroot %}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
{% for action in action_list %}
|
{% for action in action_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{{ action.action_time|date:"DATETIME_FORMAT" }}</th>
|
<th scope="row">{{ action.action_time|date:"DATETIME_FORMAT" }}</th>
|
||||||
<td>{{ action.user.username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}</td>
|
<td>{{ action.user.get_username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}</td>
|
||||||
<td>{{ action.change_message }}</td>
|
<td>{{ action.change_message }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% block reset_link %}
|
{% block reset_link %}
|
||||||
{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %}
|
{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
|
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
|
||||||
|
|
||||||
{% trans "Thanks for using our site!" %}
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,13 @@ from django.shortcuts import get_object_or_404
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils import six
|
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
|
|
||||||
csrf_protect_m = method_decorator(csrf_protect)
|
csrf_protect_m = method_decorator(csrf_protect)
|
||||||
|
|
||||||
|
|
||||||
class GroupAdmin(admin.ModelAdmin):
|
class GroupAdmin(admin.ModelAdmin):
|
||||||
search_fields = ('name',)
|
search_fields = ('name',)
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
|
@ -106,9 +105,10 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
if extra_context is None:
|
if extra_context is None:
|
||||||
extra_context = {}
|
extra_context = {}
|
||||||
|
username_field = self.model._meta.get_field(self.model.USERNAME_FIELD)
|
||||||
defaults = {
|
defaults = {
|
||||||
'auto_populated_fields': (),
|
'auto_populated_fields': (),
|
||||||
'username_help_text': self.model._meta.get_field('username').help_text,
|
'username_help_text': username_field.help_text,
|
||||||
}
|
}
|
||||||
extra_context.update(defaults)
|
extra_context.update(defaults)
|
||||||
return super(UserAdmin, self).add_view(request, form_url,
|
return super(UserAdmin, self).add_view(request, form_url,
|
||||||
|
@ -171,4 +171,3 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
admin.site.register(Group, GroupAdmin)
|
admin.site.register(Group, GroupAdmin)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ class RemoteUserBackend(ModelBackend):
|
||||||
# built-in safeguards for multiple threads.
|
# built-in safeguards for multiple threads.
|
||||||
if self.create_unknown_user:
|
if self.create_unknown_user:
|
||||||
user, created = UserModel.objects.get_or_create(**{
|
user, created = UserModel.objects.get_or_create(**{
|
||||||
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
UserModel.USERNAME_FIELD: username
|
||||||
})
|
})
|
||||||
if created:
|
if created:
|
||||||
user = self.configure_user(user)
|
user = self.configure_user(user)
|
||||||
|
|
|
@ -52,6 +52,9 @@ class ReadOnlyPasswordHashField(forms.Field):
|
||||||
kwargs.setdefault("required", False)
|
kwargs.setdefault("required", False)
|
||||||
super(ReadOnlyPasswordHashField, self).__init__(*args, **kwargs)
|
super(ReadOnlyPasswordHashField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def clean_password(self):
|
||||||
|
return self.initial
|
||||||
|
|
||||||
|
|
||||||
class UserCreationForm(forms.ModelForm):
|
class UserCreationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
|
@ -118,9 +121,6 @@ class UserChangeForm(forms.ModelForm):
|
||||||
"this user's password, but you can change the password "
|
"this user's password, but you can change the password "
|
||||||
"using <a href=\"password/\">this form</a>."))
|
"using <a href=\"password/\">this form</a>."))
|
||||||
|
|
||||||
def clean_password(self):
|
|
||||||
return self.initial["password"]
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ class AuthenticationForm(forms.Form):
|
||||||
|
|
||||||
# Set the label for the "username" field.
|
# Set the label for the "username" field.
|
||||||
UserModel = get_user_model()
|
UserModel = get_user_model()
|
||||||
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
|
username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
|
||||||
self.fields['username'].label = capfirst(username_field.verbose_name)
|
self.fields['username'].label = capfirst(username_field.verbose_name)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
|
@ -34,7 +34,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
u = UserModel.objects.using(options.get('database')).get(**{
|
u = UserModel.objects.using(options.get('database')).get(**{
|
||||||
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
UserModel.USERNAME_FIELD: username
|
||||||
})
|
})
|
||||||
except UserModel.DoesNotExist:
|
except UserModel.DoesNotExist:
|
||||||
raise CommandError("user '%s' does not exist" % username)
|
raise CommandError("user '%s' does not exist" % username)
|
||||||
|
|
|
@ -42,7 +42,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
UserModel = get_user_model()
|
UserModel = get_user_model()
|
||||||
|
|
||||||
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
|
username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
|
||||||
other_fields = UserModel.REQUIRED_FIELDS
|
other_fields = UserModel.REQUIRED_FIELDS
|
||||||
|
|
||||||
# If not provided, create the user with an unusable password
|
# If not provided, create the user with an unusable password
|
||||||
|
@ -74,7 +74,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
# Get a username
|
# Get a username
|
||||||
while username is None:
|
while username is None:
|
||||||
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
|
username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
|
||||||
if not username:
|
if not username:
|
||||||
input_msg = capfirst(username_field.verbose_name)
|
input_msg = capfirst(username_field.verbose_name)
|
||||||
if default_username:
|
if default_username:
|
||||||
|
@ -91,7 +91,7 @@ class Command(BaseCommand):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
UserModel.objects.using(database).get(**{
|
UserModel.objects.using(database).get(**{
|
||||||
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
UserModel.USERNAME_FIELD: username
|
||||||
})
|
})
|
||||||
except UserModel.DoesNotExist:
|
except UserModel.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -55,7 +55,7 @@ class RemoteUserMiddleware(object):
|
||||||
# getting passed in the headers, then the correct user is already
|
# getting passed in the headers, then the correct user is already
|
||||||
# persisted in the session and we don't need to continue.
|
# persisted in the session and we don't need to continue.
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
if request.user.username == self.clean_username(username, request):
|
if request.user.get_username() == self.clean_username(username, request):
|
||||||
return
|
return
|
||||||
# We are seeing this user for the first time in this session, attempt
|
# We are seeing this user for the first time in this session, attempt
|
||||||
# to authenticate the user.
|
# to authenticate the user.
|
||||||
|
|
|
@ -165,7 +165,7 @@ class BaseUserManager(models.Manager):
|
||||||
return get_random_string(length, allowed_chars)
|
return get_random_string(length, allowed_chars)
|
||||||
|
|
||||||
def get_by_natural_key(self, username):
|
def get_by_natural_key(self, username):
|
||||||
return self.get(**{getattr(self.model, 'USERNAME_FIELD', 'username'): username})
|
return self.get(**{self.model.USERNAME_FIELD: username})
|
||||||
|
|
||||||
|
|
||||||
class UserManager(BaseUserManager):
|
class UserManager(BaseUserManager):
|
||||||
|
@ -227,6 +227,7 @@ def _user_has_module_perms(user, app_label):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class AbstractBaseUser(models.Model):
|
class AbstractBaseUser(models.Model):
|
||||||
password = models.CharField(_('password'), max_length=128)
|
password = models.CharField(_('password'), max_length=128)
|
||||||
last_login = models.DateTimeField(_('last login'), default=timezone.now)
|
last_login = models.DateTimeField(_('last login'), default=timezone.now)
|
||||||
|
@ -236,6 +237,16 @@ class AbstractBaseUser(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
def get_username(self):
|
||||||
|
"Return the identifying username for this User"
|
||||||
|
return getattr(self, self.USERNAME_FIELD)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.get_username()
|
||||||
|
|
||||||
|
def natural_key(self):
|
||||||
|
return (self.get_username(),)
|
||||||
|
|
||||||
def is_anonymous(self):
|
def is_anonymous(self):
|
||||||
"""
|
"""
|
||||||
Always returns False. This is a way of comparing User objects to
|
Always returns False. This is a way of comparing User objects to
|
||||||
|
@ -277,7 +288,6 @@ class AbstractBaseUser(models.Model):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class AbstractUser(AbstractBaseUser):
|
class AbstractUser(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
An abstract base class implementing a fully featured User model with
|
An abstract base class implementing a fully featured User model with
|
||||||
|
@ -314,6 +324,7 @@ class AbstractUser(AbstractBaseUser):
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
|
USERNAME_FIELD = 'username'
|
||||||
REQUIRED_FIELDS = ['email']
|
REQUIRED_FIELDS = ['email']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -321,12 +332,6 @@ class AbstractUser(AbstractBaseUser):
|
||||||
verbose_name_plural = _('users')
|
verbose_name_plural = _('users')
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.username
|
|
||||||
|
|
||||||
def natural_key(self):
|
|
||||||
return (self.username,)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return "/users/%s/" % urlquote(self.username)
|
return "/users/%s/" % urlquote(self.username)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.comments.models import Comment
|
from django.contrib.comments.models import Comment
|
||||||
from django.utils.translation import ugettext_lazy as _, ungettext
|
from django.utils.translation import ugettext_lazy as _, ungettext
|
||||||
from django.contrib.comments import get_model
|
from django.contrib.comments import get_model
|
||||||
from django.contrib.comments.views.moderation import perform_flag, perform_approve, perform_delete
|
from django.contrib.comments.views.moderation import perform_flag, perform_approve, perform_delete
|
||||||
|
|
||||||
|
|
||||||
|
class UsernameSearch(object):
|
||||||
|
"""The User object may not be auth.User, so we need to provide
|
||||||
|
a mechanism for issuing the equivalent of a .filter(user__username=...)
|
||||||
|
search in CommentAdmin.
|
||||||
|
"""
|
||||||
|
def __str__(self):
|
||||||
|
return 'user__%s' % get_user_model().USERNAME_FIELD
|
||||||
|
|
||||||
|
|
||||||
class CommentsAdmin(admin.ModelAdmin):
|
class CommentsAdmin(admin.ModelAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None,
|
(None,
|
||||||
|
@ -24,7 +35,7 @@ class CommentsAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = 'submit_date'
|
date_hierarchy = 'submit_date'
|
||||||
ordering = ('-submit_date',)
|
ordering = ('-submit_date',)
|
||||||
raw_id_fields = ('user',)
|
raw_id_fields = ('user',)
|
||||||
search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address')
|
search_fields = ('comment', UsernameSearch(), 'user_name', 'user_email', 'user_url', 'ip_address')
|
||||||
actions = ["flag_comments", "approve_comments", "remove_comments"]
|
actions = ["flag_comments", "approve_comments", "remove_comments"]
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
|
|
|
@ -111,7 +111,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
if u.get_full_name():
|
if u.get_full_name():
|
||||||
userinfo["name"] = self.user.get_full_name()
|
userinfo["name"] = self.user.get_full_name()
|
||||||
elif not self.user_name:
|
elif not self.user_name:
|
||||||
userinfo["name"] = u.username
|
userinfo["name"] = u.get_username()
|
||||||
self._userinfo = userinfo
|
self._userinfo = userinfo
|
||||||
return self._userinfo
|
return self._userinfo
|
||||||
userinfo = property(_get_userinfo, doc=_get_userinfo.__doc__)
|
userinfo = property(_get_userinfo, doc=_get_userinfo.__doc__)
|
||||||
|
@ -192,7 +192,7 @@ class CommentFlag(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s flag of comment ID %s by %s" % \
|
return "%s flag of comment ID %s by %s" % \
|
||||||
(self.flag, self.comment_id, self.user.username)
|
(self.flag, self.comment_id, self.user.get_username())
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.flag_date is None:
|
if self.flag_date is None:
|
||||||
|
|
|
@ -15,7 +15,6 @@ from django.views.decorators.csrf import csrf_protect
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CommentPostBadRequest(http.HttpResponseBadRequest):
|
class CommentPostBadRequest(http.HttpResponseBadRequest):
|
||||||
"""
|
"""
|
||||||
Response returned when a comment post is invalid. If ``DEBUG`` is on a
|
Response returned when a comment post is invalid. If ``DEBUG`` is on a
|
||||||
|
@ -27,6 +26,7 @@ class CommentPostBadRequest(http.HttpResponseBadRequest):
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
self.content = render_to_string("comments/400-debug.html", {"why": why})
|
self.content = render_to_string("comments/400-debug.html", {"why": why})
|
||||||
|
|
||||||
|
|
||||||
@csrf_protect
|
@csrf_protect
|
||||||
@require_POST
|
@require_POST
|
||||||
def post_comment(request, next=None, using=None):
|
def post_comment(request, next=None, using=None):
|
||||||
|
@ -40,7 +40,7 @@ def post_comment(request, next=None, using=None):
|
||||||
data = request.POST.copy()
|
data = request.POST.copy()
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
if not data.get('name', ''):
|
if not data.get('name', ''):
|
||||||
data["name"] = request.user.get_full_name() or request.user.username
|
data["name"] = request.user.get_full_name() or request.user.get_username()
|
||||||
if not data.get('email', ''):
|
if not data.get('email', ''):
|
||||||
data["email"] = request.user.email
|
data["email"] = request.user.email
|
||||||
|
|
||||||
|
@ -137,4 +137,3 @@ comment_done = confirmation_view(
|
||||||
template="comments/posted.html",
|
template="comments/posted.html",
|
||||||
doc="""Display a "comment was posted" success page."""
|
doc="""Display a "comment was posted" success page."""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,12 @@ Methods
|
||||||
:class:`~django.contrib.auth.models.User` objects have the following custom
|
:class:`~django.contrib.auth.models.User` objects have the following custom
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
|
.. method:: models.User.get_username()
|
||||||
|
|
||||||
|
Returns the username for the user. Since the User model can be swapped
|
||||||
|
out, you should use this method instead of referencing the username
|
||||||
|
attribute directly.
|
||||||
|
|
||||||
.. method:: models.User.is_anonymous()
|
.. method:: models.User.is_anonymous()
|
||||||
|
|
||||||
Always returns ``False``. This is a way of differentiating
|
Always returns ``False``. This is a way of differentiating
|
||||||
|
@ -1829,8 +1835,9 @@ you should reference the user model using
|
||||||
currently active User model -- the custom User model if one is specified, or
|
currently active User model -- the custom User model if one is specified, or
|
||||||
:class:`~django.contrib.auth.User` otherwise.
|
:class:`~django.contrib.auth.User` otherwise.
|
||||||
|
|
||||||
In relations to the User model, you should specify the custom model using
|
When you define a foreign key or many-to-many relations to the User model,
|
||||||
the :setting:`AUTH_USER_MODEL` setting. For example::
|
you should specify the custom model using the :setting:`AUTH_USER_MODEL`
|
||||||
|
setting. For example::
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -1910,6 +1917,60 @@ password resets. You must then provide some key implementation details:
|
||||||
identifies the user in an informal way. It may also return the same
|
identifies the user in an informal way. It may also return the same
|
||||||
value as :meth:`django.contrib.auth.User.get_full_name()`.
|
value as :meth:`django.contrib.auth.User.get_full_name()`.
|
||||||
|
|
||||||
|
The following methods are available on any subclass of
|
||||||
|
:class:`~django.contrib.auth.models.AbstractBaseUser`::
|
||||||
|
|
||||||
|
.. class:: models.AbstractBaseUser
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.get_username()
|
||||||
|
|
||||||
|
Returns the value of the field nominated by ``USERNAME_FIELD``.
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.is_anonymous()
|
||||||
|
|
||||||
|
Always returns ``False``. This is a way of differentiating
|
||||||
|
from :class:`~django.contrib.auth.models.AnonymousUser` objects.
|
||||||
|
Generally, you should prefer using
|
||||||
|
:meth:`~django.contrib.auth.models.AbstractBaseUser.is_authenticated()` to this
|
||||||
|
method.
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.is_authenticated()
|
||||||
|
|
||||||
|
Always returns ``True``. This is a way to tell if the user has been
|
||||||
|
authenticated. This does not imply any permissions, and doesn't check
|
||||||
|
if the user is active - it only indicates that the user has provided a
|
||||||
|
valid username and password.
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.set_password(raw_password)
|
||||||
|
|
||||||
|
Sets the user's password to the given raw string, taking care of the
|
||||||
|
password hashing. Doesn't save the
|
||||||
|
:class:`~django.contrib.auth.models.AbstractBaseUser` object.
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.check_password(raw_password)
|
||||||
|
|
||||||
|
Returns ``True`` if the given raw string is the correct password for
|
||||||
|
the user. (This takes care of the password hashing in making the
|
||||||
|
comparison.)
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.set_unusable_password()
|
||||||
|
|
||||||
|
Marks the user as having no password set. This isn't the same as
|
||||||
|
having a blank string for a password.
|
||||||
|
:meth:`~django.contrib.auth.models.AbstractBaseUser.check_password()` for this user
|
||||||
|
will never return ``True``. Doesn't save the
|
||||||
|
:class:`~django.contrib.auth.models.AbstractBaseUser` object.
|
||||||
|
|
||||||
|
You may need this if authentication for your application takes place
|
||||||
|
against an existing external source such as an LDAP directory.
|
||||||
|
|
||||||
|
.. method:: models.AbstractBaseUser.has_usable_password()
|
||||||
|
|
||||||
|
Returns ``False`` if
|
||||||
|
:meth:`~django.contrib.auth.models.AbstractBaseUser.set_unusable_password()` has
|
||||||
|
been called for this user.
|
||||||
|
|
||||||
|
|
||||||
You should also define a custom manager for your User model. If your User
|
You should also define a custom manager for your User model. If your User
|
||||||
model defines `username` and `email` fields the same as Django's default User,
|
model defines `username` and `email` fields the same as Django's default User,
|
||||||
you can just install Django's
|
you can just install Django's
|
||||||
|
@ -1941,6 +2002,31 @@ additional methods:
|
||||||
Unlike `create_user()`, `create_superuser()` *must* require the caller
|
Unlike `create_user()`, `create_superuser()` *must* require the caller
|
||||||
to provider a password.
|
to provider a password.
|
||||||
|
|
||||||
|
:class:`~django.contrib.auth.models.BaseUserManager` provides the following
|
||||||
|
utility methods:
|
||||||
|
|
||||||
|
.. class:: models.BaseUserManager
|
||||||
|
.. method:: models.BaseUserManager.normalize_email(email)
|
||||||
|
|
||||||
|
A classmethod that normalizes email addresses by lowercasing
|
||||||
|
the domain portion of the email address.
|
||||||
|
|
||||||
|
.. method:: models.BaseUserManager.get_by_natural_key(username)
|
||||||
|
|
||||||
|
Retrieves a user instance using the contents of the field
|
||||||
|
nominated by ``USERNAME_FIELD``.
|
||||||
|
|
||||||
|
.. method:: models.BaseUserManager.make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
|
||||||
|
|
||||||
|
Returns a random password with the given length and given string of
|
||||||
|
allowed characters. (Note that the default value of ``allowed_chars``
|
||||||
|
doesn't contain letters that can cause user confusion, including:
|
||||||
|
|
||||||
|
* ``i``, ``l``, ``I``, and ``1`` (lowercase letter i, lowercase
|
||||||
|
letter L, uppercase letter i, and the number one)
|
||||||
|
* ``o``, ``O``, and ``0`` (uppercase letter o, lowercase letter o,
|
||||||
|
and zero)
|
||||||
|
|
||||||
Extending Django's default User
|
Extending Django's default User
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -2020,6 +2106,16 @@ control access of the User to admin content:
|
||||||
Returns True if the user has permission to access models in
|
Returns True if the user has permission to access models in
|
||||||
the given app.
|
the given app.
|
||||||
|
|
||||||
|
You will also need to register your custom User model with the admin. If
|
||||||
|
your custom User model extends :class:`~django.contrib.auth.models.AbstractUser`,
|
||||||
|
you can use Django's existing :class:`~django.contrib.auth.admin.UserAdmin`
|
||||||
|
class. However, if your User model extends
|
||||||
|
:class:`~django.contrib.auth.models.AbstractBaseUser`, you'll need to define
|
||||||
|
a custom ModelAdmin class. It may be possible to subclass the default
|
||||||
|
:class:`~django.contrib.auth.admin.UserAdmin`; however, you'll need to
|
||||||
|
override any of the definitions that refer to fields on
|
||||||
|
:class:`~django.contrib.auth.models.AbstractUser` that aren't on your
|
||||||
|
custom User class.
|
||||||
|
|
||||||
Custom users and Proxy models
|
Custom users and Proxy models
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -2036,11 +2132,11 @@ behavior into your User subclass.
|
||||||
A full example
|
A full example
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Here is an example of a full models.py for an admin-compliant custom
|
Here is an example of an admin-compliant custom user app. This user model uses
|
||||||
user app. This user model uses an email address as the username, and has a
|
an email address as the username, and has a required date of birth; it
|
||||||
required date of birth; it provides no permission checking, beyond a simple
|
provides no permission checking, beyond a simple `admin` flag on the user
|
||||||
`admin` flag on the user account. This model would be compatible with all
|
account. This model would be compatible with all the built-in auth forms and
|
||||||
the built-in auth forms and views, except for the User creation forms.
|
views, except for the User creation forms.
|
||||||
|
|
||||||
This code would all live in a ``models.py`` file for a custom
|
This code would all live in a ``models.py`` file for a custom
|
||||||
authentication app::
|
authentication app::
|
||||||
|
@ -2086,7 +2182,9 @@ authentication app::
|
||||||
class MyUser(AbstractBaseUser):
|
class MyUser(AbstractBaseUser):
|
||||||
email = models.EmailField(
|
email = models.EmailField(
|
||||||
verbose_name='email address',
|
verbose_name='email address',
|
||||||
max_length=255
|
max_length=255,
|
||||||
|
unique=True,
|
||||||
|
db_index=True,
|
||||||
)
|
)
|
||||||
date_of_birth = models.DateField()
|
date_of_birth = models.DateField()
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
|
@ -2124,6 +2222,87 @@ authentication app::
|
||||||
# Simplest possible answer: All admins are staff
|
# Simplest possible answer: All admins are staff
|
||||||
return self.is_admin
|
return self.is_admin
|
||||||
|
|
||||||
|
Then, to register this custom User model with Django's admin, the following
|
||||||
|
code would be required in ``admin.py``::
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from django.contrib.auth.forms import ReadOnlyPasswordHashField
|
||||||
|
|
||||||
|
from customauth.models import MyUser
|
||||||
|
|
||||||
|
|
||||||
|
class UserCreationForm(forms.ModelForm):
|
||||||
|
"""A form for creating new users. Includes all the required
|
||||||
|
fields, plus a repeated password."""
|
||||||
|
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
|
||||||
|
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MyUser
|
||||||
|
fields = ('email', 'date_of_birth')
|
||||||
|
|
||||||
|
def clean_password2(self):
|
||||||
|
# Check that the two password entries match
|
||||||
|
password1 = self.cleaned_data.get("password1")
|
||||||
|
password2 = self.cleaned_data.get("password2")
|
||||||
|
if password1 and password2 and password1 != password2:
|
||||||
|
raise forms.ValidationError('Passwords don't match')
|
||||||
|
return password2
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
# Save the provided password in hashed format
|
||||||
|
user = super(UserCreationForm, self).save(commit=False)
|
||||||
|
user.set_password(self.cleaned_data["password1"])
|
||||||
|
if commit:
|
||||||
|
user.save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class UserChangeForm(forms.ModelForm):
|
||||||
|
"""A form for updateing users. Includes all the fields on
|
||||||
|
the user, but replaces the password field with admin's
|
||||||
|
pasword hash display field.
|
||||||
|
"""
|
||||||
|
password = ReadOnlyPasswordHashField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MyUser
|
||||||
|
|
||||||
|
|
||||||
|
class MyUserAdmin(UserAdmin):
|
||||||
|
# The forms to add and change user instances
|
||||||
|
form = UserChangeForm
|
||||||
|
add_form = UserCreationForm
|
||||||
|
|
||||||
|
# The fields to be used in displaying the User model.
|
||||||
|
# These override the definitions on the base UserAdmin
|
||||||
|
# that reference specific fields on auth.User.
|
||||||
|
list_display = ('email', 'date_of_birth', 'is_admin')
|
||||||
|
list_filter = ('is_admin',)
|
||||||
|
fieldsets = (
|
||||||
|
(None, {'fields': ('email', 'password')}),
|
||||||
|
('Personal info', {'fields': ('date_of_birth',)}),
|
||||||
|
('Permissions', {'fields': ('is_admin',)}),
|
||||||
|
('Important dates', {'fields': ('last_login',)}),
|
||||||
|
)
|
||||||
|
add_fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'classes': ('wide',),
|
||||||
|
'fields': ('email', 'date_of_birth', 'password1', 'password2')}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
search_fields = ('email',)
|
||||||
|
ordering = ('email',)
|
||||||
|
filter_horizontal = ()
|
||||||
|
|
||||||
|
# Now register the new UserAdmin...
|
||||||
|
admin.site.register(MyUser, MyUserAdmin)
|
||||||
|
# ... and, since we're not using Django's builtin permissions,
|
||||||
|
# unregister the Group model from admin.
|
||||||
|
admin.site.unregister(Group)
|
||||||
|
|
||||||
.. _authentication-backends:
|
.. _authentication-backends:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue