I'm trying to build several websites with DjangoCMS with some shared pages. Is it possible to create a page which is shared across all django Sites?
With a basic DjangoCMS configuration, when a page is published on a Site it does not appear on other Site. I am wondering if this is configurable in any way.
When looking at the code I've seen that the TreeNode is linked to a specific Site (https://github.com/divio/django-cms/blob/develop/cms/models/pagemodel.py#L52), so I guess that if it's possible it won't be that simple.
class TreeNode(MP_Node):
# [...]
site = models.ForeignKey(
Site,
on_delete=models.CASCADE,
verbose_name=_("site"),
related_name='djangocms_nodes',
db_index=True,
)
# [...]
I'd be fine with an external module if DjangoCMS does not handle this, or even some ideas or lead of how to approach this, I really don't have a clue.
Thanks a lot!
I've solved this issue with some ugly patches in the DjangoCMS code itself.
from cms import cms_menus
from cms.templatetags import cms_tags
from cms.utils import page
# Ugly-patching DjangoCMS so that a page from another Django Site can be displayed
def new_get_page_from_path(site, path, preview=False, draft=False):
"""
Resolves a url path to a single page object.
Returns None if page does not exist
"""
from cms.models import Title
titles = Title.objects.select_related('page__node')
published_only = not draft and not preview
if draft:
titles = titles.filter(publisher_is_draft=True)
elif preview:
titles = titles.filter(publisher_is_draft=False)
else:
titles = titles.filter(published=True, publisher_is_draft=False)
titles = titles.filter(path=(path or ''))
titles = list(titles.iterator())
for title in titles:
if title.page.node.site_id != site.pk:
continue
if published_only and not page._page_is_published(title.page):
continue
title.page.title_cache = {title.language: title}
return title.page
# This is the different part from the DjangoCMS code:
# re do the same loop, but this time ignore the Site filtering
for title in titles:
if published_only and not page._page_is_published(title.page):
continue
title.page.title_cache = {title.language: title}
return title.page
return
# Ugly-patching DjangoCMS so that a page from another Django Site can fetched
# using {% pageurl %} (for example)
def new_get_page_queryset(site, draft=True, published=False):
from cms.models import Page
if draft:
pages = Page.objects.drafts().on_site(site)
if pages:
return pages
if published:
pages = Page.objects.public().published(site)
if pages:
return pages
pages = Page.objects.public().on_site(site)
if pages:
return pages
# This is the different part from the DjangoCMS code:
# re do the same logic, but this time ignore the Site filtering
if draft:
return Page.objects.drafts()
if published:
return Page.objects.public().published()
return Page.objects.public()
page.get_page_from_path = new_get_page_from_path
page.get_page_queryset = new_get_page_queryset
cms_tags.get_page_queryset = new_get_page_queryset
cms_menus.get_page_queryset = new_get_page_queryset
Then I'm importing this file before the urlpatterns variable in the urls.py file (warned you it was ugly).
What DjangoCMS does is that it tries to find the Page with the Site given in the request. If the Page is not found, DjangoCMS would raise a 404 error, but in our case we re-do the same query but this time without the Site filter.
This way a Page created on one Site is accessible on each sub-Site.
I then needed some Page accessible on every Site with most of the content identical, but SOME different content. I've solved this issue by using the static_placeholder tag which can be specified per sub-Site. http://docs.django-cms.org/en/latest/reference/templatetags.html#static-placeholder
Related
I was hoping someone could help me with a pagination question.
I am trying to use Django pagination following the information on this page (https://docs.djangoproject.com/en/2.2/topics/pagination/). Whilst I have successfully displayed the correct number of items on the first page and the last page works, the next and previous pages keep taking me to the first page.
I think the issue may revolve around the ‘request’ element and I’m not sure if I am picking up an incorrect version. The example states:-
def listing(request):
contact_list = Contacts.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page
page = request.GET.get('page')
contacts = paginator.get_page(page)
return render(request, 'list.html', {'contacts': contacts})
The command:
page = request.GET.get(‘page’)
returns “AttributeError: 'Request' object has no attribute 'GET'”
By replacing this code with:
page = request.args.get('page', type=int)
the code successfully renders the first (and last) page but next and previous do not work.
As background I built my system on the Flask megatutorial but I have been unable to use that pagination, I understand because I haven’t used the Flask SQL Alchemy for creating and updating databases. My routes file has
from flask import request
Should I replace this with another utility's “request” and if so, which?
It seems the problem was in a missing () within the HTML file:-
next
listed within the example should have been:
next
I'm attempting to set a page as the root home page of my wagtail based site.
I know you can set the homepage using the sites setting. But, my use case is different.
I'm using a "redirect" page which redirects to an index page as the homepage. The index is restricted to only allow ertain page types (since it's an index....). But, this causes issues with other pages not being in the tree if it was set at the root.
Hence, the redirect. But, upon redirecting the serve view of the redirect page to use this page it picks up the slug.
I still want it to appear at the root url. Meaning, /. How can I achieve this?
High level description:
from django.shortcuts import redirect
class IndexPage(Page):
# children restricted to IndexSubpage
class IndexSubpage(Page):
# parent restricted to IndexPage
class RedirectPage(Page):
def serve(request):
# Link is a link to a page through a ForeignKey....
return redirect(self.link, permanent=True)
So, this will pick up the slug of IndexPage instead of being at /
I am working on a Wagtail project consisting of a few semi-static pages (homepage, about, etc.) and a blog. In the homepage, I wanted to list the latest blog entries, which I could do adding the following code to the HomePage model:
def blog_posts(self):
# Get list of live blog pages that are descendants of this page
posts = BlogPost.objects.live().order_by('-date_published')[:4]
return posts
def get_context(self, request):
context = super(HomePage, self).get_context(request)
context['posts'] = self.blog_posts()
return context
However, I would also like to add the last 3 entries in the footer, which is a common element of all the pages in the site. I'm not sure of what is the best way to do this — surely I could add similar code to all the models, but maybe there's a way to extend the Page class as a whole or somehow add "global" context? What is the best approach to do this?
This sounds like a good case for a custom template tag.
A good place for this would be in blog/templatetags/blog_tags.py:
import datetime
from django import template
from blog.models import BlogPost
register = template.Library()
#register.inclusion_tag('blog/includes/blog_posts.html', takes_context=True)
def latest_blog_posts(context):
""" Get list of live blog pages that are descendants of this page """
page = context['page']
posts = BlogPost.objects.descendant_of(page).live().public().order_by('-date_published')[:4]
return {'posts': posts}
You will need to add a partial template for this, at blog/templates/blog/includes/blog_posts.html. And then in each page template that must include this, include at the top:
{% load blog_tags %}
and in the desired location:
{% latest_blog_posts %}
I note that your code comment indicates you want descendants of the given page, but your code doesn't do that. I have included this in my example. Also, I have used an inclusion tag, so that you do not have to repeat the HTML for the blog listing on each page template that uses this custom template tag.
I have a bunch of models. All these models has a method get_absolute_url and a field text. I want to make internal links in the text field just like wikipedia does.
Wikipedia's internal links in pages only refer to other pages. I need to link to all my models.
I could make a pattern for internal links and replacing this pattern with a hardcoded url to an url but it's really not a good idea because the links can change. So it would be best if I could refer to get_absolute_url.
Another option would be to use a template tag to change a specific pattern to links.
How should it be done? Are there any open source projects in which this has already been done?
I wanted to answer this same problem just a few days ago, and I did it with a template filter. My links are relative URLs, not absolute, but you could tweak that pretty easily, and you could also tweak the regex pattern to match whatever link markup you prefer.
Using the filter, the link is only looked up at display time, so if your view's URL has changed, that should automatically update with the reverse() lookup.
I also use Markdown to process my description fields, so I make the link return a markdown-formatted link instead of HTML, but you could tweak that too. If you use Markdown, you'd want to put this filter first.
So to display a description TextField with internal links, in the template would be something like this:
{{ entity.description|internal_links|markdown }}
(See the Django docs on writing your own custom filters for more details on writing and registering filters.)
As for the specific filter itself, I did it like this:
from django import template
from django.core.urlresolvers import reverse
from my.views import *
register = template.Library()
#register.filter
def internal_links(value):
"""
Takes a markdown textfield, and filters
for internal links in the format:
{{film:alien-1979}}
...where "film" is the designation for a link type (model),
and "alien-1979" is the slug for a given object
NOTE: Process BEFORE markdown, as it will resolve
to a markdown-formatted linked name:
[Alien](http://opticalpodcast.com/cinedex/film/alien-1979/)
:param value:
:return:
"""
try:
import re
pattern = '{{\S+:\S+}}'
p = re.compile(pattern)
#replace the captured pattern(s) with the markdown link
return p.sub(localurl, value)
except:
# If the link lookup fails, just display the original text
return value
def localurl(match):
string = match.group()
# Strip off the {{ and }}
string = string[2:-2]
# Separate the link type and the slug
link_type, link_slug = string.split(":")
link_view = ''
# figure out what view we need to display
# for the link type
if(link_type == 'film'):
link_view = 'film_detail'
elif(link_type == 'person'):
link_view = 'person_detail'
else:
raise Exception("Unknown link type.")
link_url = reverse(link_view, args=(link_slug,))
entity = get_object_or_404(Entity, slug=link_slug)
markdown_link = "[" + entity.name + "](" + link_url + ")"
return markdown_link
Im trying to render a cms page, within another page using a custom cms plugin.
The this is my plugin class:
class PageInDiv(CMSPlugin):
page_div = models.ForeignKey(Page, verbose_name= "page")
def __unicode__(self):
return self.page_div.get_title()
as you can see all it does is link the plugin to a page then on my cms_plugins.py i have
class PageInDivPlugin(CMSPluginBase):
model = PageInDiv
name = _("Page in div")
render_template = "page.html"
admin_preview = False
def render(self, context, instance, placeholder):
temp = loader.get_template(instance.page_div.get_template())
html = temp.render(context)
context.update({
'html': html,
'title':instance.page_div.get_title(),
'placeholder':placeholder,
})
return context
as you can see i pass the html for the provided page to the plugin template, then the plugin template is rendered within the page thats hosting the plugin.
The problem i am having is that the placeholder content from the page thats selected via foreignkey is not being rendered ( displayed ).
So my question is, is there a way to render a pages placeholders programatically ?
Just for a moment ignoring the idea of creating a custom plugin in order to do what you describe (ie, render a page's placeholders programatically), the following might be a viable alternative, depending on what exactly you are trying to achieve...
You should be able, just in the template for your "outer" cms page (ie, the page within which you want to display the contents of another cms page), to get access to the current page like this:
{{ request.current_page }}
This is by virtue of the cms page middleware. So taking that a step further, you should be able to access the page's placeholders like this:
{% for placeholder in request.current_page.placeholders %}
{{ placeholder.render }}
{% endfor %}
That's one way you could go about rendering a page's placeholders "inside" another page.
I needed to render another page from within the template which could be accomplished with:
#register.simple_tag(takes_context=True)
def render_page(context, page, default="base.html"):
if not page:
return loader.get_template(default).render(context)
new_context = copy(context)
new_context['request'] = copy(context['request'])
new_context['request'].current_page = page
new_context['current_page'] = page
new_context['has_change_permissions'] = page.has_change_permission(context['request'])
new_context['has_view_permissions'] = page.has_view_permission(context['request'])
if not new_context['has_view_permissions']:
return loader.get_template(default).render(context)
return loader.get_template(page.get_template()).render(new_context)