My urls no longer point to my pages Wagtail - wagtail

On a wagtail site I have lost links to my blog pages (possibly as a result of moving to a newer version)
My blog listing page is still accessible with the url "http://localhost:8080/blog/"
I have the following models
class BlogListingPage(Page):
template = "home/blog_listing_page.html"
subpage_types = ['home.BlogDetailPage']
blog_listing_title = models.CharField(
max_length=100,
blank=False,
null=False,
help_text="Heading for the blog listing page",
)
content_panels = Page.content_panels + [
FieldPanel("blog_listing_title"),
]
class BlogDetailPage(Page):
"""Blog detail page."""
template = "home/blog_detail_page.html"
parent_page_types = ['home.BlogListingPage']
blog_title = models.CharField(
max_length=100,
blank=False,
null=True,
help_text="Blog title (100 chars max)",
)
content_panels = Page.content_panels + [
FieldPanel("blog_title"),
]
I can list the blog pages in my blog listing page:
{{ blog_listing.get_children.specific}}
{% for blog in blog_listing.get_children.live %}
<h5>{{ blog.specific.blog_title}}</h5>
<h5>{{ blog.slug }}</h5>
{% endfor %}
The slug displays correctly (e.g. /blog/xxx/) but the pageurl blog is None and if I click on it I get a 404 error (Request URL: http://localhost:8080/blog/xxx/)
Finally, even if I put http://localhost:8080/blog/xxx/ in the browser I get a 404 error
The line
{{ blog_listing.get_children.specific}}
displays the page queryset with the list of wagtail.core.models.Pages as I would expect
If I enter the following code into views
views.py
pqs = BlogListingPage.objects.all()
children = pqs.live()[0].get_children()
for child in children:
print('s', child.slug, '|', child.url_path)
I get the output
s xxx | /blog/xxx/
Which is what I expect.
Why are the pages/links no longer in sync?
I cannot post an MRE because, ironically, when I try to construct one, it works as expected. I cannot see what the difference is with my live site, but if someone could point to where I might look it would help.

Related

wagtail 4.0/ How return urls (templates) for preview in admin board from #path

class ProductIndexPage(RoutablePageMixin, Page):
subpage_types = ['ProductPage']
parent_page_types = ['home.HomePage']
def live_categories(self):
all_products_live_id = ProductPage.objects.live().values_list('categories_id', flat=True)
list_live_id_uniqe = list(set(all_products_live_id))
live_cat = ProductCategory.objects.filter(id__in=list_live_id_uniqe).order_by('-id')
return live_cat
#path('')
#path('all-categories/')
def all_category_page(self, request):
productpages = self.get_children().live().order_by('-first_published_at')
return self.render(request, context_overrides={
'title': self.title,
'productpages': productpages,
'live_categories': self.live_categories,
})
#path('<str:cat_name>/', name='cat_url')
def current_category_page(self, request, cat_name=None):
productpages = ProductPage.objects.live().filter(categories__slug__iexact=cat_name).order_by \
('-first_published_at')
current_cat = self.live_categories().get(slug=cat_name).name
return self.render(request, context_overrides={
'title': "%s" % current_cat,
'productpages': productpages,
'live_categories': self.live_categories,
})
#path('<str:cat_name>/<str:prod_name>/')
def product_page(self, request, cat_name=None, prod_name=None):
product = ProductPage.objects.get(categories__slug__iexact=cat_name, slug=prod_name)
return self.render(request, context_overrides={
'product': product},
template="products/product_page.html",)
I can't edit page in wagtail admin menu from path:
#path('<str:cat_name>/<str:prod_name>/')
My page tree in admin:
root/products/<prod_name>
How to change route in Admin for editing pages from wagtal button or for preview in Admin?
I am newbie, plz show me example.
sorry for English)
With RoutablePageMixin there is only one page - and it displays differently depending on the parameter sent on the url. Unfortunately, this means you can't preview the category version of your page, current_category_page.
But it looks like the product page you are routing too as #path('str:cat_name/str:prod_name/') should be displaying the same page as you serve from the ProductPage's own url. So the ProductPage you see in the preview ProductIndexPage + 'str:cat_name/str:prod_name/'

React unable to read property of undefined

Can't figure out where my mistake is. Not able to map through to display the list of blog comments. I'm using django and react. From the code below, I tried to assess each blog post with comments. But I'm not able to get the comment property from the blog. If I do something like {blog.title} I get the title of the blog back on the browser. Since comments are associated with each post I try to get different properties of comment from the blog object (just as I specified in the code below) but the Value I'm getting is undefined. and have the following blog post and blog comment models.
class BlogComment(models.Model):
post = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, related_name="post_comment", null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="user_comment", null=True)
name = models.CharField(max_length=200, null=True, blank=True)
comment = models.TextField(null=True, blank=True)
dateCreated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user.username)
class BlogPost(models.Model):
...
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
body = models.TextField()
dateCreated = models.DateTimeField(auto_now_add=True)
And the serializers for both models are:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = BlogComment
fields = '__all__'
class BlogPostSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField(read_only=True)
class Meta:
model = BlogPost
fields = "__all__"
def get_comments(self, obj):
comments = obj.comment_set.all()
serializer = CommentSerializer(comments, many=True)
return serializer.data
<h2>{blog.title}</h2>
<img src={blog.image} />
<div variant='flush'>
{blog.comments.map((comment) => (
<div key={comment.id}>
<strong>{comment.name}</strong>
<p>{comment.dateCreated}</p>
<p>{comment.comment}</p>
</div>
))}
</div>
The comment API is functioning properly. In react, I'm able to add comments to each post using a form and the comments will appear in the database. But when I try to map through to display the comments of each blog post I get that error. How do I fix this?
You can check the length of an array before the map. Or you can use optional chaining.
like this obj?.property
<h2>{blog?.title}</h2>
<img src={blog?.image} />
<div variant='flush'>
{blog?.post?.comments.map((comment) => (
<div key={comment?.id}>
<strong>{comment?.name}</strong>
<p>{comment?.dateCreated}</p>
<p>{comment?.comment}</p>
</div>
))}
</div>

How to display certain Wagtail page on the homepage?

I'm making a website where there should be a links to specific pages on the homepage. I'm using PageChooserPanel to do this, but cannot display certain page.
This is my code:
models.py
class HomePage(Page):
FeaturedPageTitle = models.CharField(
null=True,
blank=True,
max_length=255,
)
FeaturedPage = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
)
content_panels = Page.content_panels + [
MultiFieldPanel([
MultiFieldPanel([
FieldPanel('FeaturedPageTitle'),
PageChooserPanel('FeaturedPage'),
]),
],
]
def pages(self):
pages = StandardPage.objects.all()
return pages
class StandardPage(Page)
home_page.html
{% if page.featured_page %}
{{ page.featured_page_title }}
{% for page in page.pages %}
{% image page.image %}
{{page.title}}
{% endfor %}
{% endif %}
You have called your featured page fields FeaturedPageTitle / FeaturedPage, but used featured_page_title / featured_page on the template.
In Python, capitalisation / punctuation in variable names is significant - the normal convention is to use capitals (CamelCase) only for class names, and underscore_case for other variables.

How to create an invisible dummy page in Wagtail?

How can I create an invisible dummy page in Wagtail?
I need a "virtual" page object within Wagtail to build a menu for non Wagtail based pages and also external resources. (See my entry post here)
class MenuDummyPage(Page):
menu_description = models.CharField(max_length=255, blank=True)
menu_icon = models.CharField(max_length=255, blank=True)
menu_link = models.CharField(max_length=255, blank=True)
settings_panels = [
FieldPanel('menu_description'),
FieldPanel('menu_icon'),
FieldPanel('menu_link'),
]
def get_sitemap_urls(self):
return []
def serve(self, request):
pass
If I create the above page object then it is not listed within the generated wagtail sitemap.
But if I navigate on my own to that page manually the object is called. How can I stop this?
Example:
If I create a MenuDummyPage with the title "This is a test" then the system will automatically generate a slug => "this-is-a-test".
If I call "/this-is-a-test"/ in my browser wagtail is answering because the slug exists. How can I remove this behavior for my "MenuDummyPage" objects?
If what you mean by a dummy page is a page under which other pages are kept in the page tree, then you could do the following:
from django.http import HttpResponseRedirect
class Node(Page):
subpage_types = [your subpage types]
parent_page_types = [your parent page types]
link = models.CharField(max_length=255, default='', blank='True')
content_panels = Page.content_panels + [
FieldPanel('link')
]
def serve(self, request):
if self.link is not None:
return HttpResponseRedirect(self.link)
# To handle the situation where someone inadvertantly lands on a parent page, do a redirect
first_descendant = self.get_descendants().first()
if first_descendant:
return HttpResponseRedirect(first_descendant.url)
else:
return HttpResponseRedirect(request.site.root_page.url)
The optional link field allows you to define a link if you wish for this position in the page tree structure. The above, again, assumes that you are using this Page-based item as a placeholder in the Page tree so that you can have other Pages under it. As long as you don't render the url of this page in your template, then users should never know how to get to the url for the Node, but if someone does get to the url for a Node-type page, then the first_descendant logic handles this situation by sending them to either the first descendant of the Node or to the home page if no descendants of Node exist.
In your template (note the use of specific_class):
{% for item in menu_items %}
<li>
<a href="{% if item.specific.link and item.specific.link != '' %}{{ item.specific.link }}{% elif item.specific_class == 'Node'%}#{% else %}{% pageurl item %}{% endif %}">{{ item.title }
</a>
</li>
{% endfor %}

Wagtail generic gallery implementation and OneToOneField

http://docs.wagtail.io/en/v1.13.1/getting_started/tutorial.html
The wagtail getting_started tutorial intros a blog gallery feature, implements as below:
class BlogPage(Page):
...
class BlogPageGalleryImage(Orderable)
page = ParentalKey(BlogPage, related_name='gallery_images')
image = ...
This way works, however BlogPageGalleryImage couples with BlogPage model. My intention is to make a generic gallery model which can be embbed with any model(page). The idea is using an intermediate Gallery model:
class BlogPage(Page):
gallery = models.OneToOneField(Gallery, on_delete=models.SET_NULL, null=True)
...
class Gallery(Page):
pass
class GalleryImage(Orderable):
gallery = ParentalKey(Gallery, related_name='images')
Then in code, we can get the images via blog.gallery.images.
My question is how to get it work with wagtail admin interface to inline create/edit the gallery object (OneToOneField) when editing the blog page object.
One way to do this is via a more generic relationship for your Page-Image connection, relating this to the Page model, rather than a specific BlogPage model.
This means that any page can have gallery images, you just need to expose the field to content panels via an InlinePanel.
You can also create a Mixin class to make some helpful methods available without rewriting them each time.
Here is an example:
from django.db import models
from wagtail.admin.edit_handlers import InlinePanel
from wagtail.core.models import Orderable, Page
from wagtail.images.edit_handlers import ImageChooserPanel
class ImageGalleryRelationship(Orderable, models.Model):
""" Relationship between any `Page` and `Image` for an image gallery."""
page = ParentalKey(Page, related_name='gallery_page')
image = models.ForeignKey('wagtailimages.Image', related_name='gallery_image')
panels = [ImageChooserPanel('image')]
class PageGalleryMixin():
def gallery_images(self):
images = [g.image for g in self.gallery_page.all()]
return images
class BlogPage(Page, PageGalleryMixin):
# all page fields, gallery does not need to be defined here
content_panels = Page.content_panels + [
InlinePanel('gallery_page', label='Image Gallery'),
#...
]
Note: This is not a OneToOne connection, InlinePanel requires a ParentalKey relationship. There is no real 'Gallery' model in this solution just a set of orderable relationships.
In general app (or wherever), models.py
class PageGalleryImage(Orderable):
page = ParentalKey(Page,
on_delete=models.CASCADE,
related_name='image_gallery')
image = models.ForeignKey('wagtailimages.Image',
on_delete=models.CASCADE,
related_name='page_gallery_image')
caption = models.CharField(blank=True, max_length=250)
panels = [
ImageChooserPanel('image'),
FieldPanel('caption'),
]
Other apps as blog, models.py:
class BlogPage(Page):
content_panels = Page.content_panels + [
...
InlinePanel('image_gallery', label="Gallery images"),
]
This provides one app = one gallery.

Resources