bgneal@16
|
1 Retiring get_profile and AUTH_PROFILE_MODULE
|
bgneal@16
|
2 ############################################
|
bgneal@16
|
3
|
bgneal@16
|
4 :date: 2014-05-24 14:08
|
bgneal@16
|
5 :tags: django
|
bgneal@16
|
6 :slug: retiring-get-profile-and-auth-profile-module
|
bgneal@16
|
7 :author: Brian Neal
|
bgneal@16
|
8 :summary: Here is how I prepared for the upcoming deprecation in Django 1.7 of
|
bgneal@16
|
9 the get_profile() and AUTH_PROFILE_MODULE functionality.
|
bgneal@16
|
10
|
bgneal@16
|
11 Django 1.7 is coming soon! This looks like an exciting release with cool
|
bgneal@16
|
12 features like migrations and an app loading framework. However it also comes
|
bgneal@16
|
13 with many deprecations_ that we need to prepare for. In particular, now is the
|
bgneal@16
|
14 time to finally move away from the AUTH_PROFILE_MODULE_ setting and the
|
bgneal@16
|
15 `get_profile()`_ method on the ``User`` model. I had been putting this off
|
bgneal@16
|
16 until the last minute, but after finally sitting down and doing it, I am happy
|
bgneal@16
|
17 to report it isn't difficult.
|
bgneal@16
|
18
|
bgneal@16
|
19 My initial fears about removing this feature were unfounded. Originally
|
bgneal@16
|
20 I thought perhaps database schema changes were needed. And without migrations
|
bgneal@16
|
21 support I would be doomed. I was also confusing this deprecation with the
|
bgneal@16
|
22 introduction of the new `configurable user model`_ from Django 1.5.
|
bgneal@16
|
23
|
bgneal@16
|
24 It turns out that no database changes are needed to migrate away from
|
bgneal@16
|
25 ``get_profile()``. You simply replace your profile model's ForeignKey_ with
|
bgneal@16
|
26 a OneToOneField_. This does not change the database schema at all. You can
|
bgneal@16
|
27 observe this yourself by running the ``manage.py sql`` command before and after
|
bgneal@16
|
28 making the change. You then need to fix up your code by removing all
|
bgneal@16
|
29 ``get_profile()`` calls.
|
bgneal@16
|
30
|
bgneal@16
|
31 Django 1.5 introduced the ability to define your own user model, but you do not
|
bgneal@16
|
32 need to jump to this to move away from ``get_profile()``. That is a separate
|
bgneal@16
|
33 decision to make. You may in fact wish to abandon a separate user profile model
|
bgneal@16
|
34 and create your own combined user model. But this can be done after following
|
bgneal@16
|
35 the steps I outline below.
|
bgneal@16
|
36
|
bgneal@16
|
37 In my case, I decided it makes more sense to maintain a separate user profile
|
bgneal@16
|
38 model. It did not feel right to add non-authentication related fields into the
|
bgneal@16
|
39 user model. It is true that you will incur the overhead of database joins with
|
bgneal@16
|
40 a separate profile model. However, in practice, at least for me, this was not
|
bgneal@16
|
41 particularly burdensome. Most of the time this meant adding an additional
|
bgneal@16
|
42 `select_related()`_ call on an ORM query. Your mileage may vary of course. But
|
bgneal@16
|
43 if you are happy with the additional profile model solution you need only
|
bgneal@16
|
44 follow these steps and be done.
|
bgneal@16
|
45
|
bgneal@16
|
46 Here is an outline of the steps I followed to remove the ``get_profile()``
|
bgneal@16
|
47 functionality from my code base.
|
bgneal@16
|
48
|
bgneal@16
|
49 Remove the AUTH_PROFILE_MODULE setting
|
bgneal@16
|
50 --------------------------------------
|
bgneal@16
|
51
|
bgneal@16
|
52 This step is easy. Find your ``AUTH_PROFILE_MODULE`` setting and delete it.
|
bgneal@16
|
53
|
bgneal@16
|
54 .. sourcecode:: python
|
bgneal@16
|
55
|
bgneal@16
|
56 # In your settings.py:
|
bgneal@16
|
57 AUTH_PROFILE_MODULE = 'myapp.profile' # delete this line
|
bgneal@16
|
58
|
bgneal@16
|
59
|
bgneal@16
|
60 Change your profile model
|
bgneal@16
|
61 -------------------------
|
bgneal@16
|
62
|
bgneal@16
|
63 In this step we change the ``ForeignKey`` to Django's ``User`` model to
|
bgneal@16
|
64 a ``OneToOneField``. Again, this does not affect the database schema.
|
bgneal@16
|
65
|
bgneal@16
|
66 Before:
|
bgneal@16
|
67
|
bgneal@16
|
68 .. sourcecode:: python
|
bgneal@16
|
69
|
bgneal@16
|
70 from django.db import models
|
bgneal@16
|
71 from django.contrib.auth.models import User
|
bgneal@16
|
72
|
bgneal@16
|
73 class Profile(models.Model):
|
bgneal@16
|
74 """model to represent additional information about users"""
|
bgneal@16
|
75 user = models.ForeignKey(User, unique=True) # change this line
|
bgneal@16
|
76 # ... other custom stuff here
|
bgneal@16
|
77
|
bgneal@16
|
78 After:
|
bgneal@16
|
79
|
bgneal@16
|
80 .. sourcecode:: python
|
bgneal@16
|
81
|
bgneal@16
|
82 from django.db import models
|
bgneal@16
|
83 from django.contrib.auth.models import User
|
bgneal@16
|
84
|
bgneal@16
|
85 class Profile(models.Model):
|
bgneal@16
|
86 """model to represent additional information about users"""
|
bgneal@16
|
87 user = models.OneToOneField(User)
|
bgneal@16
|
88 # ... other custom stuff here
|
bgneal@16
|
89
|
bgneal@16
|
90 Update the code
|
bgneal@16
|
91 ---------------
|
bgneal@16
|
92
|
bgneal@16
|
93 You now need to go through your code and find all the ``get_profile()`` calls
|
bgneal@16
|
94 and replace them. For example:
|
bgneal@16
|
95
|
bgneal@16
|
96 Before:
|
bgneal@16
|
97
|
bgneal@16
|
98 .. sourcecode:: python
|
bgneal@16
|
99
|
bgneal@16
|
100 profile = request.user.get_profile()
|
bgneal@16
|
101
|
bgneal@16
|
102 After:
|
bgneal@16
|
103
|
bgneal@16
|
104 .. sourcecode:: python
|
bgneal@16
|
105
|
bgneal@16
|
106 profile = request.user.profile
|
bgneal@16
|
107
|
bgneal@16
|
108 After making all these changes you are basically done! Congratulations!
|
bgneal@16
|
109
|
bgneal@16
|
110 Cleanup and sanity checking
|
bgneal@16
|
111 ---------------------------
|
bgneal@16
|
112
|
bgneal@16
|
113 There are a few other things I recommend doing at this point. First I would
|
bgneal@16
|
114 search your code for all occurrences of your ``Profile`` model. There were
|
bgneal@16
|
115 several places in my code where the extra database calls caused by
|
bgneal@16
|
116 ``get_profile`` were hurting my performance. I had replaced ``get_profile()``
|
bgneal@16
|
117 with custom code that queried the ``Profile`` manager directly to reduce
|
bgneal@16
|
118 database calls. I reviewed these changes and replaced them with
|
bgneal@16
|
119 ``select_related()`` calls. This simplified things. I love deleting lines of
|
bgneal@16
|
120 code. While I was doing this, I noticed a few places that I could use
|
bgneal@16
|
121 `prefetch_related()`_ (on other related fields) since my code pre-dated this
|
bgneal@16
|
122 method.
|
bgneal@16
|
123
|
bgneal@16
|
124 Next you should run any unit tests and verify they still pass. Hopefully you
|
bgneal@16
|
125 have unit tests, right? It was a big relief to have my tests passing after
|
bgneal@16
|
126 making these numerous changes.
|
bgneal@16
|
127
|
bgneal@16
|
128 Finally I would suggest using a profiler like the `Django Debug Toolbar`_ to do
|
bgneal@16
|
129 some spot checking on the number of SQL queries your pages generate after making
|
bgneal@16
|
130 these changes. In most cases I was able to reduce the number of calls or at
|
bgneal@16
|
131 least keep them the same. Using the magic of version control, I ran a version
|
bgneal@16
|
132 of my site both with and without my changes side-by-side. Using the profiler
|
bgneal@16
|
133 I convinced myself I was in fact making things better and not worse.
|
bgneal@16
|
134
|
bgneal@16
|
135 Conclusion
|
bgneal@16
|
136 ----------
|
bgneal@16
|
137
|
bgneal@16
|
138 I am not familiar with early Django history, but I'm guessing that
|
bgneal@16
|
139 ``get_profile()`` must have pre-dated the OneToOneField_. After making the
|
bgneal@16
|
140 switch, my code seems more straightforward with less "magic". In any event,
|
bgneal@16
|
141 I hope this post will make you less fearful about this change. It was not the
|
bgneal@16
|
142 major change I had thought it was going to be.
|
bgneal@16
|
143
|
bgneal@16
|
144
|
bgneal@16
|
145 .. _deprecations: https://docs.djangoproject.com/en/1.6/releases/1.5/#auth-profile-module
|
bgneal@16
|
146 .. _AUTH_PROFILE_MODULE: https://docs.djangoproject.com/en/1.6/ref/settings/#auth-profile-module
|
bgneal@16
|
147 .. _get_profile(): https://docs.djangoproject.com/en/1.6/ref/contrib/auth/#django.contrib.auth.models.User.get_profile
|
bgneal@16
|
148 .. _configurable user model: https://docs.djangoproject.com/en/1.6/releases/1.5/#configurable-user-model
|
bgneal@16
|
149 .. _ForeignKey: https://docs.djangoproject.com/en/1.6/ref/models/fields/#foreignkey
|
bgneal@16
|
150 .. _OneToOneField: https://docs.djangoproject.com/en/1.6/ref/models/fields/#onetoonefield
|
bgneal@16
|
151 .. _select_related(): https://docs.djangoproject.com/en/1.6/ref/models/querysets/#select-related
|
bgneal@16
|
152 .. _prefetch_related(): https://docs.djangoproject.com/en/1.6/ref/models/querysets/#prefetch-related
|
bgneal@16
|
153 .. _Django Debug Toolbar: https://github.com/django-debug-toolbar/django-debug-toolbar
|