errorous
errorous

Reputation: 1071

Django: reverse OneToOneField matching without related_name

I have several models representing different type of user profiles,

class User(models.Model):
  pass

class ProfileA(models.Model):
  user = models.OneToOneField(User, related_name="%(app_label)s_%(class)s_related",)

class ProfileB(models.Model):
  user = models.OneToOneField(User, related_name="%(app_label)s_%(class)s_related",)

class ProfileC(models.Model):
  user = models.OneToOneField(User, related_name="%(app_label)s_%(class)s_related",)

Now, let's say I get the user instance: how can I get the related profile object? All three profile models(in my code I've 6 different profile models extending BaseProfile model, which is abstract) have additional fields, and I basically need the whole profile object accessible from within User object instance.

Upvotes: 2

Views: 1126

Answers (2)

Lord Elrond
Lord Elrond

Reputation: 16032

The docs recommend using hasattr, but in your case that can get a bit messy.

To simplify the process, I would subclass the base User model, then create a custom method to fetch it's own profile.

for example:

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    def get_profile(self):
        if hasattr(self, 'profile_a'):
            return self.profile_a
        elif hasattr(self, 'profile_b'):
            return self.profile_b
        elif hasattr(self, 'profile_c'):
            return self.profile_c

class ProfileA(models.Model):
    user = models.OneToOneField(User, related_name='profile_a', on_delete=models.CASCADE, primary_key=True)
    name = models.CharField(max_length=45)

class ProfileB(models.Model):
    user = models.OneToOneField(User, related_name='profile_b', on_delete=models.CASCADE, primary_key=True)
    name = models.CharField(max_length=45)

class ProfileC(models.Model):
    user = models.OneToOneField(User, related_name='profile_c', on_delete=models.CASCADE, primary_key=True)
    name = models.CharField(max_length=45)

Now you can get the profile of any User object:

def view(request):
    user = request.user
    print(user.get_profile())

Also, don't forget to tell django that you created a new user model, by adding AUTH_USER_MODEL = 'yourapp.User' to your settings.py.

Upvotes: 1

Dash Winterson
Dash Winterson

Reputation: 1295

the app name is the name of the app in installed apps with dot references replaced with underscores, for instance for the app myapp

user = User.objects.get(...)
a = user.myapp_profilea_related
b = user.myapp_profileb_related
c = user.myapp_profilec_related

you can get these values programatically via the _meta property which is an instance of your Meta subclass:

user = User.objects.get(...)
a = getattr(user, f'{ProfileA._meta.app_label}_{ProfileA._meta.model_name}_related')
b = getattr(user, f'{ProfileB._meta.app_label}_{ProfileB._meta.model_name}_related')
c = getattr(user, f'{ProfileC._meta.app_label}_{ProfileC._meta.model_name}_related')

Upvotes: 1

Related Questions