Reputation: 432
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
Upvotes: 2
Views: 456
Reputation: 79
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:
userdetail
or not (using hasattr
) every time you want to access it. Read the documentation. This is the correct way to go if the relationship is nullable (optional, not required). Only, it is ugly. It should have been as simple as if user.detail
. So maybe it can be improved.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.
In the end there is only two ways it can be: one to one relationship is either required or optional. if it is required, one shouldn't exist without the other. so they should be created together. if it is optional, it should be treated as nullable.
Upvotes: 3
Reputation: 629
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)
Upvotes: 2
Reputation: 3710
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)
Upvotes: 4