Mercurial > public > pelican-blog
view 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 |
line wrap: on
line source
Retiring get_profile and AUTH_PROFILE_MODULE ############################################ :date: 2014-05-24 14:08 :tags: django :slug: retiring-get-profile-and-auth-profile-module :author: Brian Neal :summary: Here is how I prepared for the upcoming deprecation in Django 1.7 of the get_profile() and AUTH_PROFILE_MODULE functionality. Django 1.7 is coming soon! This looks like an exciting release with cool features like migrations and an app loading framework. However it also comes with many deprecations_ that we need to prepare for. In particular, now is the time to finally move away from the AUTH_PROFILE_MODULE_ setting and the `get_profile()`_ method on the ``User`` model. I had been putting this off until the last minute, but after finally sitting down and doing it, I am happy to report it isn't difficult. My initial fears about removing this feature were unfounded. Originally I thought perhaps database schema changes were needed. And without migrations support I would be doomed. I was also confusing this deprecation with the introduction of the new `configurable user model`_ from Django 1.5. It turns out that no database changes are needed to migrate away from ``get_profile()``. You simply replace your profile model's ForeignKey_ with a OneToOneField_. This does not change the database schema at all. You can observe this yourself by running the ``manage.py sql`` command before and after making the change. You then need to fix up your code by removing all ``get_profile()`` calls. Django 1.5 introduced the ability to define your own user model, but you do not need to jump to this to move away from ``get_profile()``. That is a separate decision to make. You may in fact wish to abandon a separate user profile model and create your own combined user model. But this can be done after following the steps I outline below. In my case, I decided it makes more sense to maintain a separate user profile model. It did not feel right to add non-authentication related fields into the user model. It is true that you will incur the overhead of database joins with a separate profile model. However, in practice, at least for me, this was not particularly burdensome. Most of the time this meant adding an additional `select_related()`_ call on an ORM query. Your mileage may vary of course. But if you are happy with the additional profile model solution you need only follow these steps and be done. Here is an outline of the steps I followed to remove the ``get_profile()`` functionality from my code base. Remove the AUTH_PROFILE_MODULE setting -------------------------------------- This step is easy. Find your ``AUTH_PROFILE_MODULE`` setting and delete it. .. sourcecode:: python # In your settings.py: AUTH_PROFILE_MODULE = 'myapp.profile' # delete this line Change your profile model ------------------------- In this step we change the ``ForeignKey`` to Django's ``User`` model to a ``OneToOneField``. Again, this does not affect the database schema. Before: .. sourcecode:: python from django.db import models from django.contrib.auth.models import User class Profile(models.Model): """model to represent additional information about users""" user = models.ForeignKey(User, unique=True) # change this line # ... other custom stuff here After: .. sourcecode:: python from django.db import models from django.contrib.auth.models import User class Profile(models.Model): """model to represent additional information about users""" user = models.OneToOneField(User) # ... other custom stuff here Update the code --------------- You now need to go through your code and find all the ``get_profile()`` calls and replace them. For example: Before: .. sourcecode:: python profile = request.user.get_profile() After: .. sourcecode:: python profile = request.user.profile After making all these changes you are basically done! Congratulations! Cleanup and sanity checking --------------------------- There are a few other things I recommend doing at this point. First I would search your code for all occurrences of your ``Profile`` model. There were several places in my code where the extra database calls caused by ``get_profile`` were hurting my performance. I had replaced ``get_profile()`` with custom code that queried the ``Profile`` manager directly to reduce database calls. I reviewed these changes and replaced them with ``select_related()`` calls. This simplified things. I love deleting lines of code. While I was doing this, I noticed a few places that I could use `prefetch_related()`_ (on other related fields) since my code pre-dated this method. Next you should run any unit tests and verify they still pass. Hopefully you have unit tests, right? It was a big relief to have my tests passing after making these numerous changes. Finally I would suggest using a profiler like the `Django Debug Toolbar`_ to do some spot checking on the number of SQL queries your pages generate after making these changes. In most cases I was able to reduce the number of calls or at least keep them the same. Using the magic of version control, I ran a version of my site both with and without my changes side-by-side. Using the profiler I convinced myself I was in fact making things better and not worse. Conclusion ---------- I am not familiar with early Django history, but I'm guessing that ``get_profile()`` must have pre-dated the OneToOneField_. After making the switch, my code seems more straightforward with less "magic". In any event, I hope this post will make you less fearful about this change. It was not the major change I had thought it was going to be. .. _deprecations: https://docs.djangoproject.com/en/1.6/releases/1.5/#auth-profile-module .. _AUTH_PROFILE_MODULE: https://docs.djangoproject.com/en/1.6/ref/settings/#auth-profile-module .. _get_profile(): https://docs.djangoproject.com/en/1.6/ref/contrib/auth/#django.contrib.auth.models.User.get_profile .. _configurable user model: https://docs.djangoproject.com/en/1.6/releases/1.5/#configurable-user-model .. _ForeignKey: https://docs.djangoproject.com/en/1.6/ref/models/fields/#foreignkey .. _OneToOneField: https://docs.djangoproject.com/en/1.6/ref/models/fields/#onetoonefield .. _select_related(): https://docs.djangoproject.com/en/1.6/ref/models/querysets/#select-related .. _prefetch_related(): https://docs.djangoproject.com/en/1.6/ref/models/querysets/#prefetch-related .. _Django Debug Toolbar: https://github.com/django-debug-toolbar/django-debug-toolbar