How to display certain Wagtail page on the homepage? - wagtail

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.

Related

My urls no longer point to my pages 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.

Routablepage of the BlogPage/category

I fail to display the routable index page for categories/given category.
My code uses:
-BlogCategory(model)
-BlogPageBlogCategory (page) an intemediary structure that links to:
-PostPage(page)
I can pass the code to the post_page template, but when I click on a specific category link I get:
Reverse for 'post_by_category' with arguments '('',)' not found. 1 pattern(s) tried: ['category/(?P<category>[-\\w]+)/$']
In the following post #gasman wrote: "the routablepageurl tag has received an empty string". I couldn't find the 'empty' string/missing link.
I assume it's related to the route of my 'def post_by_category'. Any input that would help me deepen my learning woulg be great.
NB - in case it helps, when I run this procedure without the intemeiary page all's fine. I can display the BlogPage/given_category once I click on the category in the PostPage.
Here's my code:
Models
class BlogPage(RoutablePageMixin, Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context["posts"] = posts
return context
def get_posts(self):
return PostPage.objects.descendant_of(self).live().order_by("-post_date")
#route(r'^category/(?P<category>[-\w]+)/$')
def post_by_category(self, request, category, *args, **kwargs):
self.posts = self.get_posts().filter(categories__blog_category__slug=category)
context["categories"] = BlogCategory.objects.all()
return self.render(request)
class PostPage(MetadataPageMixin, Page):
...
content_panels = Page.content_panels + [
...
InlinePanel("categories", label="category"),
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
return context
class PostPageBlogCategory(models.Model):
page = ParentalKey(
"blog.PostPage", on_delete=models.CASCADE, related_name="categories"
)
blog_category = models.ForeignKey(
"blog.BlogCategory", on_delete=models.CASCADE, related_name="post_pages"
)
panels = [
SnippetChooserPanel("blog_category"),
]
class Meta:
unique_together = ("page", "blog_category")
#register_snippet
class BlogCategory(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=80)
panels = [
FieldPanel('name'),
FieldPanel("slug"),
]
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'categories'
The post_page.html:
{% extends "base.html" %}
{% load wagtailroutablepage_tags %}
...
{% for category in page.categories.all %}
<li>
<a href="{% routablepageurl blog_page "post_by_category" category.slug %}">
{{ blog_category.name }}
</a>
</li>
{% empty %}
No categories yet'
{% endfor %}
...
After debugging and testing, I didn't find any blank categories, (which doesn't mean there were none, but that I didn't find or deleted them Unintentionally).
I case it could help, what worked for me was adding the category_type at the beginning of my varaiable:
{{ category.blog_category.name }} rather than {{ blog_category.name }}
{% for category in page.categories.all %}
<a href="{% routablepageurl article_index_page "post_by_category" category.blog_category.slug %}" class="link-primary text-decoration-none">
{{ category.blog_category.name }}
</a>
{% empty %}
No categories yet'
{% endfor %}

Wagtail formbuilder submissions are not sending emails

Contact form data (using Wagtail's formbuilder) is not sending to email what is the issue?
I added email host also but not sending data to email
The form response (contact data) is being saved in wagtail admin but data not sending to email.
Console Output
DEBUG CONSOLE
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: contact form submission
From: archanapco8#gmail.com
To: ranjuranjitha. 1997#gmail.com
Date: Mon, 08 Nov 2021 08:13:45 -0000
Message-ID: <163635922547.9304.15785246130187863651#DESKTOP-NC1TOGR>
Auto-submitted: auto-generated
Your Name: ranjitha
Your company: mdrift
Your email address: rmanju#dgmail.com
When do you want to start?: 20-09-2021
What is your budget: 1000
Describe your needs, the more we know, the better: hi
[08/Nov/2021 13:43:45] "POST /contact-us/ HTTP/1.1" 200 9590
[08/Nov/2021 13:43:45] "GET /static/img/Thank-you.png HTTP/1.1" 200 15470
O
contact/models.py
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
class FormField(AbstractFormField):
page = ParentalKey('Form Page', related_name='custom_form_fields')
class Form Page(AbstractEmailForm):
thank_you_text = RichTextField(blank=True)
content_panels = AbstractEmailForm.content_panels + [
InlinePanel('custom_form_fields', label="Form fields"),
FieldPanel('thank_you_text', classname="full"),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
], "Email Notification Config"),
]
def get_form_fields(self):
return self.custom_form_fields.all()
form_page.html
{% load static wagtailcore_tags widget_tweaks %}
{% block content %}
<h1>Contact</h1>
<br>
<form action="{% pageurl page %}" method="POST">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% render_field field class+="form-control" %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary" >Submit</button>
</form>
{% endblock %}
settings/base.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'ranjuranjitha.1997#gmail.com'
EMAIL_HOST_PASSWORD = 'Password$'
EMAIL_USE_TLS = True
EMAIL_USE_SSL = True
DEFAULT_FROM_EMAIL ='ranjuranjitha.1997#gmail.com'
EMAIL_TO = 'ap8366106#gmail.com'
This is dev.py file. I given email backend also but not sending the mail.
settings/dev.py
from .base import *
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-iu22rfee4za6ro+mez!4*#_trpy7!ebpbtu8iw$95v(rh_5fib'
# SECURITY WARNING: define the correct hosts in production!
ALLOWED_HOSTS = ['*']
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
try:
from .local import *
except ImportError:
pass
A few potential solutions below.
1. dev mode settings
Based on your log output and your settings/dev.py file, it appears that the email is working but is just logging the email.
In the dev settings you have EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend', this is a normal dev setup and leverages the Email Console backend which will never actually send an email but just log the email output to the console (terminal).
This means, while in development mode you can easily check the email content without actually sending a real email.
To temporarily override this you can create a file settings/local.py and put a different email backend in that file.
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
Then restart your dev server and it should use the override in the local.py file.
This works because of the settings/dev.py code you already have
try:
from .local import *
except ImportError:
pass
Note: Ensure that your settings/local.py is not committed to your code (git) as this may contain private secrets/passwords etc and usually is just a quick way to test something different.
Refer to this answer about managing Django settings for more insights. It would be good to read the full documentation page end to end that explains how sending email works in Django also.
2. Gmail settings
Check you have ensured that your Gmail will allow SMTP sending? You need to ensure less secure apps are enabled - support.google.com/accounts/answer/6010255?hl=en
See also How to send Django emails with SMTP for more details.
However, using Gmail for this purpose is quite risky and likely to be blocked by Gmail and have spam issues. It is recommended you use a third party service, see some recommendations for third party email services.

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 %}

delete function need write user into useredit

every one I got
models.py
....
class ProductsTbl(models.Model):
.....
slug = models.SlugField(unique=True)
user = models.ForeignKey(User, blank=True, null=True)
useredit = models.CharField(max_length=32, blank=True, null=True)
image = models.ImageField(upload_to=get_imagep_Product, blank=True)
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.created = timezone.now()
return super(ProductsTbl, self).save(*args, **kwargs)
def get_image_path(instance, filename):
return '/'.join(['thing_images', instance.thing.slug, filename])
class Upload(models.Model):
thing = models.ForeignKey(ProductsTbl, related_name="uploads")
image = models.ImageField(upload_to=get_image_path) #delete or upload image for this one
def save(self, *args, **kwargs):
super(Upload, self).save(*args, **kwargs)
if self.image:
image = Image.open(self.image)
i_width, i_height = image.size
max_size = (640,480)
if i_width > 1000:
image.thumbnail(max_size, Image.ANTIALIAS)
image.save(self.image.path)
and I got the views.py function it is for delete the image in class Upload(models.Model) of models.py,
views.py
.....
#login_required
def delete_upload(request, id):
# grab the image
upload = Upload.objects.get(id=id)
upload.thing.useredit = request.user.username
upload.save()
# security check
# if upload.thing.user != request.user:
# raise Http404
# delete the image
upload.delete()
# refresh the edit page
return redirect('edit_thing_uploads', slug=upload.thing.slug)
what I have to do is when I delete the image I have to write the "user" into the
"useredit".
however,,when never I delete the image the "user" won't write into "useredit"
,,in contrast,here is my
edit_thing_uploads.html
{% extends 'base.html' %}
{% block title %}
Edit {{ thing.name }}'s Images - {{ block.super }} {% endblock %}
{% block content %}
<h1>Edit {{ thing.name }}'s Images</h1>
<h2>Uploaded images</h2>
{% for upload in uploads %}
<img src="{{ upload.image.url }}" alt="" />
Delete
{% endfor %}
<h2>Upload a new image</h2>
<form role="form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
when I upload an image it will write "user" into "useredit" successfully ,,the function for upload image in
views.py
#login_required
def edit_thing_uploads(request, slug):
# grab the object...
thing = ProductsTbl.objects.get(slug=slug)
# double checking just for security
# if thing.user != request.user:
# raise Http404
form_class = ProductsTblUploadForm
# if we're coming to this view from a submitted form,
if request.method == 'POST':
# grab the data from the submitted form, # note the new "files" part
form = form_class(data=request.POST,files=request.FILES, instance=thing)
if form.is_valid():
thing = form.save(commit=False)
thing.useredit = request.user.username
thing.save()
# create a new object from the submitted form
Upload.objects.create(
image=form.cleaned_data['image'],
thing=thing,
)
return redirect('edit_thing_uploads', slug=thing.slug)
# otherwise just create the form
else:
form = form_class(instance=thing)
# grab all the object's images
uploads = thing.uploads.all()
# and render the template
return render(request, 'things/edit_thing_uploads.html', {
'thing': thing,
'form': form,
'uploads': uploads,
})
however,,I have to let the "user" into "useredit" when I delete also,,,how can I do it? thank you!
I solve the problem
views.py
#login_required
def delete_upload(request, id):
# grab the image
upload = Upload.objects.get(id=id)
upload.thing.useredit = request.user.username
upload.thing.save()
# security check
# if upload.thing.user != request.user:
# raise Http404
# delete the image
upload.delete()
# refresh the edit page
return redirect('edit_thing_uploads', slug=upload.thing.slug)

Resources