meeb / django-distill

Minimal configuration static site generator for Django
MIT License
441 stars 35 forks source link

Triggering page generation manually? #21

Closed denny64 closed 4 years ago

denny64 commented 4 years ago

Hi. Is there a way to trigger the generation of static pages manually? Eg. Trigger it to render on receiver save?

I've got 50000 mostly static pages that only need to be refreshed once a day. What's the best way to handle that? It would take too long to prerender 50K pages everytime I deploy.

Thanks

meeb commented 4 years ago

Hi,

It sounds like your use case might not be an ideal candidate for complete static site generation. What's your plan? I assume if you have a functioning Django site which updates content regularly and this site is public it might be a more sensible route to just put a caching proxy in front of it like nginx with a 12 hour cache on the semi-static pages. If you are just editing 1 or 2 pages a day and it's mainly just entirely static and you still want to use distill it is possible to use selected page renderings on save, however I've never deployed this myself.

distill works off of URLs, not models, it has no knowledge of what is required to render a particular URL so coupling distill to models doesn't conceptually make much sense. Of course, if you have one main model per page this could work for you.

What you would need to do is restrict the URLs you're generating to just the ones you want to regenerate after saving a model. Internally, distill keeps a list of the URLs to generate from when you call distill_path() in your urls.py, here:

https://github.com/meeb/django-distill/blob/master/django_distill/distill.py#L4

so you could manually run the renderer with a custom list of urls, something like this (note untested code, just hand-written in ticket to give you an idea so likely has issues):

# Assuming you have a model called PageContent, where single models are referenced
# by a URL like /page/content/1 where '1' is a PageContent primary key

# manually import the rendering the rendering helper
from django_distill.renderer import render_to_dir

# render_to_dir takes a func for log handling, this can be logging.info or print, in this case do nothing
def no_logging(*args, **kwargs):
    pass

@receiver(post_save, sender=PageContent)
def distill_page_on_save_handler(sender, instance, **kwargs):

    def iter_one_item():
        # distill needs a func to yield the items to iterate over to generate the URLs
        # this could be a lambda, but making it a method for verbosity, yield our model
        # instance which fits into our URL /page/content/<int:id>
        yield instance.pk

    # create a custom list of URLs to render
    urls_to_distill = [
        # The URLs to render, this will duplicate some of your stuff in your apps urls.py, format:
        # (distill_func, distill_file, view_name, view_args, view_kwargs)
        (iter_one_item, None, SomeView.as_view(), 'view-name', (), {})
        # this is exactly the same as calling
        #   distill_path('page/content/<int:id>',
        #                    SomeView.as_view(),
        #                    name='view-name',
        #                    distill_func=iter_one_item),
        # in your urls.py
    ]
    # spam out your static files
    render_to_dir('/some/output/directory', urls_to_distill, no_logging)

This will do the sort of thing you want, or at least get you on the right track of where to hack in your static rendering requirements.

I don't have any particular plan at the moment to add this a more supported feature as it seems relatively niche and you're the first person to ask about it.

meeb commented 4 years ago

Heya,

I'll close this issue for now. Feel free to ask follow-up questions if you get stuck.

Cheers.