This is an alternative to User.get_profile.
Rather than having you call User.get_profile
directly, this retrieves the profile instance for a User
and attaches the fields from the profile to the User
object when instantiated. The special methods for DateField
, FileField
, ImageField
and fields with choices
are also created.
Since the profile object still has to be retrieved from the database before its fields can be added to the User
, the costs for using this might outweigh the rewards unless you are heavily using profiles.
To install, place it in a module on your PYTHONPATH
and add it to INSTALLED_APPS
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.db import models
from django.db.models.signals import post_init
from django.dispatch import dispatcher
from django.utils.functional import curry
try:
getattr(settings, 'AUTH_PROFILE_MODULE')
except AttributeError:
raise ImproperlyConfigured(
"The 'AUTH_PROFILE_MODULE' setting isn't defined."
)
def _get_next_or_previous_by_profile_FIELD(self, field, is_next):
profile = self.get_profile()
next_or_previous = profile._get_next_or_previous_by_FIELD(field, is_next)
return next_or_previous.user
def inject_profile(sender, instance):
"""Attach fields from a ``Profile`` to a ``User`` instance.
Provide an alternative to ``User.get_profile`` by automatically attaching
the profile's fields to the ``User`` when it is instantiated. The special
methods for ``DateField``, ``FileField``, ``ImageField`` and fields with
choices are also created. ``_get_next_or_previous_by_profile_FIELD`` is
used instead of ``Model._get_next_or_previous_by_FIELD`` when currying the
``get_next_by_FIELD`` and ``get_previous_by_FIELD`` methods.
If the profile doesn't exist -- ``AUTH_PROFILE_MODULE`` isn't set or a
profile instance hasn't been created for the ``User`` -- nothing happens.
"""
try:
profile = instance.get_profile()
except ObjectDoesNotExist:
return None
# Don't overwrite existing fields on User model.
blacklist = [f.name for f in instance._meta.fields] + ['id', 'user']
for field in [f for f in profile._meta.fields if f.name not in blacklist]:
value = getattr(profile, field.name)
setattr(instance, field.name, value)
if field.choices:
setattr(
sender,
'get_%s_display' % field.name,
curry(sender._get_FIELD_display, field=field)
)
if isinstance(field, models.DateField) and not field.null:
setattr(
sender,
'get_next_by_%s' % field.name,
curry(
_get_next_or_previous_by_profile_FIELD,
field=field,
is_next=True
)
)
setattr(
sender,
'get_previous_by_%s' % field.name,
curry(
_get_next_or_previous_by_profile_FIELD,
field=field,
is_next=False
)
)
if isinstance(field, models.FileField):
setattr(
sender,
'get_%s_filename' % field.name,
curry(sender._get_FIELD_filename, field=field)
)
setattr(
sender,
'get_%s_url' % field.name,
curry(sender._get_FIELD_url, field=field)
)
setattr(
sender,
'get_%s_size' % field.name,
curry(sender._get_FIELD_size, field=field)
)
if isinstance(field, models.ImageField):
setattr(
sender,
'get_%s_height' % field.name,
curry(sender._get_FIELD_height, field=field)
)
setattr(
sender,
'get_%s_width' % field.name,
curry(sender._get_FIELD_width, field=field)
)
dispatcher.connect(inject_profile, sender=User, signal=post_init)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 5 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
And what is the sense?
#
It's not the only benefit, but a big one is not having to explain to your designer why they can do
{{ user.first_name }}
but not{{ user.gender }}
.{{ user.get_profile.gender }}
exposes implementation details of the authentication and profile systems that a designer shouldn't have to know or care about.#
The better way is to pass profile instance explicitly into context. Profile may have proxy properties to underlying user attributes: username,email,first_name,second_name. And there is no need in any magic. It is my opinion.
#
Passing the profile instance into context doesn't fix the problem that the separation between user and profile objects is an implementation detail.
Why should anyone but me care that I used a generic auth system and then augmented it with a site-specific profile?
If you'd prefer to take the opposite approach and add the user attributes to the profile instance, that's fine -- but I don't see how that's fundamentally any different from what I do here.
#
Please login first before commenting.