ox.core

This module regroup all the bases for building and setup Oxylus applications.

ox.core.apps

Oxylus application inherits from AppConfig. It allows to:

  • handle assets;

  • provide application metadata;

  • application level permission;

We plan to use those metadata and dependencies in order to automate loading and ease application installation by end-users.

class ox.core.apps.AppConfig(*args, **kwargs)[source]

Bases: AppConfig

Base AppConfig application to use for Oxylus applications.

It provides extra features as:

  • assets managements (Assets);

  • custom root url for the whole application (see ox.urls)

  • default icon (as MDI specifier)

It is planned later to handle app dependencies.

assets: Assets = <ox.core.assets.base.Assets object>

The assets use by the application. It will be used at two places:

  • building and managing assets, through ./manage.py assets;

  • rendering scripts and stylesheets includes into templates

Note: there is no need to provide an extra Asset specifying the application to be compiled, since it is what the Assets class does.

dependencies: list[Type[AppConfig]] | None = None

List of required dependencies for this application.

icon: str = 'mdi-home'

Material design icon class.

npm_package: str | None = None

Name of the corresponding NPM package to look up for.

Defaults to app label.

root_url: str = ''

Provide an alternative to app label when we target application in paths.

For example Oxylus will nest template directories as ox/core/ instead of ox_core. The same happens for urls.

class ox.core.apps.AppMeta[source]

Bases: Owned

dependencies: tuple[str] = ()

Application dependencies, as a tuple of:

  • AppConfig_name: related app config

  • TODO: (AppConfig_name, “version”): where `version is matched agains’t “^” operator.

description: str = ''

Describe application here.

Default will be fetched from metadatas.

extra_metadata: dict[str, str] | None = None

Application metadata.

If not provided, fetch those information based on root module of the package (using importlib.metadatay).

property package_metadata[source]

Return metadata read from package.

permission: str = ''

Permission to access application.

ox.core.assets

This module provides assets management and integration into django apps.

Each Asset represent an npm package to use as static. They are regrouped under the Assets class that represent the current django application’s frontend requirements.

It provides the following features:

  • attach Assets to an AppConfig using ox.utils.functional.Owned;

  • Provide a list of assets to include in the application template;

  • Provide an import map object that will map the provided dependencies to the corresponding files;

  • It allows to integrate theses dependencies as static using the AssetsFinder.

By technical convention, Oxylus only handles building Vue based Vite.js projects. However it allows to run other frameworks.

class ox.core.assets.Asset(name, js='', css='', dev_js='', static_dir='', dist='dist')[source]

Bases: Owned

A single Asset’s dependency.

It target a specific static module and can provide:
  • javascript and development javascript distribution file

  • css directory

css: str

Include this javascript file.

dist: str

Distribution path. The file will be looked up there.

js: str

Static directory name, defaults to name.

name: str

Asset’s package/module name, used as is for generated import map and in order to find packages in node_modules.

static_dir: str

Include this css file.

class ox.core.assets.Assets(name='', path=None, includes=None, dependencies=None, base_dir=None, owner=None)[source]

Bases: Owned

This class represent a package for a Django application.

It is responsible to:

  • provide a list of CSS, JS to include;

  • generate the import map (in ox/core/base.html);

  • provide list of directories and dependencies to include in statics;

  • provide list of exported files to include into the rendered templates;

A package can be related to a Django application or not:

  • related to a Django app: in this case it is expected that the package resides in app_dir/assets (see ox.utils.functional.Owned, and contribute());

  • not related to a Django app: in this case, a path to the package is provided;

contribute(owner)[source]

TODO

property css_urls: set[str][source]

A list of CSS static urls.

dependencies: list[Asset] | None = None

Dependencies.

get_dependencies_urls(attr)[source]

Return urls of dependencies, based on attr attribute value.

Return type:

Generator[tuple[str, str], None, None]

get_dependency_url(asset, attr)[source]

Return url of an

Return type:

tuple[str, str] | None

get_includes_urls(attr)[source]

Return urls of includes, based on attr attribute value.

Return type:

Generator[tuple[str, str], None, None]

get_locations()[source]

Get locations of npm packages.

Return a generator that yield tuples of (prefix, path_to_dist). It doesn’t yield values from inner Assets instances.

Return type:

Generator[tuple[str, Path], None, None]

get_urls(attr)[source]

Iter over assets and yield tuples (asset.name, attribute).

Return type:

Generator[tuple[str, str], None, None]

property import_map: dict[str, str][source]

A list of CSS static urls.

includes: list[Asset] | None = None

Exported assets.

property js_urls: set[str][source]

A list of JS static urls.

name: str = ''

Package name

property package_path: Path[source]

Get the actual path to package directory.

Raises:

RuntimeError – assets is related to an app and the assets directory does not exists or when not related to a path and no path is provided.

path: Path | None = None

Path of the package (or workspace’s packages dir).

The actual package path is retrieved using package_path.

ox.core.assets.ox_assets = <ox.core.assets.base.Assets object>

Common assets for all applications, as it provides the @oxylus/ox package.

ox.core.conf

class ox.core.conf.Settings(key, source=<LazySettings "instance.settings">)[source]

Bases: Settings

Main settings for Oxylus.

HTTP_SERVER_BACKEND: Optional[Literal['nginx', 'apache']] = None

Running HTTP server (defaults to nginx on production).

This settings also defines supported backends for different applications.

PROTECTED_MEDIA_DIR = 'protected/'

Directory in media used for protected data.

PROTECTED_MEDIA_URL = '/protected/'

Configured URL for protected media. MUST end with a /.

property protected_media_dir: Path

Return full path to upload dir.

protected_media_url(path)[source]

Return url for the protected media of provided path.

Parameters:

path – path related to MEDIA_ROOT

Return type:

str

ox.core.conf.ox_settings: Settings = <ox.core.conf.Settings object>

Main settings used by Oxylus, under key OX.

ox.core.exceptions

ox.core.exceptions.exception_handler(exc, context)[source]

Handle django exceptions…

ox.core.models

class ox.core.models.InheritanceQuerySet(*args, **kwargs)[source]

Bases: InheritanceQuerySet, QuerySet

This is utility QuerySet subclass for Model, inheriting from model_utils.InheritanceQuerySet.

class ox.core.models.Model(*args, **kwargs)[source]

Bases: Model

Model class used by Oxylus applications. It provides:

  • public uuid: reducing bruteforcing database row index;

  • url reverse;

Using uuid as a public identifier is preferred over directly exposing database primary key.

get_absolute_url()[source]

Return model detail url.

get_api_url(action='detail')[source]

Return model api url.

Return type:

str

get_list_url()[source]

Return model list url.

Return type:

str

classmethod reverse_url(action, namespace='', **kwargs)[source]

Reverse an url for the provided action.

Return type:

str

Parameters:
  • action (str) – name of the action (eg. detail, update);

  • namespace (str) – if provided insert namespace after application namespace;

  • **kwargs

    passed down to reverse

:return reversed url as string.

class ox.core.models.QuerySet(model=None, query=None, using=None, hints=None)[source]

Bases: QuerySet

uuid(value)[source]

Filter by uuid.

ox.core.pagination

class ox.core.pagination.PageNumberPagination[source]

Bases: PageNumberPagination

This is pagination used by default for Oxylus API views.

It just adds page_size GET parameters to urls.

ox.core.panels

This module provide application’s panels description.

This is used to generate main navigation menu and application view’s panels.

Panels are logically organised using the following structure:

  • A Panel inside a group of Panels;

  • A Panels can be nested (once) in another one (eg. “Settings” regroup multiple settings applications);

Panels are defined in applications’ module panels.py in order to separate concerns with the views. However they are not discovered automatically, but by importing the module into the view (in order to assign panels).

Panel and panels are registered through the global object registry.

Application template

An AppView have panels assigned to a Panels instance. This is used to generate components inside the template using provided Panel.component (aka Vue component), and Panel.template, actions_template (used for extensibility).

Example

By convention the navigation items are registered inside panels.py module, such as:

from ox.core.panels import registry, Panel, Panels

panels = Panels("contacts", _("Contacts"), items=[
        Panel("persons", _("Persons"), "mdi-card-account-mail",
            url="ox_contacts:index",
            order=0,
            permission="ox_contacts.view_person",
        ),
        # ...
        Panels("settings", _("Settings"),
            order=100,
            items=[
                Panel("organisationtypes", _("Organisation Types"), "mdi-domain-switch",
                    url="ox_contacts:index",
                    permission="ox_contacts.view_organisationtype",
                )
            ],
        ),
    ])
)
registry.append(panels)

# use this to append to an already registered group:
# registry["settings"].append(panels)
class ox.core.panels.Panel(name, title, icon='', component='', **kwargs)[source]

Bases: BasePanel

Describe a panel component and its navigation.

component: str = ''

Vue component.

get_panels()[source]

For interface purpose, return iterator over self.

serialize(**kwargs)[source]

Return navigation data.

template = 'ox/core/components/model_panel.html'

Django template file used to render the panel

type: str = 'item'

Menu item type: group, subheader, item.

Use by frontend OxNavItem.

url: str = None

Url name to app view. It MUST always be namespaced under app’s name.

class ox.core.panels.Panels(name, title, items=None, **kwargs)[source]

Bases: PanelsMixin, BasePanel

Regroup multiple panels, usually of an application.

This also can be used in a more UX sense of the term, such as “Settings” would regroup different nested application Panels.

serialize(**kwargs)[source]

Return navigation data.

type: str = 'group'

Menu item type: group, subheader, item.

Use by frontend OxNavItem.

class ox.core.panels.Registry(items=None)[source]

Bases: PanelsMixin

Register all applications’ panels.

The following registry methods reset the cached property nav_data(): append(), __getitem__(), __setitem__(). Those are the public methods used to update the registry, whilst nav_data is used to provide navigation data to the user and is cached for performance.

append(item)[source]

Add new item to group.

Parameters:
  • item – item

  • path – dot separated path to parent group

Returns:

the appended item

property nav_data: list[dict[str, Any]][source]

Menu data as provided to frontend application.

ox.core.panels.registry = <ox.core.panels.Registry object>

Registry of all applications’ panels.

ox.core.renderers

class ox.core.renderers.AdminBrowsableAPIRenderer[source]

Bases: BrowsableAPIRenderer

This class is used for browsable API views in order to deny access to non staff users.

get_context(*args, **kwargs)[source]

Returns the context used to render.

ox.core.serializers

class ox.core.serializers.ModelSerializer(*args, **kwargs)[source]

Bases: ModelSerializer

This ModelSerializer provides id field defaulted to model’s uuid.

class ox.core.serializers.NestedInfo(field, delete=True)[source]

Bases: object

Informations about a field nested in ModelSerializer

delete: bool = True

Delete related items not present in the user’s list.

field: str

Serializer field.

class ox.core.serializers.NestedSerializer(*args, **kwargs)[source]

Bases: ModelSerializer

This serializer class allows to specify and save relations using nested serializer values.

Default behavior implies that existing values will be updated, new ones created and removed one deleted. You can customize by provided argument for the NestedInfo specific to a field.

The attribute Meta.nested is used to specify which fields are actually related models. Its value is a list of:

  • a string that is NestedInfo.field (serializer field name);

  • tuple with positional argument, dict with positional arguments;

  • a NestedInfo instance;

Limitations: - It only works with models having a uuid field used as reference. - It assumes a FK reverse relations.

Example: two models, A and B. B has ForeignKey to A, and on A’s serializer you want to update B objects related to A. You don’t want uuid as handled by RelatedField).

class BSerializer(ModelSerializer):
    # ...

class ASerializer(ModelSerializer):
    b_items = BSerializer(source="b_set", many=True, required=False)

    class Meta:
        # Declaring fields as nested allows them to be automatically
        # create/updated.
        nested = ("b_items",)
        # ...
        #
        # If you dont want deletion of missing items, you can use this:
        # nested = (("b_items", False),)

At the serializer class creation, Meta.nested is transformed into a dict of NestedInfo by serializer field name.

create(validated_data)[source]

We have a bit of extra checking around this in order to provide descriptive messages when something goes wrong, but this method is essentially just:

return ExampleModel.objects.create(**validated_data)

If there are many to many fields present on the instance then they cannot be set until the model is instantiated, in which case the implementation is like so:

example_relationship = validated_data.pop(‘example_relationship’) instance = ExampleModel.objects.create(**validated_data) instance.example_relationship = example_relationship return instance

The default implementation also does not handle nested relationships. If you want to support writable nested relationships you’ll need to write an explicit .create() method.

class ox.core.serializers.RelatedField(*args, **kwargs)[source]

Bases: SlugRelatedField

Provide related field based on uuid.

class ox.core.serializers.RelatedObjectField(*args, **kwargs)[source]

Bases: RelatedField

A DRF field that: - On input (deserialization): takes a UUID and returns the related instance. - On output (serialization): returns the serialized object as a dict.

to_internal_value(data)[source]

Convert UUID to model instance.

to_representation(value)[source]

Return the full serialized object.

ox.core.views

class ox.core.views.AppMixin[source]

Bases: ContextMixin

Base mixin for applications.

app_config_name: str | None = None

AppConfig name of the related application.

If none provided, retrieve it based of request’s resolver match.

assets: Assets = None

Use theses assets instead of app config’s one.

default_panel: str = ''

Default panel to display.

get_app_config()[source]

Return application config.

Set to request resolved match application name by default.

get_app_data(**kwargs)[source]

Return application data to pass down to js application.

get_app_nav()[source]

Return application navigation menu.

Return type:

dict[str, Any]

get_assets()[source]

Return assets to use with the view.

It retuns assigned assets or app config one if any.

Return type:

Assets | None

panels: Panels = None

Application’s panels descriptors.

title: str = ''

Application title (as displayed in <title> and top bar).

class ox.core.views.AppView(**kwargs)[source]

Bases: UserAuthMixin, AppMixin, TemplateView

Base view used for ox based applications.

get_template_names()[source]

By default return a list with:

  • template_name value

  • {self.app_config.root_url}/app.html, if app_config is found.

Return type:

list[str]

class ox.core.views.UserAppView(**kwargs)[source]

Bases: LoginRequiredMixin, AppView

Application view requiring user to be authentified.

class ox.core.views.UserAuthMixin[source]

Bases: object

Provide request’s user in Application’s initial data, as user.

group_ser_class

alias of GroupSerializer

user_ser_class

alias of UserSerializer