Skip to content

Is there an obvious way to override PageManager? #687

@paramono

Description

@paramono

I would like to override a few methods in PageManager - what would be the most efficient way to achieve that?


Here's some context to explain why I want to do that.

I wanted to use Django's Sites framework, and configure FeinCMS in a way that would allow me to control on which sites any particular page can be shown.

Yes, FeinCMS already has an extension for that:
https://github.com/feincms/feincms/blob/e923d0466b74937d36eb45e2c46ea18cfef83d56/feincms/module/page/extensions/sites.py

However:

  1. It creates 'site' ForeignKey (I want to have 'sites' ManyToMany instead, but I easily fixed that by writing a custom extension)
  2. It relies on Site.objects.get_current()

Site.objects.get_current() works only if you have SITE_ID defined in your settings, otherwise it raises an error and asks to provide request in order to deduce the current Site. I omitted SITE_ID from my settings on purpose because I want to achieve behavior documented in Django docs:
https://docs.djangoproject.com/en/dev/ref/contrib/sites/

If the setting is omitted, the get_current_site() function will try to get the current site by comparing the domain with the host name from the request.get_host() method.

There are a few problems, though.
I noticed that feincms sites extension modifies which pages are treated as active by using the following approach:

def current_site(queryset):
    return queryset.filter(site=Site.objects.get_current())

PageManager.add_to_active_filters(current_site, key="current_site")

However, without SITE_ID specified in settings, the only way to filter by current site is to somehow pass request to the filtering logic, and from my understanding, with add_to_active_filter there is no way to do so.

Which brought me to conclusion that I need to research how Handler view works, because views have access to request.

I've tried many different approaches, but the only one that worked was to copy FeinCMS Handler code and then manually redefine PageManager methods before Handler class is defined.

return self.page_model._default_manager.for_request(

from feincms.module.page.models import PageManager
from .managers import for_request, page_for_path, best_match_for_path

PageManager.for_request = for_request
PageManager.page_for_path = page_for_path
PageManager.best_match_for_path = best_match_for_path

logger = logging.getLogger(__name__)

class Handler(ContentView):
    # ...

custom for_request simply passes request to custom page_for_path and best_match_for_path

page_for_path and best_match_for_path are overridden in order to accept request as an argument and to additionally filter pages with .filter(sites__in=[request.site])

Although it's very cumbersome, it actually works.

I tried other, seemingly more elegant approaches, but they didn't work:

  1. If I create a custom manager and try to redefine objects in Page, then AttributeError: 'NoneType' object has no attribute '_mptt_meta' is thrown in django-mptt. It's probably because of all the MetaClass magic. I assume there must be some way to fix that?
  2. I can override FEINCMS_DEFAULT_PAGE_MODEL, but it creates many unwanted side-effects (i.e. I will have to manually recreate Page admin to import custom Page model, because current admin.py in FeinCMS does not use FENCMS_DEFAULT_PAGE_MODEL). Even when I did that, I had to rename db_table in the custom Page.Meta class, after which django was still complaining about two different models named Page. I ended up ditching this approach because too many things started falling apart.
  3. If I use add_to_class('objects', PageSitesManager()) in my custom extension, where PageSitesManager is the custom Manager will all the important methods overridden, it simply does nothing.

Is there a more elegant/obvious way to override PageManager without causing errors in django-mptt?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions