Home > Mobile >  Wagtail add functions to models.py
Wagtail add functions to models.py

Time:12-06

i'm trying to make a custom plotly-graphic on a wagtail homepage. I got this far. I'm overriding the wagtail Page-model by altering the context returned to the template. Am i doing this the right way, is this possible in models.py ?

Thnx in advanced.

from django.db import models

from wagtail.models import Page
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel

import psycopg2
from psycopg2 import sql
import pandas as pd
import plotly.graph_objs as go
from plotly.offline import plot

class CasPage(Page):
    body = RichTextField(blank=True)

    content_panels = Page.content_panels   [
        FieldPanel('body'),
    ]

    def get_connection(self):
        try:
            return psycopg2.connect(
                database="xxxx",
                user="xxxx",
                password="xxxx",
                host="xxxxxxxxxxxxx",
                port=xxxxx,
            )
        except:
            return False
    conn = get_connection()
    cursor = conn.cursor()

    strquery = (f'''SELECT t.datum, t.grwaarde - LAG(t.grwaarde,1) OVER (ORDER BY datum) AS 
      gebruiktgas
                FROM XXX
                     ''')

    data = pd.read_sql(strquery, conn)

    fig1 = go.Figure(
    data = data,
    layout=go.Layout(
        title="Gas-verbruik",
        yaxis_title="aantal M3")
        )

    output = plotly.plot(fig1, output_type='div', include_plotlyjs=False)

    # https://stackoverflow.com/questions/32626815/wagtail-views-extra-context
    def get_context(self, request):
        context = super(CasPage, self).get_context(request)
        context['output'] = output
        return context

CodePudding user response:

Kind of the right track. You should move all the plot code into its own method though. At the moment, it runs the plot code when the site initialises then stays stored in memory.

There's three usual ways to get the plot to the rendered page then.

  1. As you've done with context
  2. As a property or method of the page class
  3. As a template tag called from the template

The first two have more or less the same effect, except the 2nd makes the property available anywhere, not just the template. The context method runs before the page starts rendering, the other two happen during that process. I guess the only real difference there is that if you're using template caching, the context will always run each time the page is loaded, the other two only run when the cache is invalid, or if the code is escaped out of the cache (for fragment caching).

To call the plot as a property of your page class, you'd just pull out the code into a def with the @property decorator:

class CasPage(Page):
    ....
    @property
    def plot(self):
        try:
            conn = psycopg2.connect(
                database="xxxx",
                user="xxxx",
                password="xxxx",
                host="xxxxxxxxxxxxx",
                port=xxxxx,
            )
            cursor = conn.cursor()
            strquery = (f'''SELECT t.datum, t.grwaarde - LAG(t.grwaarde,1) OVER (ORDER BY datum) AS 
                            gebruiktgas FROM XXX''')
            data = pd.read_sql(strquery, conn)

            fig1 = go.Figure(
                data = data,
                layout=go.Layout(
                    title="Gas-verbruik",
                    yaxis_title="aantal M3")
                )
            return plotly.plot(fig1, output_type='div', include_plotlyjs=False)        
            
        except Exception as e:
            print(f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}")       
            return None

^ I haven't tried this code ... it should work as is, but no guarantees I didn't make a typo ;)

Now you can access your plot with {{ self.plot }} in the template.

If you want to stick with context, then you'd stay with the def above but just amend your output line to

context['output'] = self.plot

Template tags are more useful when they're being used in StructBlocks and not part of a page class like this, or where you have code that you want to re-use in multiple templates.

Then you'd move all that plot code into a template tag file, register it and call it in the template with {% plot %}. Wagtail template tags work the same as Django: https://docs.djangoproject.com/en/4.1/howto/custom-template-tags/

Is the plot data outside of the site database? If not, you could probably get the data via the ORM if it was defined as a model. If so, it's probably worth writing a view (or stored procedure if you want to pass parameters) on the db server and calling that rather than hard coding the SQL into your python.

The other consideration is the page load time - if the dataset is big, this could take a while and prevent the page from loading. You'd probably want a front-end solution in that case.

  • Related