sbdchd / django-types

:doughnut: Type stubs for Django
MIT License
202 stars 63 forks source link

Inverse of OneToOneField #59

Open lpil opened 3 years ago

lpil commented 3 years ago

Hello!

What should be used as the inverse of an OneToOneField?

I've been attempting something like the ForeignKey example but I've not been able to determine what exactly so far. https://github.com/sbdchd/django-types#foreignkey-ids-and-related-names-as-properties-in-orm-models

Thanks, Louis

sbdchd commented 3 years ago

Yeah don't really have any docs on those, but I think it would be similar to the ForeignKey where we have to do some if TYPE_CHECKING stuff

Using the django docs https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.OneToOneField,

from django.conf import settings
from django.db import models

class MySpecialUser(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    supervisor = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='supervisor_of',
    )

And docs say:

>>> user = User.objects.get(pk=1)
>>> hasattr(user, 'myspecialuser')
True
>>> hasattr(user, 'supervisor_of')
True

So we'd need to update User to have the properties, something like:

class User(models.Model):
    # some fields
    if TYPE_CHECKING:
        myspecialuser: MySpecialUser
        user: MySpecialUser

I think the if TYPE_CHECKING might not be necessary, so maybe:

class User(models.Model):
    # some fields
    myspecialuser: MySpecialUser
    user: MySpecialUser
lpil commented 3 years ago

Oh thank you! I thought that the model class would need to be wrapped in some other object. Clearly I've been overthinking this.

I'll try this out and if I can't find any problems perhaps I could add this to the README?

last-partizan commented 2 years ago

The problem with reverse OneToOneField, is it does not exists and will raise RelatedObjectDoesNotExist when there is not corresponding model.

> user = User.objects.get(pk=1)
> hasattr(user, 'myspecialuser')
False
> MySpecialUser.objects.create(user=user)
> hasattr(user, 'myspecialuser')
True

Any ideas how to type this?

sbdchd commented 2 years ago

Yikes, that's pretty dynamic. I'm not sure there is a good way. I think the best we can do is pretend the attr is always there.

last-partizan commented 2 years ago

This could lead to some unexpected errors.

But that's not something type checking should solve. I think it's better to define something like get_mysecialuser which would handle those exceptions.