We have the following models:



This app now requires a Contact app (see get_contact_model) so we can exclude (soft) deleted contacts.


Does a user have access to an app e.g. the MyApps.CRM app:

from apps.models import App
from example_apps.models import MyApps

App.objects.has_access(user, MyApps.CRM)


Created 11/06/2023, There may be a better way to do this! For the tests, see example_apps/tests/



The App model contains the properties of the apps in a project e.g. name, icon


The user_apps method is used to display a list of apps for a user. e.g. AppListView


Useful methods include:

  • admin_apps: List of apps where the user is an administrator.

  • admin_app_groups: List of apps where the user is a group administrator.

  • app_menu: Can be used to control the main menu in the project

    • current_app_admin can be used to enable menu options for a user who is an administrator for the current app.

    • show_app_menu will tell you if the user has access to just one app i.e. no need to show the app menu unless they have access to several apps.

  • app_perms: Check the source code for documentation.

  • create_app: Create a new app…

  • is_administrator: Is the user an administrator for this app?

  • is_administrator_for_any_app: Is the user an administrator for any app?

  • user_apps: Apps available to this user.

  • user_dash_url: Dashboard URL for this user (if there is a single one).

This model includes a slug, name, menu caption and icon for the app.

  • administrators (many to many): Users who can administer the app.

  • app_group (many to many): Users belonging to these groups (AppGroup) have permission to access the app.

  • global_group (many to many): Users belonging to these groups (Django Group) have permission to access the app.

  • hide_app_groups (boolean): Does the app use app_group (or not)?

  • url_contact_edit and url_dash are URLs for the dashboard and contact edit pages for the app.


  • app_superusers (many to many): Users who can set the administrator for an app.

  • contact_administrators (many to many): Users who can create / update / delete users (or contacts).

AppGroup and AppGroupUser

List of groups for an application. These are different to the Django Group.


You can add the following to your base.html template:

{% block menu %}
  {% if user.is_authenticated %}
    {% if app_menu and app_menu.show_app_menu %}
      <li class="pure-menu-item{% if 'apps' in path %} pure-menu-selected{% endif %}">
        <a href="{% url 'apps.list' %}" class="pure-menu-link">
          <i class="fa fa-bullseye fa-fw"></i>
    {% endif %}
    {% if app_menu and app_menu.current_app %}
      <li class="pure-menu-item{% if app_menu.current_app.url_dash == request.build_absolute_uri %} pure-menu-selected{% endif %}">
        <a href="{{ app_menu.current_app.url_dash }}" class="pure-menu-link">
          <i class="fa fa-dashboard fa-fw"></i>
    {% endif %}
    {% if app_menu and app_menu.show_settings %}
      <li class="pure-menu-item{% if 'settings' in path %} pure-menu-selected{% endif %}">
        <a href="{% url 'project.settings' %}" class="pure-menu-link">
          <i class="fa fa-cog fa-fw"></i>
    {% endif %}
  {% endif %}
{% endblock menu %}


To add details of the app_menu to the context of BaseMixin, we use the BASE_MIXIN_CONTEXT_PLUGIN system:

Add the following to settings/

# See 'base/'
BASE_MIXIN_CONTEXT_PLUGIN = ["apps.plugin.AppBaseMixin"]

Set the current_app_slug if your template is for a particular app e.g:

class DashView(LoginRequiredMixin, BaseMixin, TemplateView):
    current_app_slug = MyApps.REPORT
    template_name = "example/dash.html"


The Settings view needs access to various permissions:

from braces.views import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import DetailView

from apps.views import is_contact_or_app_admin, SettingsBaseMixin

class SettingsView(
    LoginRequiredMixin, UserPassesTestMixin, SettingsBaseMixin, TemplateView
    template_name = "example/settings.html"

    def test_func(self, user):
        return is_contact_or_app_admin(user)

The permissions returned by SettingsBaseMixin will work with the following:

{% block content %}
  <div class="pure-g">
    {% include 'apps/_settings_apps.html' %}
    {% include 'apps/_settings_groups.html' %}
{% endblock content %}