Login

Alternative to User.get_profile()

Author:
jpwatts
Posted:
March 19, 2008
Language:
Python
Version:
.96
Score:
1 (after 1 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

daevaorn (on March 19, 2008):

And what is the sense?

#

jpwatts (on March 20, 2008):

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.

#

daevaorn (on March 20, 2008):

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.

#

jpwatts (on March 20, 2008):

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.