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

Polymorphic field queries appear to be broken by abstract classes #25

Open
cewing opened this issue May 24, 2013 · 3 comments
Open

Polymorphic field queries appear to be broken by abstract classes #25

cewing opened this issue May 24, 2013 · 3 comments

Comments

@cewing
Copy link

cewing commented May 24, 2013

Hi. I believe I've found a problem with the class inheritance hierarchy of this package.

I've been working with a page class derived from the FluentPageBase abstract class. This class provides a foreign key to django.contrib.auth.models.Group, and my intention was then to create a customized dispatcher and menu template tag that would filter the menu display and prevent access to pages that weren't shared with the current user.

The intention here was to be able to customize get_queryset in CmsPageDispatcher like so:

class RestrictedCmsPageDispatcher(CmsPageDispatcher):

    def get_queryset(self):
        """
        Return the QuerySet used to find the pages.
        """
        user = self.request.user
        # never anything for anonymous
        if not user.is_authenticated():
            return self.model.objects.none()

        if self.request.user.is_staff or self.request.user.is_superuser:
            return self.model.objects.published()

        restricted = self.model.objects.published()\
            .filter(~Q(instance_of=RestrictedFluentPage) |
                    Q(RestrictedFluentPage___restricted_to=None) |
                    Q(RestrictedFluentPage___restricted_to__in=user.groups.all()))

        return restricted

This should then return any "normal" page types, plus all restricted page types that are either open, or shared with a group to which the current user belongs.

Unfortunately, this does not work. The method polymorphic.query_translate.translate_polymorphic_field_path returns an appropriately modified path that lists the full inheritance structure of my class (htmlpage__fluentpagebase__restrictedfluentpage), but the UrlNodeManager does not recognize htmlpage as a valid query field.

Testing against the structure in the django-polymorphic tests where all the members of the class hierarchy are concrete works fine:

from polymorphic import ShowFieldType, PolymorphicModel
class Model2A(ShowFieldType, PolymorphicModel):
    field1 = models.CharField(max_length=10)
class Model2B(Model2A):
    field2 = models.CharField(max_length=10)
class Model2C(Model2B):
    field3 = models.CharField(max_length=10)
class Model2D(Model2C):
    field4 = models.CharField(max_length=10)

Here I can do Model2A.objects.filter(Q(Model2C___field3='a')) and it works great. I get all the Model2C instances with that value for field3.

I suspect that the problem may be in the abstract classes that are spread through this class hierarchy. Can you confirm this? If it is the case, is there any workaround available?

@vdboor
Copy link
Contributor

vdboor commented May 24, 2013

Hi, thanks for your report! I like the interesting case it presents. This is an area I want to look into as well; adding authorization hooks for backends.

I've encountered some problems with django-polymorphic in Django 1.5 before, because the class hierarchy has an abstract class, and proxy model too. The basic support for it got fixed in the recent 0.4.1 release.

HtmlPage is an abstract class, and Page is a proxy model. I suspect there is a bug in polymorphic to handle this. If you would be able to fix that, I can help you over there too (I recently became a co-maintainer of django-polymorphic).

As workaround, I'd be thinking about something like:

restricted = self.model.objects.published().filter(~Q(instance_of=RestrictedFluentPage)) \
           | RestrictedFluentPage.objects.published().filter(
                    Q(restricted_to=None) | Q(restricted_to__in=user.groups.all())
            )

You could also override get_object() to perform the check after retrieving the object.

@cewing
Copy link
Author

cewing commented May 25, 2013

Thanks for the quick feedback on this. I agree that the problem is in the way that polymorphic field lookup is accomplished. I don't think it's compatible at this point with abstract classes or proxy models. If I can find the time, I'll look further into it. Good to know you'd be able to manage updating!

I've opened an issue on the project where we are using this package in the hopes that we'll be able to find the project budget to implement some sort of authorization hooks. I really like the idea of being able to implement authorization in a pluggable manner. My current approach is entirely to invasive and specific to the needs of our current project.

@vdboor
Copy link
Contributor

vdboor commented May 25, 2013

You're welcome! Glad to know I can help. If you're able to work on this issue before I manage to address it, I'd be happy to look into that and merge it. Thanks so far for your assistance and help on improving this project.

I've been looking into your fork too, this is a good inspiration for designing a good permission-based API :)

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