Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using AutoOneToOneField with proxy model #88

Open
johncpang opened this issue Sep 12, 2018 · 2 comments
Open

Using AutoOneToOneField with proxy model #88

johncpang opened this issue Sep 12, 2018 · 2 comments

Comments

@johncpang
Copy link

johncpang commented Sep 12, 2018

When using AutoOneToOneField with Proxy model, I've a hard time to get the related model. The similar setup with OneToOneField doesn't have problem. While I check the code I found a difference here:

class AutoOneToOneField(OneToOneField):
    def contribute_to_related_class(self, cls, related):
        setattr(
            cls,
            related.get_accessor_name(),
            AutoSingleRelatedObjectDescriptor(related)
        )

Studying Django's code:

class ForeignObject(RelatedField):
    def contribute_to_related_class(self, cls, related):
        if not self.remote_field.is_hidden() and not related.related_model._meta.swapped:
            setattr(cls._meta.concrete_model, related.get_accessor_name(), self.related_accessor_class(related))
            if self.remote_field.limit_choices_to:
                cls._meta.related_fkey_lookups.append(self.remote_field.limit_choices_to)

When calling setattr(...), should AutoOneToOneField also pass in cls._meta.concrete_model instead?

@johncpang
Copy link
Author

johncpang commented Sep 12, 2018

Just verified that cls should really be cls._meta.concrete_model.

Here is my project which use a proxy model of Auth.User to filter out staff and superuser, also to display by email instead of username. Since I use/mix with Firebase SDK, username is Firebase's UID and hidden from end user. My admin stuff can only recognize end users by their email addresses.

from django.contrib.auth.models import UserManager, User

class EndUserManager(UserManager):
	def get_queryset(self):
		return super().get_queryset().filter(is_staff=False, is_superuser=False)

class EndUser(User):
	objects = EndUserManager()

	class Meta:
		proxy = True

	def __str__(self):
		return "%s" % (self.email or self.username)

Now I've two models which both are One-To-One relation with EndUser, and display with email obtained from EndUser.__str__():

class Profile(models.Model):
	owner = AutoOneToOneField(EndUser, on_delete=models.CASCADE, related_name='profile')

	def __str__(self):
		return "%s" % self.owner

class Tutor(models.Model):
	owner = OneToOneField(EndUser, on_delete=models.CASCADE, related_name='tutor')

	def __str__(self):
		return "%s" % self.owner

When I obtain an instance of User and try to access .profile and .tutor, I got error for profile but no error for tutor. Here is my test done in python shell:

>>> user = User.objects.get(pk=100)
>>> user.profile
AttributeError: 'User' object has no attribute 'profile'
>>> user.tutor
<Tutor: 100@brewingapps.com>

I know I can obtain instance of EndUser by a database call. Since I want to avoid that, I force User to become EndUser (such as self.request.user.__class__ = EndUser). At first it seems working, at least I didn't see any errors, but with AutoOneToOneField, it doesn't.

Using a patched version AutoOneToOneField does resolve the problem.

class FixedAutoOneToOneField(AutoOneToOneField):

	def contribute_to_related_class(self, cls, related):
		setattr(
			cls._meta.concrete_model,
			related.get_accessor_name(),
			AutoSingleRelatedObjectDescriptor(related)
		)

@skorokithakis
Copy link
Owner

Thanks for this research! Unfortunately, I've never used the AutoOneToOne model myself, so you know more than me at this point. Would you like to submit a PR with the fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants