Need Guidance for django wagtail - django

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.

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.

Wagtail url prefix for a custom Page model

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']

Wagtail per page user permission

My task is to enable users to edit their own page.
I see how to give permissions to a particular page to a group, but how do I do this for the user?
I'd tried use django guardian:
UserObjectPermission.objects.assign_perm('change_custompage', user, obj=custompage)
but this wasn't given me results
I also found the wagtailcore_grouppagepermission table and realized that the permissions principle is not the same as used in django auth_permission
I want to give permissions to edit, and access wagtail admin, I do not need anything else
In this particular task, wagtail I need because of the struct_blocks, I think this is a terrific concept.
Now I see two options: either an override template and the ability to use all the power of the struct_block outside the wagtail, or something to solve with permissions.
Two possibilities:
Create a group for each user - might not seem like a very sophisticated approach, but easily achievable with a bit of scripting...
Put your users in a group that has add permission, but not edit permission, on the parent page that contains all of your users' pages. In Wagtail's permission model http://docs.wagtail.io/en/v1.10.1/topics/permissions.html, 'add' permission also includes the ability to edit pages that you have created yourself. (If you'd rather have the pages created in advance, rather than having your users create them, then you need to set the owner field of the page record to the relevant user.)
Thanks to the help and advice of #gasman, I did this as follows:
Created two models:
first:
class PersonsIndexPage(RoutablePageMixin, Page):
...
#route(r'^$')
def index_view(self, request):
pages = PersonPage.objects.live().all()
return render(request, 'myapp/person_index_page.html', {
'page': self,
'pages': pages,
})
...
subpage_types = ['PersonPage']
second:
class PersonPage(Page):
...
parent_page_types = ['PersonsIndexPage']
subpage_types = []
For the group add access to wagtail admin, as well as gave permissions to create PersonsIndexPage
Since I create personal pages from Django admin, I created a simple action that creates a page for selected users:
I do not know if bulk_create is possible
def create_person_page(modeladmin, request, queryset):
root_page = PersonIndexPage.objects.first()
for user in queryset:
page = PersonPage(title=username), owner=user)
root_page.add_child(instance=page)
create_person_page.short_description = 'Create person page'

With django, how can I retrieve if the user "likes" the article of other items?

I have 3 tables in my database, the first one is the "user" table with users' basic information, the second one is the "article" table, and the last one is the "user_like_article" table which store a record for a user and a article if the user likes the article.
For instance, if a user A like an article X, the the record in the "user_like_article" table will be like this: (A,X)
So now, I want to indicate whether the user, who has logged in, like a certain article in the article list I diplay.
I have figure out a way. I first get the article list. After that, I get the list of article's id which the specific user likes. The other things work like the code below
for article in articles_to_diplay:
if article in article_list_user_likes:
article['like'] = True
This method works, but somehow I think it is too expensive. Displaying "like" button is just a little part of my project, but it is indeed very common. Facebook, Twitter, blogs, "like" is in widespread use.
Is there any way less expensive to accomplish this feature?
Thank you very much!
It's not quite simple to use some nice looking API, since we check query against logged in user object, so we need to inject is_liked method for each article object.
Also you can use ManyToManyField field for Article model, instead creating additional table (unless you're using some extra data, then let me know, so I can modify the answer accordingly.
models.py
from django.contrib.auth.models import User
class Article(models.Model):
title = models.CharField(max_length=50)
likes = models.ManyToManyField(User)
def __unicode__(self):
return self.title
views.py
def home(request):
articles = Article.objects.all().prefetch_related('likes')
for a in articles:
a.is_liked = (request.user in a.likes.all())
return render(request, 'home.html', {'articles': articles})

django admin hierarchical inline model edit

Consider a wiki application. There is a model Page, that has many Revisions and each revision has many blocks.
What is the simplest way to create an admin in which, you select a page and all the blocks of the latest revision appear; bonus points for letting change of revision by a dropdown (which is by default, sorted in reverse order anyway)
Is it absolutely necessary to create views, or can I extend some of those StackedInline forms, override save and mention some magic meta options, to get it all done automagically.
Have you tried something like this (in admin.py):
class RevInline(admin.TabularInline):
model = Revision
class PageAdmin(admin.ModelAdmin):
model = Page
inlines = (RevInline,)
admin.site.register(Page, PageAdmin)