This commit is contained in:
parent
5fb22329a1
commit
c433fcb3fb
|
@ -26,7 +26,7 @@
|
|||
{% if user.is_active and user.is_staff %}
|
||||
<div id="user-tools">
|
||||
{% 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 %}
|
||||
{% url 'django-admindocs-docroot' as docsroot %}
|
||||
{% if docsroot %}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
{% for action in action_list %}
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block reset_link %}
|
||||
{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %}
|
||||
{% 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!" %}
|
||||
|
||||
|
|
|
@ -11,14 +11,13 @@ from django.shortcuts import get_object_or_404
|
|||
from django.template.response import TemplateResponse
|
||||
from django.utils.html import escape
|
||||
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.views.decorators.csrf import csrf_protect
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
|
||||
csrf_protect_m = method_decorator(csrf_protect)
|
||||
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
search_fields = ('name',)
|
||||
ordering = ('name',)
|
||||
|
@ -106,9 +105,10 @@ class UserAdmin(admin.ModelAdmin):
|
|||
raise PermissionDenied
|
||||
if extra_context is None:
|
||||
extra_context = {}
|
||||
username_field = self.model._meta.get_field(self.model.USERNAME_FIELD)
|
||||
defaults = {
|
||||
'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)
|
||||
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(User, UserAdmin)
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ class RemoteUserBackend(ModelBackend):
|
|||
# built-in safeguards for multiple threads.
|
||||
if self.create_unknown_user:
|
||||
user, created = UserModel.objects.get_or_create(**{
|
||||
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
||||
UserModel.USERNAME_FIELD: username
|
||||
})
|
||||
if created:
|
||||
user = self.configure_user(user)
|
||||
|
|
|
@ -52,6 +52,9 @@ class ReadOnlyPasswordHashField(forms.Field):
|
|||
kwargs.setdefault("required", False)
|
||||
super(ReadOnlyPasswordHashField, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean_password(self):
|
||||
return self.initial
|
||||
|
||||
|
||||
class UserCreationForm(forms.ModelForm):
|
||||
"""
|
||||
|
@ -118,9 +121,6 @@ class UserChangeForm(forms.ModelForm):
|
|||
"this user's password, but you can change the password "
|
||||
"using <a href=\"password/\">this form</a>."))
|
||||
|
||||
def clean_password(self):
|
||||
return self.initial["password"]
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
|
||||
|
@ -160,7 +160,7 @@ class AuthenticationForm(forms.Form):
|
|||
|
||||
# Set the label for the "username" field.
|
||||
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)
|
||||
|
||||
def clean(self):
|
||||
|
|
|
@ -34,7 +34,7 @@ class Command(BaseCommand):
|
|||
|
||||
try:
|
||||
u = UserModel.objects.using(options.get('database')).get(**{
|
||||
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
||||
UserModel.USERNAME_FIELD: username
|
||||
})
|
||||
except UserModel.DoesNotExist:
|
||||
raise CommandError("user '%s' does not exist" % username)
|
||||
|
|
|
@ -42,7 +42,7 @@ class Command(BaseCommand):
|
|||
|
||||
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
|
||||
|
||||
# If not provided, create the user with an unusable password
|
||||
|
@ -74,7 +74,7 @@ class Command(BaseCommand):
|
|||
|
||||
# Get a username
|
||||
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:
|
||||
input_msg = capfirst(username_field.verbose_name)
|
||||
if default_username:
|
||||
|
@ -91,7 +91,7 @@ class Command(BaseCommand):
|
|||
continue
|
||||
try:
|
||||
UserModel.objects.using(database).get(**{
|
||||
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
||||
UserModel.USERNAME_FIELD: username
|
||||
})
|
||||
except UserModel.DoesNotExist:
|
||||
pass
|
||||
|
|
|
@ -55,7 +55,7 @@ class RemoteUserMiddleware(object):
|
|||
# getting passed in the headers, then the correct user is already
|
||||
# persisted in the session and we don't need to continue.
|
||||
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
|
||||
# We are seeing this user for the first time in this session, attempt
|
||||
# to authenticate the user.
|
||||
|
@ -75,6 +75,6 @@ class RemoteUserMiddleware(object):
|
|||
backend = auth.load_backend(backend_str)
|
||||
try:
|
||||
username = backend.clean_username(username)
|
||||
except AttributeError: # Backend has no clean_username method.
|
||||
except AttributeError: # Backend has no clean_username method.
|
||||
pass
|
||||
return username
|
||||
|
|
|
@ -165,7 +165,7 @@ class BaseUserManager(models.Manager):
|
|||
return get_random_string(length, allowed_chars)
|
||||
|
||||
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):
|
||||
|
@ -227,6 +227,7 @@ def _user_has_module_perms(user, app_label):
|
|||
return False
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class AbstractBaseUser(models.Model):
|
||||
password = models.CharField(_('password'), max_length=128)
|
||||
last_login = models.DateTimeField(_('last login'), default=timezone.now)
|
||||
|
@ -236,6 +237,16 @@ class AbstractBaseUser(models.Model):
|
|||
class Meta:
|
||||
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):
|
||||
"""
|
||||
Always returns False. This is a way of comparing User objects to
|
||||
|
@ -277,7 +288,6 @@ class AbstractBaseUser(models.Model):
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class AbstractUser(AbstractBaseUser):
|
||||
"""
|
||||
An abstract base class implementing a fully featured User model with
|
||||
|
@ -314,6 +324,7 @@ class AbstractUser(AbstractBaseUser):
|
|||
|
||||
objects = UserManager()
|
||||
|
||||
USERNAME_FIELD = 'username'
|
||||
REQUIRED_FIELDS = ['email']
|
||||
|
||||
class Meta:
|
||||
|
@ -321,12 +332,6 @@ class AbstractUser(AbstractBaseUser):
|
|||
verbose_name_plural = _('users')
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
def natural_key(self):
|
||||
return (self.username,)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "/users/%s/" % urlquote(self.username)
|
||||
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.comments.models import Comment
|
||||
from django.utils.translation import ugettext_lazy as _, ungettext
|
||||
from django.contrib.comments import get_model
|
||||
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):
|
||||
fieldsets = (
|
||||
(None,
|
||||
|
@ -24,7 +35,7 @@ class CommentsAdmin(admin.ModelAdmin):
|
|||
date_hierarchy = 'submit_date'
|
||||
ordering = ('-submit_date',)
|
||||
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"]
|
||||
|
||||
def get_actions(self, request):
|
||||
|
|
|
@ -19,14 +19,14 @@ class BaseCommentAbstractModel(models.Model):
|
|||
"""
|
||||
|
||||
# Content-object field
|
||||
content_type = models.ForeignKey(ContentType,
|
||||
content_type = models.ForeignKey(ContentType,
|
||||
verbose_name=_('content type'),
|
||||
related_name="content_type_set_for_%(class)s")
|
||||
object_pk = models.TextField(_('object ID'))
|
||||
object_pk = models.TextField(_('object ID'))
|
||||
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
|
||||
|
||||
# Metadata about the comment
|
||||
site = models.ForeignKey(Site)
|
||||
site = models.ForeignKey(Site)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -50,21 +50,21 @@ class Comment(BaseCommentAbstractModel):
|
|||
# Who posted this comment? If ``user`` is set then it was an authenticated
|
||||
# user; otherwise at least user_name should have been set and the comment
|
||||
# was posted by a non-authenticated user.
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
|
||||
blank=True, null=True, related_name="%(class)s_comments")
|
||||
user_name = models.CharField(_("user's name"), max_length=50, blank=True)
|
||||
user_email = models.EmailField(_("user's email address"), blank=True)
|
||||
user_url = models.URLField(_("user's URL"), blank=True)
|
||||
user_name = models.CharField(_("user's name"), max_length=50, blank=True)
|
||||
user_email = models.EmailField(_("user's email address"), blank=True)
|
||||
user_url = models.URLField(_("user's URL"), blank=True)
|
||||
|
||||
comment = models.TextField(_('comment'), max_length=COMMENT_MAX_LENGTH)
|
||||
|
||||
# Metadata about the comment
|
||||
submit_date = models.DateTimeField(_('date/time submitted'), default=None)
|
||||
ip_address = models.IPAddressField(_('IP address'), blank=True, null=True)
|
||||
is_public = models.BooleanField(_('is public'), default=True,
|
||||
ip_address = models.IPAddressField(_('IP address'), blank=True, null=True)
|
||||
is_public = models.BooleanField(_('is public'), default=True,
|
||||
help_text=_('Uncheck this box to make the comment effectively ' \
|
||||
'disappear from the site.'))
|
||||
is_removed = models.BooleanField(_('is removed'), default=False,
|
||||
is_removed = models.BooleanField(_('is removed'), default=False,
|
||||
help_text=_('Check this box if the comment is inappropriate. ' \
|
||||
'A "This comment has been removed" message will ' \
|
||||
'be displayed instead.'))
|
||||
|
@ -96,9 +96,9 @@ class Comment(BaseCommentAbstractModel):
|
|||
"""
|
||||
if not hasattr(self, "_userinfo"):
|
||||
userinfo = {
|
||||
"name" : self.user_name,
|
||||
"email" : self.user_email,
|
||||
"url" : self.user_url
|
||||
"name": self.user_name,
|
||||
"email": self.user_email,
|
||||
"url": self.user_url
|
||||
}
|
||||
if self.user_id:
|
||||
u = self.user
|
||||
|
@ -111,7 +111,7 @@ class Comment(BaseCommentAbstractModel):
|
|||
if u.get_full_name():
|
||||
userinfo["name"] = self.user.get_full_name()
|
||||
elif not self.user_name:
|
||||
userinfo["name"] = u.username
|
||||
userinfo["name"] = u.get_username()
|
||||
self._userinfo = userinfo
|
||||
return self._userinfo
|
||||
userinfo = property(_get_userinfo, doc=_get_userinfo.__doc__)
|
||||
|
@ -174,9 +174,9 @@ class CommentFlag(models.Model):
|
|||
design users are only allowed to flag a comment with a given flag once;
|
||||
if you want rating look elsewhere.
|
||||
"""
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), related_name="comment_flags")
|
||||
comment = models.ForeignKey(Comment, verbose_name=_('comment'), related_name="flags")
|
||||
flag = models.CharField(_('flag'), max_length=30, db_index=True)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), related_name="comment_flags")
|
||||
comment = models.ForeignKey(Comment, verbose_name=_('comment'), related_name="flags")
|
||||
flag = models.CharField(_('flag'), max_length=30, db_index=True)
|
||||
flag_date = models.DateTimeField(_('date'), default=None)
|
||||
|
||||
# Constants for flag types
|
||||
|
@ -192,7 +192,7 @@ class CommentFlag(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
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):
|
||||
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
|
||||
|
||||
|
||||
|
||||
class CommentPostBadRequest(http.HttpResponseBadRequest):
|
||||
"""
|
||||
Response returned when a comment post is invalid. If ``DEBUG`` is on a
|
||||
|
@ -27,6 +26,7 @@ class CommentPostBadRequest(http.HttpResponseBadRequest):
|
|||
if settings.DEBUG:
|
||||
self.content = render_to_string("comments/400-debug.html", {"why": why})
|
||||
|
||||
|
||||
@csrf_protect
|
||||
@require_POST
|
||||
def post_comment(request, next=None, using=None):
|
||||
|
@ -40,7 +40,7 @@ def post_comment(request, next=None, using=None):
|
|||
data = request.POST.copy()
|
||||
if request.user.is_authenticated():
|
||||
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', ''):
|
||||
data["email"] = request.user.email
|
||||
|
||||
|
@ -98,8 +98,8 @@ def post_comment(request, next=None, using=None):
|
|||
]
|
||||
return render_to_response(
|
||||
template_list, {
|
||||
"comment" : form.data.get("comment", ""),
|
||||
"form" : form,
|
||||
"comment": form.data.get("comment", ""),
|
||||
"form": form,
|
||||
"next": next,
|
||||
},
|
||||
RequestContext(request, {})
|
||||
|
@ -113,9 +113,9 @@ def post_comment(request, next=None, using=None):
|
|||
|
||||
# Signal that the comment is about to be saved
|
||||
responses = signals.comment_will_be_posted.send(
|
||||
sender = comment.__class__,
|
||||
comment = comment,
|
||||
request = request
|
||||
sender=comment.__class__,
|
||||
comment=comment,
|
||||
request=request
|
||||
)
|
||||
|
||||
for (receiver, response) in responses:
|
||||
|
@ -126,15 +126,14 @@ def post_comment(request, next=None, using=None):
|
|||
# Save the comment and signal that it was saved
|
||||
comment.save()
|
||||
signals.comment_was_posted.send(
|
||||
sender = comment.__class__,
|
||||
comment = comment,
|
||||
request = request
|
||||
sender=comment.__class__,
|
||||
comment=comment,
|
||||
request=request
|
||||
)
|
||||
|
||||
return next_redirect(data, next, comment_done, c=comment._get_pk_val())
|
||||
|
||||
comment_done = confirmation_view(
|
||||
template = "comments/posted.html",
|
||||
doc = """Display a "comment was posted" success page."""
|
||||
template="comments/posted.html",
|
||||
doc="""Display a "comment was posted" success page."""
|
||||
)
|
||||
|
||||
|
|
|
@ -149,6 +149,12 @@ Methods
|
|||
:class:`~django.contrib.auth.models.User` objects have the following custom
|
||||
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()
|
||||
|
||||
Always returns ``False``. This is a way of differentiating
|
||||
|
@ -1826,11 +1832,12 @@ different User model.
|
|||
Instead of referring to :class:`~django.contrib.auth.models.User` directly,
|
||||
you should reference the user model using
|
||||
:func:`django.contrib.auth.get_user_model()`. This method will return the
|
||||
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.
|
||||
|
||||
In relations to the User model, you should specify the custom model using
|
||||
the :setting:`AUTH_USER_MODEL` setting. For example::
|
||||
When you define a foreign key or many-to-many relations to the User model,
|
||||
you should specify the custom model using the :setting:`AUTH_USER_MODEL`
|
||||
setting. For example::
|
||||
|
||||
from django.conf import settings
|
||||
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
|
||||
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
|
||||
model defines `username` and `email` fields the same as Django's default User,
|
||||
you can just install Django's
|
||||
|
@ -1941,6 +2002,31 @@ additional methods:
|
|||
Unlike `create_user()`, `create_superuser()` *must* require the caller
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -2020,6 +2106,16 @@ control access of the User to admin content:
|
|||
Returns True if the user has permission to access models in
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -2036,11 +2132,11 @@ behavior into your User subclass.
|
|||
A full example
|
||||
--------------
|
||||
|
||||
Here is an example of a full models.py for an admin-compliant custom
|
||||
user app. This user model uses an email address as the username, and has a
|
||||
required date of birth; it provides no permission checking, beyond a simple
|
||||
`admin` flag on the user account. This model would be compatible with all
|
||||
the built-in auth forms and views, except for the User creation forms.
|
||||
Here is an example of an admin-compliant custom user app. This user model uses
|
||||
an email address as the username, and has a required date of birth; it
|
||||
provides no permission checking, beyond a simple `admin` flag on the user
|
||||
account. This model would be compatible with all the built-in auth forms and
|
||||
views, except for the User creation forms.
|
||||
|
||||
This code would all live in a ``models.py`` file for a custom
|
||||
authentication app::
|
||||
|
@ -2086,7 +2182,9 @@ authentication app::
|
|||
class MyUser(AbstractBaseUser):
|
||||
email = models.EmailField(
|
||||
verbose_name='email address',
|
||||
max_length=255
|
||||
max_length=255,
|
||||
unique=True,
|
||||
db_index=True,
|
||||
)
|
||||
date_of_birth = models.DateField()
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
@ -2124,6 +2222,87 @@ authentication app::
|
|||
# Simplest possible answer: All admins are staff
|
||||
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:
|
||||
|
||||
|
|
Loading…
Reference in New Issue