简体   繁体   中英

Obtaining another page's data for a password-protected version in Wagtail

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.


Update

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:

  • Retrieve the correct PlantPage based on plant_slug - we'll use Django's get_object_or_404 helper for this
  • Render the template 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM