Package: django-htmx-ui
IntroWhat isdjango_htmx_uilibraryThis library is made to combine and help leveraging:the full-stackdjangoframeworkthe frontendhtmxframeworkthedjango-htmxlibrarythejinjatemplate engineIt is basically a django app that provides:Extended djangoViewswith htmx build-in functionalityCRUD Views(Create, Retrieve, Update, Delete) for django modelsSome extraMixinsto use with yourViewsto make life easierA ready to usejinjaenvironmentSomeMiddlewaresfor automationsExtrautilsanddecoratorsfor common use casesRequirementsPython 3.11Django 4.1htmx 1.8jinja 3.1Installationvia pippip install django_htmx_ui`or add library into requirements.txtdjango_htmx_uiUsageSetupAdd the app in yoursettings.py:INSTALLED_APPS = [
     # ...
     'django_htmx_ui',
     # ...
 ]Add the middlewares you would like to use in yoursettings.py:MIDDLEWARE = [
      # ...
     'django_htmx_ui.middleware.HtmxMessagesMiddleware',
      # ...
 ]Add the jinja environment in yoursettings.py:TEMPLATES = [
      {
          'BACKEND': 'django.template.backends.jinja2.Jinja2',
          'DIRS': [BASE_DIR / 'jinja2'],
          'APP_DIRS': True,
          'OPTIONS': {
              'environment': 'django_htmx_ui.jinja.environment',
          },
      },
      # ... you can also keep your django templates engine here ...
 ]You can also extend it by creating your own inyour_project/jinja.pymodule:import django_htmx_ui.jinja

 def environment(**options):
     env = django_htmx_ui.jinja.environment(**options)

     # Add your own jinja functionalities like globals or filters here.
   
     return env...and then replace the'environment'variable inside the'OPTIONS'key:# ...
 'OPTIONS': {
     'environment': 'your_project.jinja.environment',
 },
 # ...Finally, to use the jinja FORM_RENDERER, add / replace in yoursettings.py:from django_htmx_ui.jinja import get_form_renderer
 # ...
 FORM_RENDERER = get_form_renderer()ViewsDefining viewsThe recommended structure to place your views inside your project is the following:django_project/django_app/views/module_a.pymodule_b.pyInside each view module (e.g.module_a.py) you can assign some optional module globals,
that will be used by all yourViewTemplateclasses inside the module, such as:SLUG: A slug name for the moduleTEMPLATES_DIR: The directory of the view's templatesTITLE: A title for all viewsICON: An icon for all viewsMODEL: A model in your database to automate the CRUD functionality for this moduleThen you can create someTemplateViewclasses, like this:module_afile:class UserDashboard(PrivateTemplateView):
    ... your special properties go here ...module_bfile:MODEL = User

class List(CrudListMixin, PrivateTemplateView):
    ... your special properties go here ...

class Update(CrudUpdateMixin, PrivateTemplateView):
    ... your special properties go here ...

class Display(CrudDisplayMixin, PrivateTemplateView):
    ... your special properties go here ...By default, the templates will be searched automatically in the directories:django_project/django_app/jinja2/django_app/module_a/user_dashboard.htmlfor class namedUserDashboardin yourmodule_amodule_b/list.htmlfor class namedListin yourmodule_bupdate.htmlfor class namedUpdatein yourmodule_bdisplay.htmlfor class namedDisplayin yourmodule_bTo make you life even easier you can use thedjango_htmx_ui.utils.collect_pathsfunction to help you automatically build your app'surls.pyfile.For exampledjango_project/django_app/urls.pyshould be:from django_app.views import module_a, module_b
from django_htmx_ui.utils import collect_paths


app_name = 'django_app'
urlpatterns = [
    collect_paths(module_a, app_name),
    collect_paths(module_b, app_name),
]Best practices when developing your projectCreating origin root templates containing the  tag pages.When the browser makes the first http, this will be outside htmx.
From now on, we will call this page an origin page, because it must serve all the basic
components to load a standard html page, like ,  tag and javascript
libraries including htmx.If you build a big project, chances are that you will need more than one origin pages.For example, login, signup, password forget, errors etc. pages, can be part of an origin
page calledWelcomeOrigin(OriginTemplateMixin, PublicTemplateView).
Please notive that we used theOriginTemplateMixinto tag that this view is an origin
and thePublicTemplateViewto tag that it will be accessible by everyone.Other pages, like dashboard, account, user profile, etc. need to be accessible only by
the signed-in user, so the origin page could be calledDefaultPanelOrigin(OriginTemplateMixin, PrivateTemplateView).WelcomeOriginandDefaultPanelOrigintemplates will be used as the base html page.Creating your sub-pagesAll sub-pages can extend these two basic classes like this:Login(WelcomeOrigin)orDashboard(DefaultPanelOrigin).
These sub-pages will have their own templates, but will be served by a htmx lazy-load get.Creating a partial sub-pageIf you now want to create a Widget inside your user's dashboard page, you can define
its view asWidget(PartialTemplateMixin, DefaultPanelOrigin). Please, notice the usage of thePartialTemplateMixin. When you add this Mixin, it means that this view can only be requested
via a htmx request, therefore it can't be opened directly from the browser's address
bar.Creating your first html with htmx usageThis is a very simplified example to make you understand what this library does.Firstlt, we must create some views:app_name/views/welcome.py:class WelcomeOrigin(OriginTemplateMixin, PublicTemplateView):
    pass

class Login(FormMixin, WelcomeOrigin):
    
    class Form(forms.Form):
        user = forms.CharField()
        password = forms.CharField()In order to display the above views, we create also their templates:app_name/jinja2/app_name/welcome/welcome_origin.html:<html>
    <head>...</head>
    <body>
        <div hx-get="{{ url }}">
    </body>
</html>app_name/jinja2/app_name/welcome/login.html:<p>Login Form:</p>
<form>
    {{ form }}
</form>This an incomplete and simplified example to demonstrate how this library works.
You can create more complex scenarios, if you dive in the following documentation.Generic ViewsThe followingViewsare basically an extended version of django's classTemplateViewto include all the helper properties and the shortcuts to make your life easier
with the combination of the htmx library. The first step is to choose the right
class type to extend, according to your needs, and apply all the extra properties.
Feel free to override any properties and extend futher more your own classes.django_htmx_ui.views.generic.PublicTemplateViewThis class extends the django'sTemplateViewclass and
is the main class that provides the basic functionality.
ThePublickeyword in the name refers that the class isnotrequiring the user to be authenticated in order to
access the view.Provides the following attributes:on_get (self, request, *args, **kwargs)methodThis is called any time a httpGETrequest is made to the view.
You can return a django'sResponseobject.
If you don't return, the template of the view will be
rendered and returned as a response.on_post (self, request, *args, **kwargs)methodThis is called any time a httpPOSTrequest is made to the view.
You can return a django'sResponseobject.
If you don't return, the template of the view will be
rendered and returned as a response.requestpropertyThis property refers to theRequestdjango's object.
You can use this property in any method you describe in your views.responsepropertyAnother way you can set a response, is by setting theself.responseproperty.
This is a rare case and in most cases you will not need to set or access this
property directly.response_location (self, *args, **kwargs)methodWhen the view is used by a htmx request, you can use this function to make adjango_htmx.http.HttpResponseLocationresponse. Allargsandkwargsare
passed through.response_no_content (self)methodUse this as a response when you have no content to send.redirect (self, url)methodUse this as a response to redirect the browser's location in another path.
Keep in mind that the whole will be refreshed, even if you are inside a htmx request.urlcontext propertyUse this property to retrieve current view'sUrlViewobject.
You can use inside your template file via the variable{{ url }}.
You can also add GET query parameters easily with{{ url.query(page=1) }}.
See theUrlViewobject description for more.url(view, *args, **kwargs)methodTheurlattribute can also be called as a method.
Use this method to build a newUrlViewobject, based on aTemplateViewobject.
Theviewparameter can be either a view name (e.g.'django_app:module:my_view') or
a python view path (e.g.'django_app.views.module.MyView') or
aTemplateViewobject.
Allargsandkwargsare passed through to the djangoreversefunction to build
the url.
You can use inside your template file with{{ url('django_app:module:my_view') }}.location_barpropertyThis property refers to the location (URL) bar of the browser.
Under the hook, it reads the htmx'sHX-Current-URLheader, and if this is not
available (aka is not a htmx request), it will read therequest.META['PATH_INFO'].
You can also update the location bar anywhere in your view.
See theLocationobject description for more.location_reqpropertyThis property refers to the location (URL) of the current server request.
Under the hook, it will always read therequest.META['PATH_INFO']and create aLocationobject for you.
See theLocationobject description for more.headersdictionaryUse this dictionary property to add headers at the response.trigger_client_event (self, *args, **kwargs)methodWhen the view is used by a htmx request, you can send a htmx client event using this
function. Allargsandkwargsare passed through to thedjango_htmx.http.trigger_client_eventfunction and finally to the browser event object.slug_globalclass propertyThis class property defines a global slug to use as a unique identifier for theTemplateViewin you entire project.
By default, it's a combination ofslug_module+slugdescribed below.slug_moduleclass propertyThis class property defines the slug name of the module that your view belongs to.
You can alter this property and build your own method if needed.slugclass propertyThis class property defines the slug name of your view.
By default, it returns the name of the class with snake case conversion
(e.g. MyClass becomes my_class).
This will be used with the module slug to build the view path ('my_module/my_class/').
You can alter this property and build your own method if needed.path_routeclass propertyThis class property defines the path route that your view wants to define in django's
url system.
This property is a shortcut for the parameter route of django'sre_path,
using regular expressions to describe the route and the url parameters.
See also thepathproperty following, if you need greater path handling.pathclass propertyThis class property returns the actual path object that django can recognize.
You can overwrite the property and return any ofpathorre_path.Be aware, that you must include the path of the view in your urls.
An easy way to do this, is by using thecollect_pathshelper method in theutils.py.templates_dirpropertyThis property defines the directory that contains the templates files.
The default directory for every view module is theapp_name/module_name/directory.
You can also change the module-wide default directory by setting a property calledTEMPLATES_DIRinside the module.template_filepropertyYou can set the template file of the view.
By default, the path is taken by the name of the view in a snake case plus a.htmlextension (e.g.MyViewtemplate file becomesmy_view.html).template_namepropertyThis is the full path of the template.
It is recommended as a first option to alter the templates_dir or templates_file
properties, by fill free to provide the final full path if needed.
By default, this property equals totemplates_dir+template_file.project_titlecontext propertyThis is the title of your project. It's part of the context for every view, so you can
use it inside your templates as a variable.
By default, the project title is the name of the directory that contains thesettings.pyfile.titlecontext propertyThis is the title of the view.
You can set this property and read it inside your templates.
By default, this property is derived by the module variable calledTITLE.
IfTITLEis not available, the project title is the default fallback.iconcontext propertyIt is sometimes useful in the frontend to print an icon, relative to the module the
user is viewing.
You can set this property and read it inside your templates.
By default, this property is derived by the module variable calledICON.
IfICONis not available, the empty string''is the default fallback.message_info (self, message)methodmessage_success(self, message)methodmessage_warning(self, message)methodmessage_error(self, message)methodYou can use these shortcut methods to send messages in the django's messaging system.
Enable also theHtmxMessagesMiddlewareto automatically send a htmx event every time
new messages are available to display in the frontend.django_htmx_ui.views.generic.PrivateTemplateViewThis class extends thePublicTemplateViewclass and uses
the django'sLoginRequiredMixinto check if the current user
is authenticated. If not, makes a redirect to the login page.Provides the following attributes:usercontext propertyThis is a shortcut to therequest.user.
You can use this inside your templates with{{ user }}to refer to the user object
as needed.DecoratorsUsing the following decorators you can tag any method inside yourTemplateViewand benefit from the automations it applies.django_htmx_ui.utils.ContextPropertyIf you decorate any method with this magic decorator it will automatically convert it
in a property and pass it through the template engine context as a variable.For example:class MyView(PublicTemplateView):

    @ContextProperty
    def variable_name(self):
        return 'This is a test string to demonstrate the functionality.'Now you can use{{ variable_name }}inside your template filemy_view.html.django_htmx_ui.utils.ContextCachedPropertyThe same asdjango_htmx_ui.utils.ContextProperty, but the result is cached using thefunctools.cached_property. You can call many times the property
(e.g. print a variable in many places inside a template), but the method will be called
only once.CRUD Viewsdjango_htmx_ui.views.crud.CrudMixinThis is the Base Mixin for all following Crud* classes.Provides the following attributes:permissionpropertyYou can define this property to check if the current request or user has the permission
to access the view and return True or False based on your own criteria.
By default, this property will return True and grant access to the view.django_htmx_ui.views.crud.CrudCreateMixin(CrudMixin, FormMixin)Add this Mixin to yourTemplateViewclasses to add the object creation functionality.
It basically combines theCrudMixinand theFormMixinmixnins.django_htmx_ui.views.crud.CrudRetrieveMixin(CrudMixin, FormMixin)django_htmx_ui.views.crud.CrudListMixin(CrudMixin, FormMixin)CrudListMixinis an alias ofCrudRetrieveMixin.
Add this Mixin to yourTemplateViewclasses to add the object listing functionality.
It basically combines theCrudMixinmixnin, with some extra helper attributes.Provides the following attributes:filterdictionary propertyYou can define a django filter to list only the item you want.
By default, all model objects will be listed.filters_getdictionary propertyThis is an automation to help you define extra filters through the query GET request
parameters.
You can add a query parameter in the form offilter_ + field_nameand the object will
be filtered out.
You can create more complex scenarios by overwriting this behaviour.instancescontext propertyThis property will return all filtered objects and is ready to use inside your
template file, using{{ instances }}variable.
In combinesfilterdictionary andfilters_getdictionary, as described above.django_htmx_ui.views.crud.CrudUpdateMixin(InstanceMixin, CrudMixin)Add this Mixin to yourTemplateViewclasses to add the object update functionality.
It basically combines theCrudMixinand theInstanceMixinmixnins.django_htmx_ui.views.crud.CrudDisplayMixin(InstanceMixin, CrudMixin)Add this Mixin to yourTemplateViewclasses to add the object display functionality.
It basically combines theCrudMixinand theInstanceMixinmixnins.django_htmx_ui.views.crud.CrudActionMixin(InstanceMixin, CrudMixin)Add this Mixin to yourTemplateViewclasses to add the object action functionality.
It basically combines theResponseNoContentMixin,CrudMixinand theInstanceMixinmixnins.
It can be used to build custom object actions, according to your specific needs.django_htmx_ui.views.crud.CrudDeleteMixin(CrudActionMixin)Add this Mixin to yourTemplateViewclasses to add the object delete functionality.
It basically is aCrudActionMixinsubclass to implement the delete function,
which is the most common action in an object based on the CRUD modeling.If you send a http POST in this view, the object will be deleted using theself.instance.delete()django's function and a success or invalid message will be set.
You can alter these behaviours by overwritingon_post,on_post_success_messageandon_post_invalid_messageattributes.MixinsThe following Mixins are available to automate some common scenarios.
Feel free to extend them more or overwriting the attributes.django_htmx_ui.views.mixins.OriginTemplateMixinAdd this Mixin in yourTemplateViewfor the view to be accessible directly from
a browser HTTP request and via a htmx request.
You can also set thepush_urlattribute toFalse. In that case, the URL will be
replaces in the browser's bar but no history will be creared (HX-Replaceheader).
You can also set thepush_urlattribute toNone. In that case, the URL will not
be replaced in the browser's bar.django_htmx_ui.views.mixins.PartialTemplateMixinAdd this Mixin in yourTemplateViewfor the view to be only accessible via a htmx
request.
By default, if the url of view is called directly from the browser (outside htmx call),
a redirection will happen to the/route path.
You can overwrite the default redirection route path by defining theredirect_partialattribute of the view.django_htmx_ui.views.mixins.FormMixinAdd this Mixin in yourTemplateView, if the view contains a form.
You can define aFormclass inside theTemplateViewclass, which is recommended to
be a subclass of django'sFormorModelFormor any other similar kind.For example:class MyView(PrivateTemplateView):

    class Form(forms.Form):
        user = forms.CharField()
        password = forms.CharField()Or as a ModelForm:class MyView(PrivateTemplateView):

    class Form(forms.ModelForm):

        class Meta:
            model = MyModel
            fields = ['field1', 'field2']Also, when a POST http request is made,FormMixInwill automatically test the form.If the form is in a valid state, theon_postmethod will callthesave()method of the formtheon_post_success_messagemethod, so you can overwrite ittheon_post_successmethod, so you can overwrite itIf the form is not in a valid state, theon_postmethod will call:theon_post_invalid_messagemethod, so you can overwrite ittheon_post_invalidmethod, so you can overwrite itProvides the following attributes:formcontext cached propertyUse this property as a variable{{ form }}in your template file to refer at the form
instance and print the form.
You can also overwrite the property in yourTemplateViewclass, in order to change
the attributes of the instance of the form.form_instancepropertyThis attribute defines the django form's instance parameter, if using aforms.ModelForm.
By default, gets its value from the instance property, if it is available.django_htmx_ui.views.mixins.InstanceMixin(FormMixin)Add this Mixin in yourTemplateView, if the view contains a model form.
SeeFormMixinattributes, as this Mixin is a subclass.Provides the following attributes:path_routeclass propertyIt extends the path route url to include automatically the primary key (pk field) of the
model instance, in the form of'(?P<pk>\w+)/' + super().path_route.
You can overwrite this class property to describe your own.instancecontext cached propertyIt returns the model object of the instance, reading the pk field from the url path.instance_slugpropertyIt returns a unique slug for the instance.titlecontext propertyIt returns the view title, taken from the string representation of the model's instance.on_post_success_messagemethodSets a "'Instance' saved" message, when the form is successfully saved.on_post_success_messagemethodSets a "'Instance' not saved" message, when the form is not valid.django_htmx_ui.views.mixins.ResponseNoContentMixinAdd this Mixin in yourTemplateViewto returnHTTP 204 No Contentas a response.
No template rendering will happen.django_htmx_ui.views.mixins.TabsMixinIt is common sometimes to use Tabs (subpages) in your project.
Add this Mixin in yourTemplateViewto implement tabs' functionality.You can use the subclassTaband its subclassLinkto define yourtabsatttribute:@ContextCachedProperty
def tabs(self):
    return self.Tabs(
        self.Tabs.Link(
            title='First tab',
            url=self.url(FirstTabView),
            slug='first_tab',
        ),
        self.Tabs.Link(
            title='Second Tab',
            url=self.url(SecondTabView),
            slug='second_tab',
        ),
        remember=True,
    )Then you can use the{{ tabs }}variable to print your tabs.
TheTabsclass provides the following attributes to use in your template file:active: the active tabLinkobjectselected: the selected index as a number representation, stating from 0remember: boolean option, if you want the last tab clicked to be remembered
automatically and be activated the next time, using a django sessionlinks: a list of all links in theTabsobject, with the following attributes:index: the index number of the link, starting from 0 for the first linktitle: the title of the link/taburl: the url of the link/tabslug: the slug of the link/tabProvides the following attributes:slug_tabclass propertyA unique slug for the tab.tab_query_varpropertyThe query keyword used on the GET request to display the selected tab.
By default, it used theslug_tabattribute.path_routeclass propertyThepath_routeto push or replace on the location_bar.
This will only be pushed or replaced when the active link has a slug.
By default, it binds tosuper().path_route + f'(?:(?P<{cls.slug_tab}>\w+)/)?'django_htmx_ui.views.mixins.ModalMixinIt is common sometimes to use Modals in your project.
Add this Mixin in yourTemplateViewto implement a modal functionality.You can use the subclassModalto define yourmodal_*atttribute:@ContextCachedProperty
def modal_create(self):
    return self.Modal(
        url=self.url(Create).query(event=self.request.GET.get('event')),
    )In this view a{{ modal_create }}variable will be available in your template.
You can basically use the two attributes defined:url: the htmx url to load inside your modalid: a unique id for your modal, which is basicallymodal_+self.slug_globalUtilsUtils ClassesUrlclassThis class represents anUrlmodel you can use inside your views controller class or
in your templates files.
It converts to a url string automatically if you pass it through str() or use it inside
a{{ url }}brackets variable.Provides the following attributes:__init__(path, query_list) methodYou can initialize aUrlobject by providing a path string and a query_list in the
form of tuples (name, value).__call__(path=None, query_list=None) methodYou can alter the path or the query_list or both as needed if you call the object again.pathstringThe string of the url's path.queryUrl.Query objectSee below for the attributes provided.resolver_matchpropertyReturns the django'sResolverMatchobject for the path.viewpropertyReturns theTemplateViewclass, provided by theresolver_matchabove.
Usually, it will be the view that resolves to theUrlobject.Url.QueryclassThis class represents anQuerymodel you can use inside your views controller class or
in your templates files to alter the query part of theUrl. It automatically built
by theUrlobject and is ready to use as sub-object / attribute inside.Please be aware that the query consists of a list of tupled variables (name, value).
So, the same name can be represented more than once.
This is a standard behaviour of the url's query.
Although, if you provide a kwargs directory (name, value) this name will become unique.Provides the following attributes:reset(*args, **kwargs) methodBuilds from scratch the query_list based on args list tuples (name, value) and
the kwargs dictionary.set(query_list) methodReplaces the query_list with the list provided in the method parameter.__call__(*args, **kwargs) methodadd(*args, **kwargs) methodAppends allargstuples (name, value) in the query_list.Updates allkwargsnamed values. If name exists, it removes first all instances.remove(name)Removes any instances ofnamein the query_list.update(**kwargs)Updates allkwargsnamed values. If name exists, it removes first all instances.UrlViewclassThis is basically a wrapper of theUrlclass mentioned above, to represent a view.
It passes through the attributespathandqueryto theUrlsub-object.Provides the following attributes:__init__(view, *args, **kwargs) method__call__(view=None, *args, **kwargs) methodDefines the view that will represent theUrlobject.Theviewparameter can be in the form of:named view, likeapp_name:module_a:my_viewpython path of the class childTemplateView, likeapp_name.views.module_a.MyViewany class child ofTemplateViewany instance ofTemplateViewThe*argsand**kwargsparameters, represents the url path parameters (if any).
These parameters are described in thepathorre_pathfunctions.update(*args, **kwargs) methodUpdates the url path parameters.pathpropertyPassed to theUrl.pathsub-object, see details above.querypropertyPassed to theUrl.Querysub-object, see details above.LocationclassThis class is a subclass ofUrlclass, representing a location object that can be
read by any path string in the request process, like the browser's location bar,
or the location derived from the request itself.__init__(path, query_list, push=False) method__call__(path=None, query_list=None, push=False) methodInitializes or updates theUrlobject (see details above for the parameterspathandquery_list).
Thepushparameter is a flag to be used optionally to remember / set if the engine
will push this location back to the browser's url location bar.resolver_matchpropertyThis returns a resolver django's object, representing the url view resolved path.create_from_urlclass methodThis class method creates aLocationobject from a url string.Utils Methodscollect_paths(module, app_name) methodUse this method to collect the paths of theTemplateViewclassed inside a module.
The app_name is the name of the application the path belongs to.
This method will automatically add:an application namespace (e.g.app_name)followed by a module namespace, derived by:module'sSLUGattribute, if applicable (e.g.module_a.SLUG), and if notthe name of the module (e.g. module_a.py has a name ofmodule_a)followed by theslugattribute of theTemplateView(e.g.MyView.slug)So the final full-name django's internal view name will beapp_name:module_a:my_viewYou can anytime refer to this view name and use all django standard calls.
For example, use thereversemethod like this:reverse('app_name:module_a:my_view').For more info, how to use the collect_paths inside the django'surls.pyfile,
see theDefining viewssection above.to_snake_case(name) methodConverts a string (name) fromCamelCasetosnake_case.to_camel_case(name) methodConverts a string (name) fromsnake_casetoCamelCase.