Routablepage of the BlogPage/category - django-models

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

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.

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

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)

Configuring MIME type

Hi I want to configure my mime type:
The MIME type for KML files is
* application/vnd.google-earth.kml+xml
How can I do this with google app engine? I generate KML on a template that looks like this:
<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>{% for article in articles %}{% if article.geopt %}
<Placemark><name></name>
<description>
<![CDATA[{% if article.kmluri2view %}<img src="http://{{host}}/images/{{ article.kmluri2view.key.id }}.jpg">{% endif %} {{ article.title }} <br/>{{article.text}}]]></description><Point><coordinates>{{article.geopt.lon|floatformat:2}},{{article.geopt.lat|floatformat:2}}</coordinates></Point>
</Placemark>{% endif %}{% endfor %}
</Document>
</kml>
Updated the code I try set the MIME type like below. How can I verify it works?
class KMLHandler(webapp.RequestHandler):
def get(self):
start=datetime.datetime.now()-timedelta(days=10)#vary
host = os.environ.get("HTTP_HOST", os.environ["SERVER_NAME"])
logging.debug('host '+host)
count = int(self.request.get('count')) if not self.request.get('count')=='' else 1000
from google.appengine.api import memcache
memcache.flush_all()
memcache_key = "ads"
data = memcache.get(memcache_key)
if data is None:
a= Ad.all().filter("modified >", start).filter("url IN", ['www.koolbusiness.com']).filter("published =", True).order("-modified").fetch(count)
memcache.set("ads", a)
else:
a = data
dispatch='templates/kml.html'
template_values = {'a': a , 'request':self.request, 'host':host}
path = os.path.join(os.path.dirname(__file__), dispatch)
self.response.headers['Content-Type'] = 'application/vnd.google-earth.kml+xml'
self.response.out.write(template.render(path, template_values))
Simply set the Content-Type header in the response to the mimetype you want. If you're using webapp, for instance, you do it like this:
self.response.headers['Content-Type'] = 'application/vnd.google-earth.kml+xml'

Resources