Wagtail url prefix for a custom Page model - django

This question is probably trivial, but I am unable to see a simple solution.
I have custom page model representing Post:
class PostPage(Page):
I would like to make all instances of this model (all Posts) accessible only with url prefix
/posts/
Example:
User creates new Post, the assigned slug will be
awesome-first-post
What should happen is, that
/awesome-first-post/
will result in 404, while
/posts/awesome-first-post/
will display the post.
Note: I want this prefix only for the specific model Postpage. Other pages should be served directly from their slug.

In Wagtail, page URLs are formed from the list of slugs of the page's parent and ancestor pages, based on the page's position in the tree - the developer doesn't specify them directly. So, to get the URL /posts/awesome-first-post/, create a page with the slug posts (usually you'd create a dedicated PostIndexPage page model to serve as a listing page), and create the page awesome-first-post as a child of that one (by clicking the '+' icon next to the Posts page in the explorer listing view).
If you want to make sure that users only ever create PostPages as children of the PostIndexPage, use a subpage_types / parent_page_types setting, for example:
class PostPage(Page):
# ...
parent_page_types = ['PostIndexPage']

Related

Add own functions in views.py wagtail

I have setup a wagtail website. It works great for postings like a blog and simply add new pages.
But what if I want to add some extra functions to a page. Like showing values from my own database in a table.
Normally i use a models.py, views.py and template.py. But now I don’t see any views.py to add functions or a urls.py to redirect to an url?
Don’t know where to start!
Or is this not the meaning of a wagtail site, to customize it that way?
Thnx in advanced.
You can certainly add additional data to pages. One option is to add the additional information to the context of a page type by overriding its get_context method. For example, this page is just a place to display a bunch of links. The links and the collections they belong to are plain old Django models (managed as snippets). And then there is a page model that queries the database like this:
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
collection_tuples = []
site = Site.find_for_request(request)
for collection in Collection.objects.filter(links__audiences=self.audience, site=site).distinct():
links = Link.objects.filter(audiences=self.audience, collections=collection, site=site)
collection_tuples.append((collection.name, links.order_by('text')))
# sort collection tuples by the collection name before sending to the template
context['collection_tuples'] = sorted(collection_tuples, key=lambda x: x[0], reverse=False)
return context
Another option is to do basically the same thing - but in a StructBlock. Then you can include the StructBlock in a StreamField on your page. Most of the Caltech site is written using blocks that can be included in one large StreamField on a page. Some of those blocks manage their own content, e.g. rich text blocks or image blocks, but others query data and render it in a block template.
To add to #cnk's excellent answer - you can absolutely use views.py and urls.py just as you would in an ordinary Django project. However, any views you define in that way will be available at a fixed URL, which means they'll be distinct from the Wagtail page system (where the URL for a page is determined by the page slug that the editor chooses within the Wagtail admin).
If you're defining URLs this way, make sure they appear above the include(wagtail_urls) route in your project's top-level urls.py.

How to override the default wagtail page slug

I'm trying to make a wagtail pages for the astrologer based on category, country and city.
And my url's are:
https//localhost:8080/<category>
https//localhost:8080/<category>/<country>
https//localhost:8080/<category><country>/<city>
If I am trying to save the url with '/' forward slash then it removing the '/' form the slug and saving it as a string.
Ex: <category><country>/<city> to categorycountrycity
So Is it possible to override the wagtail default page slug type 'SlugField' to 'CharField'.
or is there any other solution ?
URLs are tightly tied to the page tree, and the slug is just the part which identifies the page at that level, rather than any parent levels. So if you want https://localhost:8080/<category>/<country>/<city>, you will need:
A page at https://localhost:8080/<category>/, whose slug is just the name of the category
The above category page to have a child, with the slug of just the country (which will give it the URL https://localhost:8080/<category>/<country>/)
The country page to have a child page, with the slug of just the city name (which will give it the URL https://localhost:8080/<category>/<country>/<city>).
Need other cities, make more child pages under the "country" page. Need more countries, make more child pages under the "category" page. Need more categories, make more child pages under the root page.

Transfer Class-based view to another

I created a DetailView class that can receive links from categories or posts from a website. In the “get_object ()” method, I identify if it is Category or Post models (by url slug). The URLs for this class are listed below:
/category/
/category/subcategory/
/category/post/
/category/subcategory/post/
The class got too long because the categories and posts have specific behaviors.
I was wondering if it is possible to "transfer" one class to another to handle specific information?
For example:
GenericView redirect to CategoryView after identifying that it is the url: / category / or for PostView, if it is / category / post /
NOTE: I am transferring the Wordpress site to Django, so I cannot change the url structure.
Is there any way to do this? Do you suggest another better solution?

Need Guidance for django wagtail

currently i'm trying to Integrating Wagtail with existing django project.
I'm new in wagtail, and still learning about wagtail
class BlogPage(Page):
body = RichTextField(blank=True)
categories = ParentalManyToManyField('blog.BlogCategory', blank=True)
location = models.ForeignKey('blog.Location', on_delete=models.PROTECT)
and then i register the category and the location model as snippets.
how's the best practice for build page contains of BlogPage with
certain category / location ?
and how to call that page from django's menu
or maybe where can i find documentation for integrating wagtail to existing django project
Thank you
I think you're looking for a Blog Listing Page, where you can list all your blog posts and then have blog posts based on a certain category.
You'll probably want to use a RoutablePageMixin (if you're not creating an SPA with Vue or React). A RoutablePageMixin lets you automatically create additional child pages, without having to create Wagtail child pages.
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
class BlogListingPage(RoutablePageMixin, Page):
"""BlogListingPage class."""
template = 'cms/blog/blog_listing_page.html'
subpage_types = ['pages.BlogPage']
# ... other fields here
#route(r'^category/(?P<cat_slug>[-\w]*)/$', name='category_list')
def category_list(self, request, cat_slug):
"""Return posts in a certain category."""
context = self.get_context(request)
posts = BlogPage.objects.live().filter(categories__slug=cat_slug).order_by('-pub_date')
context['posts'] = posts
return render(request, 'cms/blog/blog_category_page.html', context)
Note I did not test this code, you may need to fix any errors and adjust this for your needs.
The above code will take your blog listing page (say its localhost:8000/blog/) and create a category listing page (ie. localhost:8000/blog/category/topic-slug/)
That topic-slug will be passed into the category_list() method, where you can then filter your BlogPage's based on the category(ies) it's in. It will add posts to your page, and render a different listing page, where you can customize your template.
It's been a while since I checked, but the Wagtail Bakery Demo probably has examples of this in there (and a lot of really sweet goodies).
You can read more about Wagtail Routable Pages at https://docs.wagtail.io/en/latest/reference/contrib/routablepage.html as well.

Customize ImageField url/path

I have a Page model that basically describes an HTML page. Pages are then served with URLs, such as http://www.mysite.com/page/1234/ for the page of id (pk) 1234.
I want to be able to add or attach images to my page. Therefore, I would like to use an Image class with a foreign key to a Page object:
class Page(models.Model):
title = ...
content = ...
class Image(models.Model):
page = models.ForeignKey(Page)
image = models.ImageField(...)
Here is my problem: I would like to deliver images to the client with urls of the form:
http://www.mysite.com/images/1234/image_name.jpg, i.e a URL that includes the page id. Also on the server, the paths should reflect the page structure: /path/to/media/images/1234/image_name.jpg
I don't know how to tackle this problem. On one hand, I would like to keep the features of an ImageField related to path formatting. For example when uploading two images with the same name, Django creates two paths ending with "image.jpg" and "image_2.jpg" or so to make the difference between both images.
On the other hand, the upload_to option has limited capability, and I don't know how to insert the page id in the path. Especially, some tricky cases such as uploading an image at the same time a page is created (using the same form), which means a page id should be generated before uploading the image.
Is it reasonably easy to make an image model that would behave as described above? If so, how do I have to modify the Image model to insert the page id in the image path?
I have seen the question Customize save path for ImageField, but it does not address the problem of primary key that might not be assigned.
Thanks.
Actually, a simple function passed as an upload_to parameter works, and there is no problem of non-existing id. So I guess that Django's default behaviour saves the image after saving the parent page model, as I wanted to.
In the Image model:
image = models.ImageField(upload_to=get_image_path)
with the following function:
def get_image_path(instance, filename):
return 'pics/' + str(instance.page.id) + '/' + filename