-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changes for compatibility with Wagtail>=3.0.3
- Refactored `helpers.thirdparty.wagtail` module - `mentions_wagtail_path`, `mentions_wagtail_re_path` parameter `model_class` can now accept a model name if the class is not available. e.g. model_class=MyModel, model_class="myapp.MyModel", model_class="MyModel" should all work. App name should be included where possible.
- Loading branch information
Showing
17 changed files
with
449 additions
and
276 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
build/ | ||
dist/ | ||
env/ | ||
env-*/ | ||
*.egg-info/ | ||
__pycache__/ | ||
.coverage | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from .resolution import get_model_for_url_by_wagtail | ||
from .urls import ( | ||
mentions_wagtail_path, | ||
mentions_wagtail_re_path, | ||
mentions_wagtail_route, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
"""Modules should import wagtail components from here. | ||
The library should work with (and without)wWagtail==3.0.3 and above. The @path | ||
decorator was only introduced in wagtail==4.0.0 so we need to handle it being | ||
unavailable.""" | ||
|
||
from mentions.exceptions import OptionalDependency | ||
|
||
try: | ||
from wagtail.contrib.routable_page.models import RoutablePageMixin | ||
from wagtail.contrib.routable_page.models import path as wagtail_path | ||
from wagtail.contrib.routable_page.models import re_path as wagtail_re_path | ||
|
||
except ImportError: | ||
|
||
def config_error(function_name: str, min_wagtail_version: str, django_func): | ||
"""When wagtail is not available or""" | ||
|
||
def fake_decorator(pattern, name): | ||
def raise_error(*args, **kwargs): | ||
raise OptionalDependency( | ||
f"Tried to use decorator '{function_name}' but " | ||
f"wagtail>={min_wagtail_version} is not installed. (" | ||
f"pattern={pattern}, name={name})" | ||
) | ||
|
||
def fake_wrapper(view_func): | ||
if not hasattr(view_func, "_routablepage_routes"): | ||
view_func._routablepage_routes = [] | ||
|
||
view_func._routablepage_routes.append( | ||
( | ||
django_func( | ||
pattern, | ||
raise_error, | ||
name=name or view_func.__name__, | ||
), | ||
1000, | ||
) | ||
) | ||
return view_func | ||
|
||
return fake_wrapper | ||
|
||
return fake_decorator | ||
|
||
try: | ||
from django.urls import re_path | ||
from wagtail.contrib.routable_page.models import RoutablePageMixin | ||
from wagtail.contrib.routable_page.models import route as wagtail_re_path | ||
|
||
except ImportError: | ||
wagtail_re_path = config_error("mentions_wagtail_re_path", "3.0.3", re_path) | ||
|
||
class RoutablePageMixin: | ||
pass | ||
|
||
from django.urls import path | ||
|
||
wagtail_path = config_error("mentions_wagtail_path", "4.0", path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from typing import Callable, Optional, Type | ||
|
||
from django.apps import apps | ||
from django.urls import ResolverMatch | ||
|
||
from mentions import config, contract, options | ||
from mentions.exceptions import BadUrlConfig, NoModelForUrlPath, OptionalDependency | ||
from mentions.helpers.resolution import get_model_for_url_by_helper | ||
from mentions.helpers.thirdparty.wagtail.proxy import RoutablePageMixin | ||
from mentions.helpers.thirdparty.wagtail.util import get_annotation_from_viewfunc | ||
from mentions.helpers.types import MentionableImpl, ModelClass | ||
from mentions.models.mixins import MentionableMixin | ||
|
||
__all__ = [ | ||
"get_model_for_url_by_wagtail", | ||
"autopage_page_resolver", | ||
] | ||
|
||
|
||
def get_model_for_url_by_wagtail(match: ResolverMatch) -> MentionableMixin: | ||
"""Try to resolve a Wagtail Page instance, if Wagtail is installed. | ||
If using RoutablePageMixin you must replace the Wagtail @path/@re_path | ||
decorators with @mentions_wagtail_path/@mentions_wagtail_re_path to add | ||
the metadata required to resolve the correct target Page. | ||
""" | ||
|
||
if not config.is_wagtail_installed(): | ||
raise OptionalDependency("wagtail") | ||
|
||
import wagtail.views | ||
|
||
if match.func != wagtail.views.serve: | ||
raise OptionalDependency("wagtail") | ||
|
||
from wagtail.models.sites import get_site_for_hostname | ||
|
||
site = get_site_for_hostname(options.domain_name(), None) | ||
path = match.args[0] | ||
path_components = [component for component in path.split("/") if component] | ||
|
||
page, args, kwargs = site.root_page.localized.specific.route(None, path_components) | ||
|
||
if isinstance(page, MentionableMixin): | ||
return page | ||
|
||
if not isinstance(page, RoutablePageMixin): | ||
raise NoModelForUrlPath() | ||
|
||
view_func, view_args, view_kwargs = args | ||
|
||
kwarg_mapping = get_annotation_from_viewfunc(view_func) | ||
view_kwargs.update(kwarg_mapping) | ||
|
||
model_name = view_kwargs.get(contract.URLPATTERNS_MODEL_NAME) | ||
|
||
try: | ||
model_class: Type[MentionableMixin] = resolve_model(model_name, page) | ||
|
||
except LookupError: | ||
raise BadUrlConfig(f"Cannot find model `{model_name}`!") | ||
|
||
return get_model_for_url_by_helper(model_class, view_args, view_kwargs) | ||
|
||
|
||
def autopage_page_resolver( | ||
model_class: ModelClass, | ||
lookup: dict, | ||
view_func: Callable, | ||
) -> Callable: | ||
def wrapped_view_func(self, request, *args, **kwargs): | ||
resolved_model = resolve_model(model_class, view_func) | ||
lookup_kwargs = {**lookup, **kwargs} | ||
page = get_model_for_url_by_helper(resolved_model, args, lookup_kwargs) | ||
|
||
return view_func(self, request, page) | ||
|
||
return wrapped_view_func | ||
|
||
|
||
def resolve_app_name(module_name: str) -> Optional[str]: | ||
app_configs = apps.get_app_configs() | ||
for conf in app_configs: | ||
if module_name.startswith(conf.name): | ||
return conf.label | ||
|
||
raise LookupError(f"Cannot find app for module {module_name}") | ||
|
||
|
||
def resolve_model(model_class: ModelClass, context: object) -> Type[MentionableImpl]: | ||
"""Resolve a model type from an identifier. | ||
model_class may be: | ||
- a class already. | ||
- a dotted string name for a class. | ||
- a simple class name. In this case, use context.__module__ to try and | ||
construct a valid dotted name for the class. | ||
""" | ||
if not isinstance(model_class, str): | ||
return model_class | ||
|
||
if "." in model_class: | ||
return apps.get_model(model_class) | ||
|
||
app_name = resolve_app_name(context.__module__) | ||
return apps.get_model(f"{app_name}.{model_class}") |
Oops, something went wrong.