Skip to main content

splent_feature_profile

The profile feature of a SPLENT product line.

Every account has a profile with a name and a surname. The person edits it at /profile/edit, and a client reads it as JSON at /api/v1/profile/me. Other features extend all three without touching profile's code. They add fields to the edit form, sections below it and keys to the JSON. It needs auth, which owns the account. With signup in the product, the sign-up asks for the name and surname as well.

A SPLENT feature is a Python package that a product composes with others. Its models, routes, services, templates and hooks are registered when the product starts. What this feature provides and requires is listed in the [tool.splent.contract] section of pyproject.toml, which splent feature:contract --write keeps in sync with the source.

A profile for every account

auth sends user-registered when an account is opened through AuthenticationService.create_user, as the sign-up and the external sign-in features do, and profile creates its UserProfile then. The name and surname come with the signal when the sign-up asked for them, and the profile is empty otherwise.

An account opened without that signal, from auth's accounts screen, with its create-user command or by a seeder, has no profile at first. It gets an empty one the first time it is needed, when the person opens the edit page or a client asks /api/v1/profile/me, and the person fills it in on the edit page. An account that already has a profile keeps it.

Name and surname at sign-up

When the product has signup, profile registers two fields on its form with register_signup_field, name at order 10 and surname at order 20, so both are asked for before the address. Both are required. signup is optional, and without it nothing is registered. A signup older than register_signup_field (0.2.0 and before) asks for no name, and the product starts all the same.

Fields other features add

A feature that keeps more about the person asks for it on the edit page with register_profile_field. The field is drawn on the page, validated with the form, filled from the profile attribute of the same name and saved onto it when UserProfile has a column of that name. The feature that registers the field adds the column, with a declared model extension of UserProfile and a migration of its own.

# __init__.py of a feature that extends UserProfile with an affiliation
from flask_babel import lazy_gettext as _l
from wtforms import StringField
from wtforms.validators import Length, Optional

from splent_io.splent_feature_profile.forms import register_profile_field


def init_feature(app):
    register_profile_field(
        "affiliation",
        StringField(_l("Affiliation"), validators=[Optional(), Length(max=255)]),
    )

order places the field. Lower comes first.

Field Order
A registered field that says so its own
name 100 (NAME_ORDER)
surname 200 (SURNAME_ORDER)
A registered field that does not say 300 (DEFAULT_FIELD_ORDER)

The registry is read when a request needs the form, so a field registered after profile has loaded is asked for too. The form class is built once for each set of registered fields and reused while that set stays the same. Registering a name again replaces the earlier field, and remove_profile_field(name) stops asking for one. A field's own render_kw (a placeholder) and description (a line of help) are shown. A field without a column is validated and drawn, and profile does not store it.

Some names are refused with a ValueError when the field is registered. id, user_id and user say whose profile it is, which is never the person's to edit. The form's own fields (name, surname, submit, csrf_token) and the form's attributes (data, errors, meta...) cannot be taken either.

Sections below the form

The edit page opens the slot profile.edit.sections, right below the profile form. Linked accounts, notification preferences and the like go there. Each section is a block of its own, outside the profile form, so it can carry forms of its own. A callback takes no arguments and returns HTML, or an empty string to add nothing on this request. When no callback returns anything, nothing is drawn.

# hooks.py of a feature that links external accounts
from flask import render_template
from splent_framework.hooks.template_hooks import register_template_hook


def linked_accounts():
    return render_template("hooks/linked_accounts.html")


register_template_hook("profile.edit.sections", linked_accounts, order=10)

The profile as JSON

GET /api/v1/profile/me answers the signed-in person's profile.

{"name": "Ada", "surname": "Lovelace", "email": "ada@example.com"}

Nobody signed in gets 401, as JSON with an error key, so a page that calls it with fetch never receives the login page instead. An account without a profile gets an empty one, and answers with empty name and surname. Every answer carries Cache-Control: no-store, so no browser or proxy keeps one person's data for the next.

A feature adds its own keys with register_profile_payload. The function takes the user and their profile and returns a dict, and runs on every request.

from splent_io.splent_feature_profile.payload import register_profile_payload


def affiliation(user, profile):
    return {"affiliation": profile.affiliation}


def init_feature(app):
    register_profile_payload(affiliation)

The functions run in the order they were registered, so a later one wins a key two of them send. name, surname and email always hold the profile's and the account's own values. Registering a function again does nothing, and remove_profile_payload(fn) takes one out.

Menus and the account menu

The feature declares Edit profile in the framework's menu registry, first in the Account section, and an application shell that draws menu_sections() and user_menu_sections() shows it to every signed-in account.

Key Menu Section Order Opens
profile Sidebar account 10 profile.edit_profile
user_profile User menu account 10 profile.edit_profile

A product hides or reorders them by key in [tool.splent.layout.menu]. The layout.sidebar.authenticated.items and layout.navbar.authenticated hooks still draw the same entry and the name for one minor, for the shells that draw those slots. A shell draws the registry or those slots, never both. The next minor drops the hooks.

A shell that opens the layout.navbar.user slot at the top of its account menu, inside the .dropdown-menu and before the entries of user_menu_sections(), gets who is signed in from this feature: the initials, the name ("Surname, Name") and the address, so the shell draws the account's entries without knowing anything of profiles. A visitor gets nothing. The block is only that: the button that opens the menu and the divider under the block are the shell's, and it draws the divider when the slot renders something.

{% set who = render_template_hooks("layout.navbar.user") %}
{% if who %}{{ who }}<div class="dropdown-divider"></div>{% endif %}

The edit page extends shell('app'), so it follows SPLENT_SHELL_APP.

Translations

The edit page, its form and the sign-up fields ship with a Spanish catalog.

Use it in a product

splent feature:add splent_feature_profile

Develop

splent feature:test splent_feature_profile
splent feature:contract splent_feature_profile

Release

splent feature:release splent_feature_profile --dry-run --bump patch
splent feature:release splent_feature_profile

The first command rehearses the release and changes nothing. The second publishes it to the git forge and to PyPI, and a PyPI version is permanent.

Metadata

Release files for splent-feature-profile 1.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for splent-feature-profile 1.7.0
File Size Uploaded Provenance Actions
splent_feature_profile-1.7.0.tar.gz 32.1 kB Without provenance Details for splent_feature_profile-1.7.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for splent-feature-profile 1.7.0
File Interpreter ABI Platform Actions
splent_feature_profile-1.7.0-py3-none-any.whl Python 3 none any Details for splent_feature_profile-1.7.0-py3-none-any.whl

Total release size: 71.0 kB

Release files / splent_feature_profile-1.7.0.tar.gz

Download URL splent_feature_profile-1.7.0.tar.gz
Size 32.1 kB
Tags Source
SHA-256 checksum
How to use checksums
cc96e816dd01c6849c4e3c30355f78a832f814d86677191dea9a9678715459f3
BLAKE2b-256 checksum
How to use checksums
9458051c0ebc37b3e50af95886cbc7706c51465162f34b8b815dae614eb1bf52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.16

Release files / splent_feature_profile-1.7.0-py3-none-any.whl

Download URL splent_feature_profile-1.7.0-py3-none-any.whl
Size 38.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bb0ac40324a770f31ae64659c9b11f48ac421661e635a4a4bcbc7fedeaed306b
BLAKE2b-256 checksum
How to use checksums
53b75a35e73385bbf5c2309fb1eba46e4f11c23f70c6188711c2e90fe5c1d214
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.16

Security for splent-feature-profile 1.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page