Home > Software engineering >  Django - OneToOneField does not work - RelatedObjectDoesNotExist: User has no userdetail
Django - OneToOneField does not work - RelatedObjectDoesNotExist: User has no userdetail

Time:06-01

I am trying to extend the User model with this:

class UserDetail(models.Model):
    user = models.OneToOneField(to=User, on_delete=models.CASCADE, verbose_name="Používateľ")

    def __str__(self):
        return f"{self.user.first_name} {self.user.last_name} ({self.user.username}) / Celková hodnota všetkých objednávok používateľa: {self.total_value_of_users_orders} €"

    @property
    def total_value_of_users_orders(self):
        all_orders = Order.objects.all().filter(user=self.user, status__in=["NW", "WP", "PD", "IP", "ST", "DN"])
        total_value = 0
        for order in all_orders:
            total_value  = order.total_value
        return total_value

However, when I try to access the total_value_of_users_orders property, it does not work:

from django.contrib.auth import get_user_model
User = get_user_model()
all_users = User.objects.all()

for u in all_users:
    u.userdetail.total_value_of_users_orders

it shows this exception:

RelatedObjectDoesNotExist: User has no userdetail.

What am I doing wrong? I expect all users to have the total_value_of_users_orders property which either returns 0 (if there are no Orders belonging to the given user) or the exact value (if there are Orders belonging to the given user).

Thank you

CodePudding user response:

You need to check for existence first:

if hasattr(u, 'userdetail'):
    print(u.userdetail.total_value_of_users_orders)

Note that the following will still give you an RelatedObjectDoesNotExist error:

if u.userdetail:
    print(u.userdetail.total_value_of_users_orders)

CodePudding user response:

Use django signal to create UserDetail at the time of User creation so that each user will have its own UserDetail and query can access total_value_of_users_order

from django.db.models.signals import post_save

# ... your models

def create_user_detail(sender, created, instance, **kwargs):
    if created:
        UserDetail.objects.create(user=instance)

# ...
post_save.connect(create_user_detail, sender=User)

CodePudding user response:

It means your user does not have a userdetail related one-to-one object in the database. You can create it when you need it or when you create a user.

There are several ways to handle it:

  1. Creating UserDetail object immediately after creating user. This can be easily done using django signals. (You may need to modify generated migration to create UserDetail for old ones.)
  2. Creating UserDetail object (if doesn't exist) when you want to access it. You can implement it yourself or use django-annoying's AutoOneToOneField.
  3. Checking if user has a userdetail or not (using hasattr) every time you want to access it. Read the documentation. (It works, but I don't recommend it anyway)

There are other options that may work much better if you can extend the User model, but it seems you already migrated it, so it won't be that easy.

  • Related