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