How to update Django user profile from ForeignKey to OneToOneField
In one of my projects for the user profile model was used ForeignKey field.
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
This leads to a lot of unnecessary queries when you want to access to related data, like:
{% raw %}
{% for item in items %}
{{ item.user.get_profile.company }}
{% endfor %}
{% endraw %}
But as the unique=True
, this means that for each user, there is only one profile.
For other cases, it is possible to check this with following query:
from django.db.models import Count
UserProfile.objects.annotate(user_count=Count('user')).filter(user_count__gt=1)
In this situation it is better to replace the ForeignKey with OneToOneField.
user = models.OneToOneField(User, related_name='userprofile', primary_key=True, db_column='user_id')
db_column='user_id' #because it already has the required values
related_name='userprofile' #because there may be requests like User.objects.filter(userprofile__company=...)
Then you need to update the database, I use the south, so:
mysqldump .... #for backup
python manage.py schemamigration app --auto
python manage.py migrate app
You can then make another migration to rename user_id to id:
user = models.OneToOneField(User, related_name='userprofile', primary_key=True, db_column='id')
Then in the templates user.get_profile
must be replaced with user.userprofile
and user.userprofile.id
with user.id
.
And in the code user.get_profile()
with user.userprofile
.
After that, you can significantly reduce the number of queries using the necessary select_related
, like
Item.objects.select_related('user__userprofile__company').all()