I'm new to Python and Wagtail. I've got most of the way with the Wagtail site I'm working on, but am stuck on one issue.
The site is to show a set of products (plants) for retail (done and working) and the same set for trade with trade-only prices shown. I have PlantIndexPage
and PlantPage
Wagtail models, with the latter having various product fields and two price fields.
The trade price information should be password protected with Wagtail's 'page privacy' password option, and so I believe I need a separate index page ( TradePlantIndexPage
) to which I can apply this page privacy setting in the Wagtail admin, to effectively contain its own trade versions of the public PlantPage
product pages.
So I have many PlantPage
pages (one for each plant product), and one TradePlantPage
page using RoutablePageMixin
with a @route
to accept the slug of any PlantPage
product.
# working: /plants/
class PlantIndexPage(Page):
def get_context(self, request):
context = super().get_context(request)
context['plants'] = Page.objects.exact_type(PlantPage).live().all().order_by('title')
return context
# working, password protected: /trade/
class TradePlantIndexPage(PlantIndexPage):
template = 'plants/plant_index_page.html' # for PlantIndexPage and TradePlantIndexPage
def get_context(self, request):
context = super().get_context(request)
context['trade'] = True # condition in template depends on this to show trade prices
return context
# working: /plants/<plant_page_slug>
class PlantPage(Page):
price_retail = models.DecimalField(...)
price_trade = models.DecimalField(...)
# ...
# not yet working: /trade/plant/<plant_page_slug>
class TradePlantPage(RoutablePageMixin, PlantPage):
parent_page_types = ['TradePlantIndexPage']
subpage_types = []
template = 'plants/plant_page.html' # for PlantPage and TradePlantPage
def get_context(self, request):
context = super().get_context(request)
context['trade'] = True # condition in template depends on this to show trade price
context['plant'] = Page.objects.page(???).live() # how to get PlantPage by slug?
return context
@route(r'^([-\w]+)/$')
def trade(self, request, plant_slug):
self.trade = True
# how to render TradePlantPage with PlantPage content based on plant_slug?
I'll have the TradePlantIndexPage
's list of plants link to the URL at which TradePlantPage
is available ( /trade/plant/<plant_page_slug>
) instead of PlantPage
( /plants/<plant_page_slug>
).
For displaying a particular plant with TradePlantPage
, how can I obtain the correct instance of PlantPage
based on the trade
@route
's plant_slug
, and set the context so that the template for PlantPage
, using the page
variable, works?
I've appended comments/questions related to this in the code. Thanks.
Thanks to gasman's kind and easy to understand answer the trade route for TradePlantPage
is now working. Following that answer's advice to use plant
and not page
, I have updated PlantPage
's context method to be:
def get_context(self, request):
context = super().get_context(request)
context['trade'] = False
context['plant'] = context['page']
return context
This is working, but I wonder if context['plant'] = context['page']
is a suboptimal way of being able to use plant
instead of page
in the shared template. I imagine it doubles the memory used for the field data, for example? Probably not an issue for this small site anyway, and I'll use it unless I hear otherwise.
Within a RoutablePageMixin
route method, the process of returning an HTTP response works the same way as in an ordinary Django view function - see Django's 'Views and templates' tutorial . The page's template
property and get_context
method are not used, unless you explicitly hook them up (and for the sake of simplicity, I'd suggest not doing that, and setting up the template and context variables directly in the method instead).
Your route method will need to do the following:
PlantPage
based on plant_slug
- we'll use Django's get_object_or_404
helper for this plants/plant_page.html
, passing it the context variables trade
(set to True), plant
(set to the retrieved PlantPage) and page
(set to the current page, ie the TradePlantPage
object). This gives us the code:
from django.shortcuts import get_object_or_404, render
class TradePlantPage(RoutablePageMixin, PlantPage):
parent_page_types = ['TradePlantIndexPage']
subpage_types = []
@route(r'^([-\w]+)/$')
def trade(self, request, plant_slug):
plant = get_object_or_404(PlantPage, slug=plant_slug)
return render(request, 'plants/plant_page.html', {
'trade': True,
'plant': plant,
'page': self,
})
Note: here I'm assuming that your plant_page.html
template is set up to expect the variable plant
when called from both PlantPage
and TradePlantPage
- eg you're using {{ plant.title }}
rather than {{ page.title }}
to output the plant name. If this isn't the case, it's probably OK to pass the PlantPage
object as page
, by setting 'page': plant,
in the code above. However, I'd suggest it's a good idea to give the template access to the 'real' page object currently being rendered - so that you can highlight the right item in your site navigation, for example.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.