Skip to content

Commit

Permalink
feat: LinkDict (syntactic sugar)
Browse files Browse the repository at this point in the history
  • Loading branch information
fsbraun committed Nov 7, 2024
1 parent 7b636a9 commit 5365009
Show file tree
Hide file tree
Showing 18 changed files with 499 additions and 239 deletions.
20 changes: 13 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,12 @@ models or forms.
class MyForm(forms.Form):
link = LinkFormField(required=False)
``LinkField`` is a subclass of ``JSONField`` and stores the link data as dict.
``LinkField`` is a subclass of ``JSONField`` and stores the link data as
``djangocms_link.helpers.LinkDict``, a direct subclass of ``dict``.
(An empty link will be ``{}``.)

To render the link field in a template, use the new template tag ``to_url``::
To render the link field in a template, use the ``LinkDict`` property ``url`` or
the new template tag ``to_url``::

{% load djangocms_link_tags %}
{# Variant 1 #}
Expand All @@ -207,18 +209,22 @@ To render the link field in a template, use the new template tag ``to_url``::
{% endif %}

{# Variant 2 #}
{% if obj.link %}
<a href="{{ obj.link.url }}">Link available</a>
{% endif %}

{# Variant 3 #}
{% with url=obj.link|to_url %}
{% if url %}
<a href="{{ url }}">Link available</a>
{% endif %}

To turn the ``LinkField``'s dictionary into a URL in python code, use the
``djangocms_link.helpers.get_link`` helper function::

from djangocms_link.helpers import get_link
To turn the ``LinkField``'s ``LinkDict`` dictionary into a URL in python code,
use the ``url`` property. (It will hit the database when needed. Results are
cached.)::

obj = MyModel.objects.first()
url = get_link(obj.link)
url = obj.link.url


Running Tests
Expand Down
2 changes: 1 addition & 1 deletion djangocms_link/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '5.0.0a1'
__version__ = "5.0.0a1"
63 changes: 48 additions & 15 deletions djangocms_link/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from cms.utils import get_language_from_request

from . import models
from .fields import LinkFormField, LinkWidget
from .helpers import get_manager


Expand All @@ -25,6 +26,7 @@
class GrouperModelAdmin:
pass


REGISTERED_ADMIN = [] # Will be set by djangocms_link.apps.DjangoCmsLinkConfig.ready


Expand Down Expand Up @@ -117,20 +119,42 @@ def get_queryset(self):
"""Return queryset based on ModelAdmin.get_search_results()."""
try:
# django CMS 4.2+
qs = PageContent.admin_manager.filter(language=self.language).filter(
Q(title__icontains=self.term) | Q(menu_title__icontains=self.term)
).current_content()
qs = (Page.objects.filter(pk__in=qs.values_list("page_id", flat=True)).order_by("path")
.annotate(__link_text__=Subquery(qs.filter(page_id=OuterRef("pk")).values("title")[:1])))
qs = (
PageContent.admin_manager.filter(language=self.language)
.filter(
Q(title__icontains=self.term) | Q(menu_title__icontains=self.term)
)
.current_content()
)
qs = (
Page.objects.filter(pk__in=qs.values_list("page_id", flat=True))
.order_by("path")
.annotate(
__link_text__=Subquery(
qs.filter(page_id=OuterRef("pk")).values("title")[:1]
)
)
)
if self.site:
qs = qs.filter(site_id=self.site)

Check warning on line 139 in djangocms_link/admin.py

View check run for this annotation

Codecov / codecov/patch

djangocms_link/admin.py#L139

Added line #L139 was not covered by tests
except (AttributeError, FieldError):
# django CMS 3.11 - 4.1
qs = get_manager(PageContent, current_content=True).filter(language=self.language).filter(
Q(title__icontains=self.term) | Q(menu_title__icontains=self.term)
qs = (
get_manager(PageContent, current_content=True)
.filter(language=self.language)
.filter(
Q(title__icontains=self.term) | Q(menu_title__icontains=self.term)
)
)
qs = (
Page.objects.filter(pk__in=qs.values_list("page_id", flat=True))
.order_by("node__path")
.annotate(
__link_text__=Subquery(
qs.filter(page_id=OuterRef("pk")).values("title")[:1]
)
)
)
qs = (Page.objects.filter(pk__in=qs.values_list("page_id", flat=True)).order_by("node__path")
.annotate(__link_text__=Subquery(qs.filter(page_id=OuterRef("pk")).values("title")[:1])))
if self.site:
qs = qs.filter(node__site_id=self.site)
return list(qs)
Expand All @@ -148,7 +172,9 @@ def add_admin_querysets(self, qs):
else:
new_qs = model_admin.get_queryset(self.request)
if hasattr(model_admin.model, "site") and self.site:
new_qs = new_qs.filter(Q(site_id=self.site) | Q(site__isnull=True))
new_qs = new_qs.filter(
Q(site_id=self.site) | Q(site__isnull=True)
)
elif hasattr(model_admin.model, "sites") and self.site:
new_qs = new_qs.filter(sites__id=self.site)

Check warning on line 179 in djangocms_link/admin.py

View check run for this annotation

Codecov / codecov/patch

djangocms_link/admin.py#L179

Added line #L179 was not covered by tests
new_qs, search_use_distinct = model_admin.get_search_results(
Expand All @@ -169,7 +195,9 @@ def process_request(self, request):
Validate request integrity, extract and return request parameters.
"""
term = request.GET.get("term", request.GET.get("q", "")).strip("  ").lower()
site = request.GET.get("app_label", "") # Django admin's app_label is abused as site id
site = request.GET.get(
"app_label", ""
) # Django admin's app_label is abused as site id
try:
site = int(site)
except ValueError:
Expand All @@ -191,6 +219,10 @@ class LinkAdmin(admin.ModelAdmin):
"""The LinkAdmin class provides the endpoint for getting the urls. It is not visible in the
admin interface."""

global_link_form_widget = LinkWidget
global_link_form_field = LinkFormField
global_link_url_name = None

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.global_link_url_name = f"{self.opts.app_label}_{self.opts.model_name}_urls"
Expand All @@ -202,10 +234,11 @@ def has_module_permission(self, request): # pragma: no cover
def get_urls(self):
# Only url endpoint public, do not call super().get_urls()
return [
path("urls",
self.admin_site.admin_view(self.url_view),
name=self.global_link_url_name
),
path(
"urls",
self.admin_site.admin_view(self.url_view),
name=self.global_link_url_name,
),
]

def url_view(self, request):
Expand Down
10 changes: 7 additions & 3 deletions djangocms_link/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def ready(self):
continue
# search_fields need to be defined in the ModelAdmin class, and the model needs to have
# a get_absolute_url method.
if getattr(_admin, "search_fields", []) and hasattr(_admin.model, "get_absolute_url"):
if getattr(_admin, "search_fields", []) and hasattr(
_admin.model, "get_absolute_url"
):
link_admin.REGISTERED_ADMIN.append(_admin)
else:
# turn model config into model admin instances
Expand All @@ -35,12 +37,14 @@ def ready(self):
if isinstance(model, str):
model = apps.get_model(model)
if not hasattr(model, "get_absolute_url"): # pragma: no cover
raise ImproperlyConfigured(f"{model.__name__} needs to implement get_absolute_url method")
raise ImproperlyConfigured(
f"{model.__name__} needs to implement get_absolute_url method"
)
admin = admin.site._registry[model]
if admin not in admins:
admins.append(admin)
elif not isinstance(model, ModelAdmin): # pragma: no cover
raise ImproperlyConfigured(
"DJANGOCMS_LINK_LINKABLE_MODELS must be a list of string \"app_label.model_name\""
'DJANGOCMS_LINK_LINKABLE_MODELS must be a list of string "app_label.model_name"'
)
link_admin.REGISTERED_ADMIN = admins
42 changes: 25 additions & 17 deletions djangocms_link/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class LinkPlugin(CMSPluginBase):
model = Link
name = _('Link')
name = _("Link")
text_enabled = True
text_icon = (
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-link-45deg" '
Expand All @@ -22,27 +22,35 @@ class LinkPlugin(CMSPluginBase):
allow_children = True

fieldsets = [
(None, {
'fields': (
'name',
'link',
'target',
)
}),
(_('Advanced settings'), {
'classes': ('collapse',),
'fields': (
'template',
'attributes',
)
}),
(
None,
{
"fields": (
"name",
"link",
"target",
)
},
),
(
_("Advanced settings"),
{
"classes": ("collapse",),
"fields": (
"template",
"attributes",
),
},
),
]

def get_render_template(self, context, instance, placeholder):
return f'djangocms_link/{instance.template}/link.html'
return f"djangocms_link/{instance.template}/link.html"

def render(self, context, instance, placeholder):
context['link'] = get_link(instance.link, getattr(get_current_site(context["request"]), "id", None))
context["link"] = get_link(
instance.link, getattr(get_current_site(context["request"]), "id", None)
)
return super().render(context, instance, placeholder)


Expand Down
Loading

0 comments on commit 5365009

Please sign in to comment.