app

A Flask webapp.

Application factory.

From the Flask docs:

Instead of creating a Flask instance globally, we will create it inside a function. This function is known as the application factory. Any configuration, registration, and other set up the application needs will happen inside the function, then the application will be returned.

The app will be passed through to each init_app declaration, unique to its respective module. These functions will configure the app designed in this package.

Registers:

  • This app’s commandline interface

  • Config object from config module

  • Exceptions for handling error pages

  • This app’s registered Flask extensions

  • Loggers from logging

  • Blueprints containing routing logic

  • This app’s interactive shell

app.create_app() Flask[source]

Create Flask web application object.

Accessible through wsgi.py.

Returns:

Web application initialized and processed through factory pattern.

app.cli

Application’s custom commands.

Includes commands defined in this app only. The application will have additional commands that come preloaded with Flask such as flask run and flask shell. It is likely this application will have even more commands than just these such as those defined in third-party extensions e.g. flask db from flask_migrate and flask digest from flask_static_digest.

For all available commands run flask --help.

app.cli.init_app(app: Flask) None[source]

Initialize commands belonging to this module.

Parameters:
app: Flask

Application object.

app.cli.create

Create and add assets to database.

app.cli.lexers

Commands related to language syntax.

app.cli.translate

Run translation actions.

app.config

Most configuration is set via environment variables as per https://12factor.net/config:

class app.config.Config(root_path: Path)[source]

Bases: Env

The application’s configuration object.

List objects are comma separated values.

LIST=comma,separated,values

Dict objects are comma separated variable assignments.

DICT=comma=0,separated=1,values=2
Parameters:
root_path: Path

The application’s filesystem, starting at the root.

property ADMINS : list[str]

List of web admins.

Default value is [].

property ADMIN_SECRET : str

Password for initialized admin user.

Default value is None.

property BCRYPT_LOG_ROUNDS : int

Determine the complexity of bcrypt encryption.

see bcrypt for more details.

Default value is 13.

property BOOTSTRAP_SERVE_LOCAL : bool

Bootstrap resources will be served from the local app.

See CDN support for details.

Default value is True.

property BRAND : str

App branding to display on navbar and browser tabs.

Default value is "".

property COPYRIGHT : str

Return the copyright line from LICENSE else None.

property COPYRIGHT_AUTHOR : str

Copyright holder of the application.

By default this will be parsed from the LICENSE of this repository.

property COPYRIGHT_EMAIL : str

Copyright holder’s email.

By default this will be parsed from the pyproject.toml of this repository.

property COPYRIGHT_YEAR : str

Year the copyright is valid for.

By default this will be parsed from the LICENSE of this repository.

property CSP : dict[str, str | list[str]]

Policies to add to existing Content Security Policy.

Default value is {}.

property CSP_REPORT_ONLY : bool

Do not enforce CSP and only report violations.

Default value is True.

property DATABASE_URL : str

Database URL.

Default value is None.

property DEBUG : bool

Run the application in debug mode.

Default value is False.

property DEBUG_TB_ENABLED : bool

When DEBUG is True enable TB (toolbar).

property DEBUG_TB_INTERCEPT_REDIRECTS : bool

Debug-toolbar to intercept redirects.

Default value is False.

property DEFAULT_MAIL_SENDER : str | None

Default mail sender.

Default value is None.

property FLASK_STATIC_DIGEST_BLACKLIST_FILTER : list[str]

Do not md5 tag added extensions.

eg: [“.htm”, “.html”, “.txt”]. Make sure to include the “.”.

Default value is [].

property FLASK_STATIC_DIGEST_HOST_URL : str | None

Set to a value such as https://cdn.example.com.

Prefix your static path with this URL. This would be useful if you host your files from a CDN. Make sure to include the protocol (aka. https://).

Default value is None.

property LANGUAGES : list[str]

Supported languages.

Default value is ["en"].

property MAIL_PASSWORD : str | None

Password for mail server.

Default value is None.

property MAIL_PORT : int

Mail port to send mail from.

Default value is 25.

property MAIL_SERVER : str

Mail server to send mail from.

Default value is None.

property MAIL_SUBJECT_PREFIX : str

Prefix for mail subject.

Default value is "".

property MAIL_USERNAME : str | None

Username for mail server.

Default value is None.

property MAIL_USE_SSL : bool

Use SSL for emails.

Default value is False.

property MAIL_USE_TLS : bool

Use TLS for emails.

Default value is False.

property MAX_CONTENT_LENGTH : int

Max content length for file uploads.

Default value is 1048576.

property NAVBAR_HOME : bool

Include Home in navbar as opposed to only the brand.

Default value is True.

property NAVBAR_ICONS : bool

Display certain links in navbar as icons instead of text.

Default value is False.

property NAVBAR_USER_DROPDOWN : bool

Display logged-in user links as dropdown.

Default value is False.

property PAYMENT_OPTIONS : dict[str, str]

Key-value pairs of payment options for Stripe’s API.

Price is in US cents.

Default value is {"price": "None"}.

property POSTS_PER_PAGE : int

Posts to paginate per page.

Default value is 25.

property PREFERRED_URL_SCHEME : str

Whether HTTP or HTTPS is preferred.

Default value is https.

Set remember-cookie secure parameter.

The cookie for the Remember Me checkbox in logins.

Default value is True.

property RESERVED_USERNAMES : list[str]

List of names that cannot be registered.

Default value is [].

property SCHEMAS : Path

Path to schemas.

Default value is app.root_path / "schemas".

property SECRET_KEY : str | None

Key for security related needs.

Used for securely signing the session cookie and can be used for any other security related needs by extensions or the application. It should be a long random bytes or str. For example:

$ python -c 'import secrets; print(secrets.token_hex())'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d547...'

Default value is None.

property SECURITY_PASSWORD_SALT : str | None

Specifies the HMAC salt.

This is only used if the password hash type is set to something other than plain text.

Default value is SECRET_KEY.

property SEND_FILE_MAX_AGE_DEFAULT : int

Timedelta used as cache_timeout for send_file funcs.

This configuration variable can also be set with an integer value used as seconds.

Default value is 31556926.

Don’t allow JavaScript access to marked cookies.

Default value is True.

Restrict how cookies are sent with external requests.

Default value is "strict".

Only send cookies with requests over HTTPS.

If the cookie is marked “secure” the application must be served over HTTPS for this to make sense.

Default value is True.

property SHOW_PAYMENT : bool

Show payment option.

Default value is False.

property SHOW_POSTS : bool

Show posts from database.

Default value is True.

property SHOW_REGISTER : bool

Show Register option in navbar.

Default value is True.

property SIGNATURE : str

Signature to sign emails off with.

Default value is BRAND.

property SITEMAP_CHANGEFREQ : str

Frequency of change.

property SITEMAP_INCLUDE_RULES_WITHOUT_PARAMS : bool

Generate list of rules without params.

Default value is True.

property SITEMAP_URL_SCHEME : str

URL scheme for sitemap.

property SQLALCHEMY_DATABASE_URI : str

The database URI that should be used for the connection.

Default value is "sqlite:///:memory:".

property SQLALCHEMY_TRACK_MODIFICATIONS : bool

Track modifications of objects and emit signals.

This requires extra memory and should be disabled if not needed.

Default value is False.

property STATIC_FOLDER : Path

Path to static files.

Default value is app.root_path / "static".

property STRIPE_SECRET_KEY : str

Secret key for Stripe API.

Default value is None.

property TESTING : bool

Testing mode.

Default value is False.

property TITLE : str

Header for page.

Default value is "".

property TRANSLATIONS_DIR : Path

Path to translation files.

Default value is app.root_path / "translations".

property UPLOAD_EXTENSIONS : list[str]

Extensions allowed for upload.

Default value is [".txt", ".pdf", ".png", ".jpg", ".jpeg", ".gif"].

property UPLOAD_PATH : Path

Path to upload file to.

Default value is "${STATIC_FOLDER}/uploads".

property WTF_CSRF_ENABLED : bool

Cross-Site Request Forgery: Switch off for testing.

Default value is TESTING.

property WTF_CSRF_SECRET_KEY : str

Secret key for signing CSRF tokens.

Default value is SECRET_KEY.

app.config.init_app(app: Flask) None[source]

Initialize config for this application.

Parameters:
app: Flask

Application object.

app.exceptions

Register handling of error-codes and their corresponding pages.

app.exceptions.init_app(app: Flask) None[source]

Initialize error pages for this application.

See exceptions.

Parameters:
app: Flask

Application object.

app.extensions

app.extensions.init_app(app: Flask) None[source]

Initialize extensions.

Parameters:
app: Flask

Application object.

app.extensions.babel

class app.extensions.babel.Babel(app=None, default_locale='en', default_timezone='UTC', default_domain='messages', date_formats=None, configure_jinja=True)[source]

Bases: Babel

Subclass flask_babel.Babel.

With this the Babel.init_app can be tweaked so that everything remains encapsulated in the application factory, with no need to add additional functions to the application..

init_app(app: Flask) None[source]

Set up this instance for use with app, if no app was passed to the constructor.

app.extensions.talisman

class app.extensions.talisman.ContentSecurityPolicy(schemas: Path)[source]

Bases: Dict[str, str | List[str]]

Object for setting default CSP and config override.

Parameters:
schemas: Path

Path to schemas directory.

update_policy(update: ContentSecurityPolicy) None[source]

Combine a configured policy without overriding the existing.

If the result is a single item, add a str.

If the result is a list, remove duplicates and sort.

If either is a str and the other is a list, add the str and the list contents to the new list.

If both are str values add to the new list.

Parameters:
update: ContentSecurityPolicy

A str or a list of str objects.

class app.extensions.talisman.Talisman(app=None, **kwargs)[source]

Bases: Talisman

Subclass flask_talisman.Talisman.

With this the Talisman.init_app can be tweaked so that everything remains encapsulated in the application factory, with no need to add additional functions to the application..

init_app(app: Flask, feature_policy: str | dict[str, str] = {}, permissions_policy: str | dict[str, str] = {'browsing-topics': '()'}, document_policy: str | dict[str, str] = {}, force_https: bool = True, force_https_permanent: bool = False, force_file_save: bool = False, frame_options: str = 'SAMEORIGIN', frame_options_allow_from: str | None = None, strict_transport_security: bool = True, strict_transport_security_preload: bool = False, strict_transport_security_max_age: int = 31556926, strict_transport_security_include_subdomains: bool = True, content_security_policy: str | dict[str, str] = {'default-src': "'self'", 'object-src': "'none'"}, content_security_policy_report_uri: str | None = None, content_security_policy_report_only: bool = False, content_security_policy_nonce_in: list[str] | None = None, referrer_policy: str = 'strict-origin-when-cross-origin', session_cookie_secure: bool = True, session_cookie_http_only: bool = True, session_cookie_samesite: str = 'Lax', x_content_type_options: bool = True, x_xss_protection: bool = True) None[source]

Set up this instance for use with app.

app.fs

app.fs.create_gitignore(path: Path) None[source]

Create a .gitignore file to exclude dir from versioning.

Parameters:
path: Path

Path to dir to exclude.

app.fs.init_app(app: Flask) None[source]

Initialize filesystem.

Parameters:
app: Flask

Application object.

app.log

Register custom app loggers.

app.log.init_app(app: Flask) None[source]

Initialize application loggers.

Parameters:
app: Flask

Application object.

app.log.integrate_loggers(app: Flask) None[source]

Integrate Flask logger with Gunicorn logger.

https://trstringer.com/logging-flask-gunicorn-the-manageable-way/

Parameters:
app: Flask

Application object.

app.log.smtp_handler(app: Flask) None[source]

Configure an SMTP handler for error reporting.

Parameters:
app: Flask

Application object.

app.models

Application’s database models.

class app.models.BaseModel(**kwargs)[source]

Bases: Model

Base model for which all models in this app are derived.

export() dict[str, str][source]

Get database attributes as a dict of str objects.

Returns:

Database as a dict.

class app.models.Message(**kwargs)[source]

Bases: BaseModel

Database schema for user messages.

author
body

Message body.

created

Date that the message was created and sent.

id

Message ID.

recipient
recipient_id

ID of the recipient of the message.

sender_id

ID of the sender of the message.

class app.models.Notification(**kwargs)[source]

Bases: BaseModel

Database schema for notifications.

get_mapping() dict[str, object][source]

Get dict representation of JSON data.

Returns:

Dict object of notification data.

id

ID of this notification.

mapping

Database to JSON dictionary mapping as a string.

name

Name of this notification.

set_mapping(mapping: dict[str, object]) None[source]

Set dict object as str (JSON).

Parameters:
mapping: dict[str, object]

Set object as str.

timestamp

Date this notification was created.

user
user_id

ID of the user who this notification belongs to.

class app.models.Post(**kwargs)[source]

Bases: BaseModel

Database schema for posts.

author
body

Body of this post.

created

Date that the post was created.

edited

Date that this post was last edited.

classmethod get_post(id: int, version: int | None = None, checkauthor: bool = True) Post[source]

Get post by post’s ID or abort with 404: Not Found.

Standard behaviour would be to return None, so do not bypass silently.

If a version number is provided find version by index. This is a different search to getting post by ID, as database starts at 1, but index always starts at 0. If no version can be returned a 404: Not Found error will abort instead of raising an IndexError. Assign post attributes title and body to the yielded Post object. The PostVersion cannot be returned for a restore as it is a different object and will not save when committing database changes.

The checkauthor argument is defined so that the function can be used to get a post without checking the author. This would be useful if we wrote a view to show an individual post on a page where the user doesn’t matter, because they are not modifying the post. If the logged in author does not own the post abort with 403: Forbidden.

Parameters:
id: int

The post’s ID.

version: int | None = None

If provided populate session object with version.

checkauthor: bool = True

Rule whether to check for author ID.

Returns:

Post’s connection object.

get_version(index: int) ModelBuilder | None[source]

Get version of post by index.

If no version can be returned a 404: Not Found error will abort instead of raising an IndexError.

Parameters:
index: int

Index of version beginning with 0.

Returns:

PostVersion object if within index range, else None.

id

ID of the user that wrote this post.

title

Title of this post.

user_id

ID of this post.

versions
class app.models.Task(**kwargs)[source]

Bases: BaseModel

Database schema for background tasks.

complete

Whether this task is complete or not.

description

Description of the task.

id

ID of the task.

name

Name of the task.

user
user_id

User that this task belongs to.

class app.models.User(**kwargs)[source]

Bases: UserMixin, BaseModel

Database schema for users.

about_me

About me page for the this user.

add_notifications(name: str, data: dict[str, object]) Notification[source]

Add user’s notifications to database.

Parameters:
name: str

Name of the database key.

data: dict[str, object]

Saved data.

Returns:

Instantiated Notification database model.

admin

Whether this this user is an admin or not.

authorized

Whether this user is authorized to make posts or not.

avatar(size: int) str[source]

Generate unique avatar for user derived from email hash.

Parameters:
size: int

Size of the avatar.

Returns:

URL leading to avatar for img link.

check_password(password: str) bool[source]

Match entered password against existing password hash.

Parameters:
password: str

User’s password attempt.

Returns:

Attempt matches hash: True or False.

confirmed

Whether this this user is confirmed or not.

confirmed_on

Date that the this user was confirmed on.

created

Date that the user was created.

email

Email of this user.

follow(user: User) None[source]

Follow another user if not already following.

Parameters:
user: User

User model object of user to follow.

followed : RelationshipProperty

User’s that this user if following.

followed_posts() list[Post][source]

Get all posts that the user is following.

Returns:

List of posts that the user is following in descending order.

followers
get_tasks_in_progress() list[Query][source]

Get the currently running tasks triggered by user.

Returns:

List of BaseQuery objects returned as running tasks.

id

ID of this user.

is_following(user: User) bool[source]

Check whether following another user.

Parameters:
user: User

User model object of user to check if following.

Returns:

Following the user? True or False.

last_message_read_time

The last time the user visited the messages page.

last_seen

Date that the this user last logged in on.

messages_received

Messages this user has received.

messages_sent

Messages that this user has sent.

new_messages() int[source]

Get the number of new (unread) messages delivered to user.

Returns:

Number of new messages delivered to user.

notifications

Notifications pending for this user.

password_hash

Password hash of this user’s password.

posts

Posts that have been added by the this user.

classmethod resolve_all_names(username: str) User[source]

Manage retrieval of User object by their username.

Parameters:
username: str

Username to search for user under.

Raises:

HTTPError – Raise 404: Not Found if name not resolved.

Returns:

User object.

set_password(password: str) None[source]

Hash and store a new user password.

Parameters:
password: str

User’s choice in password.

tasks

Tasks associated with this user.

unfollow(user: User) None[source]

Unfollow another user if already following.

Parameters:
user: User

User model object of user to unfollow.

username

Username of this user.

class app.models.Usernames(**kwargs)[source]

Bases: BaseModel

Database schema for username changes.

id

ID of the username.

user_id

ID of the user that this username belongs to.

username

Single username amongst the usernames.

app.shell

Define attributes regarding use of flask shell.

app.shell.init_app(app: Flask) None[source]

Register shell context objects.

Parameters:
app: Flask

Application object.

app.shell.register_models(app: Flask) None[source]

Make database models accessible to the Flask shell.

Parameters:
app: Flask

Application object.

app.user

Functionality for user’s of the app.

app.user.create_user(username: str, email: str, password: str, **kwargs: object) User[source]

Instantiate a new user, add user to the database, and return.

Parameters:
username: str

New user’s username.

email: str

New user’s email address.

password: str

New user’s password.

**kwargs: object

Non-mandatory keyword arguments to pas to model.

Returns:

New User object.

app.user.user_login(id: int) User[source]

Wrap the user object.

Parameters:
id: int

User id to get.

Returns:

Returned user object.

app.version

Contains the version of this package.

Allows for access to the version internally without cyclic imports caused by accessing it through __init__.

app.views

A view function is written to respond to application requests.

Flask uses patterns to match the incoming request URL to the view that should handle it. The view returns data that Flask turns into an outgoing response.

Flask can also go the other direction and generate a URL to view based on its name and arguments.

app.views.format_html(response: Response) Response[source]

Prettify HTML response.

See https://stackoverflow.com/a/6167432/13316671.

Parameters:
response: Response

Response returned from a route.

Returns:

Response returned from a route.

app.views.init_app(app: Flask) None[source]

Load the app with views.

Parameters:
app: Flask

Application object.

app.views.admin

app.views.admin.admin() str[source]

Render admin page.

Returns:

Rendered admin page.

app.views.auth

app.views.auth.login() str | Response[source]

Log in to an existing account.

The user is queried first and stored in a variable for use later use.

The check_password_hash function hashes the submitted password in the same way as the stored hash and securely compares the two. If they match the password is valid.

The session object is a dict that stores data across requests. When validation succeeds the user ID is stored in a new session. The data is then stored in a cookie that is sent to the browser and the browser then sends it back with subsequent requests. Flask securely signs the data so that it cannot be tampered with.

At the beginning of each request if a user is logged in their information should be loaded and made available to other views.

Returns:

Rendered login template on GET or failed login POST. Response object redirect to index view on successful login POST.

app.views.auth.logout() Response[source]

Log user out.

To log out the user’s ID needs to be removed from the session.

The load_logged_in_user function will not load a user on subsequent requests.

There is no view for this route so user will be redirected the user to the index view.

Returns:

Response object redirect to index view.

app.views.auth.register() str | Response[source]

Register a new user account.

When the user visits the /auth/register URL the register view will return an HTML template str with a form for the user to fill out. When the user submits the form it will validate their input and either create the new user and go to login page or show the form again with an error message.

Returns:

Rendered register template on GET, or POST with error. Response object redirect to the “login” view on successful POST.

app.views.auth.request_password_reset() str | Response[source]

Allow user to reset their password.

Once the request password form is filled in with the user’s email (if the email address is in the database) send an email with a link that leads to the reset password page.

Returns:

Rendered auth/request_password_reset template on GET or invalid email POST. Response object redirect to “login” view on successful email POST.

app.views.auth.reset_password(token: str) str | Response[source]

This route contains the token securely sent to the user’s inbox.

When the token is decrypted, it will return the user’s email address (if it is valid), and the correct user will be retrieved from the database to complete the password reset.

Parameters:
token: str

Unique token generated from user’s email address.

Returns:

Rendered auth/reset_password template on GET or invalid token POST. Response object redirect to index view on successful token POST.

app.views.auth.unconfirmed() str[source]

Unconfirmed email route.

Returns:

Rendered auth/unconfirmed template.

app.views.database

Admin page initialized with models for reading and writing.

class app.views.database.MyAdminIndexView(name=None, category=None, endpoint=None, url=None, template='admin/index.html', menu_class_name=None, menu_icon_type=None, menu_icon_value=None)[source]

Bases: AdminIndexView

Custom index view that handles login / registration.

index() str | Response[source]

Requires user be logged in as admin.

Returns:

Admin index page.

app.views.database.init_app(app: Flask) None[source]

Add models to admin view.

Not consistent with the application factory pattern for Flask extensions, however, the Admin object does not integrate into the application well as a global singleton.

While running pytest, Admin will be called multiple times and the following will be raised:

ValueError: The name ‘user’ is already registered for a different blueprint. Use ‘name=’ to provide a unique name.

See https://stackoverflow.com/questions/18002750

Parameters:
app: Flask

Application object.

app.views.forms

All classes derived from FlaskForm imported from Flask-WTF. Flask-WTF is a Flask extension built on top of WTForms.

class app.views.forms.EditProfile(*args, **kwargs)[source]

Bases: FlaskForm

Form for editing an existing user’s profile page.

about_me = <UnboundField(TextAreaField, (l'About me',), {'validators': [<wtforms.validators.Length object>]})>
submit = <UnboundField(SubmitField, (l'Submit',), {})>
username = <UnboundField(StringField, (l'username',), {'validators': [<wtforms.validators.DataRequired object>]})>
class app.views.forms.EmptyForm(*args, **kwargs)[source]

Bases: FlaskForm

Empty form: Submit only.

submit = <UnboundField(SubmitField, (l'Submit',), {})>
class app.views.forms.LoginForm(*args, **kwargs)[source]

Bases: FlaskForm

Form for logging in an existing user.

password = <UnboundField(PasswordField, (l'Password',), {'validators': [<wtforms.validators.DataRequired object>]})>
remember_me = <UnboundField(BooleanField, (l'Remember Me',), {})>
submit = <UnboundField(SubmitField, (l'Sign In',), {})>
username = <UnboundField(StringField, (l'Username',), {'validators': [<wtforms.validators.DataRequired object>]})>
class app.views.forms.MessageForm(*args, **kwargs)[source]

Bases: FlaskForm

Form for creating new Message objects.

message = <UnboundField(TextAreaField, (l'Message',), {'validators': [<wtforms.validators.DataRequired object>, <wtforms.validators.Length object>]})>
submit = <UnboundField(SubmitField, (l'Submit',), {})>
class app.views.forms.PostForm(*args, **kwargs)[source]

Bases: FlaskForm

Form for creating new Post objects.

body = <UnboundField(PageDownField, (l'Body',), {'validators': [<wtforms.validators.DataRequired object>], 'render_kw': {'rows': 12}})>
submit = <UnboundField(SubmitField, (l'Submit',), {})>
title = <UnboundField(StringField, (l'Title',), {'validators': [<wtforms.validators.DataRequired object>]})>
class app.views.forms.RegistrationForm(*args, **kwargs)[source]

Bases: FlaskForm

Form for registering a new user.

confirm_password = <UnboundField(PasswordField, (l'Confirm Password',), {'validators': [<wtforms.validators.DataRequired object>, <wtforms.validators.EqualTo object>]})>
email = <UnboundField(StringField, (l'Email',), {'validators': [<wtforms.validators.DataRequired object>, <wtforms.validators.Email object>, <wtforms.validators.Length object>]})>
password = <UnboundField(PasswordField, (l'Password',), {'validators': [<wtforms.validators.DataRequired object>]})>
submit = <UnboundField(SubmitField, (l'Register',), {})>
username = <UnboundField(StringField, (l'Username',), {'validators': [<wtforms.validators.DataRequired object>, <wtforms.validators.Length object>]})>
validate_email(email: StringField) None[source]

WTForms validates methods prefixed with validate_.

Parameters:
email: StringField

Email to check validity for.

Raises:

ValidationError – if user with email already exists.

validate_username(username: StringField) None[source]

WTForms validates methods prefixed with validate_.

Parameters:
username: StringField

Username to check validity for.

Raises:

ValidationError – if username already exists.

class app.views.forms.ResetPasswordForm(*args, **kwargs)[source]

Bases: FlaskForm

Form for resetting an existing user’s password.

confirm_password = <UnboundField(PasswordField, (l'Confirm Password',), {'validators': [<wtforms.validators.DataRequired object>, <wtforms.validators.EqualTo object>]})>
password = <UnboundField(PasswordField, (l'Password',), {'validators': [<wtforms.validators.DataRequired object>]})>
submit = <UnboundField(SubmitField, (l'Reset Password',), {})>
class app.views.forms.ResetPasswordRequestForm(*args, **kwargs)[source]

Bases: FlaskForm

Form for requesting a reset email for an existing user.

email = <UnboundField(StringField, (l'Email',), {'validators': [<wtforms.validators.DataRequired object>, <wtforms.validators.Email object>]})>
submit = <UnboundField(SubmitField, (l'Request Password Reset',), {})>
class app.views.forms.UploadForm(*args, **kwargs)[source]

Bases: FlaskForm

For for uploading files.

file = <UnboundField(FileField, (l'File',), {})>
submit = <UnboundField(SubmitField, (l'Submit',), {})>

app.views.mail

Setup app’s mailer.

app.views.mail.send_email(**kwargs: object) None[source]

Send a threaded email.

Without threading the app will wait until the email has been sent before continuing.

In order to access the application context for this function a protected werkzeug attribute has to be accessed. From https://blog.miguelgrinberg.com/post/ the-flask-mega-tutorial-part-xv-a-better-application-structure:

Using current_app directly in the send_async_email() function that runs as a background thread would not have worked, because current_app is a context-aware variable that is tied to the thread that is handling the client request. In a different thread, current_app would not have a value assigned.

Passing current_app directly as an argument to the thread object would not have worked either, because current_app is really a proxy object that is dynamically mapped to the application instance. So passing the proxy object would be the same as using current_app directly in the thread.

What I needed to do is access the real application instance that is stored inside the proxy object, and pass that as the app argument. The current_app._get_current_object() expression extracts the actual application instance from inside the proxy object, so that is what I passed to the thread as an argument.

Note: Keyword args (dict) to pass to attachments:

See flask_mail.Message.attach.

  • filename: filename of attachment

  • content_type: file mimetype

  • data: the raw file data

Parameters:
**kwargs: object

Keyword args to pass to Message: See flask_mail.Message.

app.views.order

app.views.order.call() Response[source]

Book a call.

Returns:

Response object.

app.views.order.cancel() Response[source]

Render template on cancellation.

Returns:

Response object redirecting user to index.

app.views.order.success() Response[source]

Render template on success.

Returns:

Success template.

app.views.post

app.views.post.create() str | Response[source]

Create a post.

The decorator will ensure that the user is logged in to visit this view, otherwise they will be redirected the login page.

The create view works the same as the auth register view. The posted data is validated and either the form is displayed (and the post is added to the database) or an error is shown.

Returns:

Rendered create template on GET or failed POST. Response object redirect to index view on successful POST.

app.views.post.delete(id: int) Response[source]

Delete post by post’s ID.

The “delete” view does not have its own template. Since there is no template it will only handle the POST method and then redirect to the index view.

Parameters:
id: int

The post’s ID.

Returns:

Response object redirect to index view.

app.views.post.read(id: int) str | Response[source]

Render post page for selected post ID.

Parameters:
id: int

ID of post to display full page on.

Returns:

Rendered post template.

app.views.post.sitemap_read() Generator[tuple[str, dict[str, Any], datetime, str, float], None, None][source]

Generate post URLs for sitemap.

Returns:

Generator yielding data for sitemap.

app.views.post.update(id: int) str | Response[source]

Update post that corresponds to the provided post ID.

Parameters:
id: int

The post’s ID.

Returns:

Rendered update template on GET or failed POST. Response object redirect to index view on successful update POST.

app.views.public

app.views.public.favicon() Response[source]

Endpoint for the app’s favicon.

Returns:

Response object.

app.views.public.index() str[source]

App’s index page.

The index will show all the posts with the most recent first.

JOIN is used so that the author information from the user table is available in the result.

Returns:

Rendered index template.

app.views.public.profile(username: str) str | Response[source]

Render user’s profile page.

Parameters:
username: str

Username of registered user.

Returns:

Rendered profile template.

app.views.public.sitemap_index() Generator[tuple[str, dict[str, Any], datetime, str, float], None, None][source]

Add index to sitemap.

Returns:

Generator yielding data for sitemap.

app.views.public.sitemap_user() Generator[tuple[str, dict[str, Any], datetime, str, float], None, None][source]

Generate user URLs for sitemap.

Returns:

Generator yielding data for sitemap.

app.views.redirect

app.views.redirect.confirm_email(token: str) Response[source]

Confirm each individual user registering with their email.

There is no view for this route and so the user will be redirected to the index page.

Parameters:
token: str

Encrypted token to verify correct user.

Returns:

Response object redirect to index view.

app.views.redirect.follow(username: str) Response[source]

Add a user model to follow to the current user model.

There is no view for this route and so the user will be redirected to the profile view.

Parameters:
username: str

User to follow.

Returns:

Response object redirect to profile view of user that has been followed.

app.views.redirect.resend_confirmation() Response[source]

Resend verification email.

There is no view for this route and so the user will be redirected to the auth/unconfirmed view.

Returns:

Response object redirect to auth/unconfirmed view.

app.views.redirect.unfollow(username: str) Response[source]

Remove a user model to unfollow from the current user model.

There is no view for this route and so the user will be redirected to the profile view.

Parameters:
username: str

User to unfollow.

Returns:

response object redirect to profile view of user that has been unfollowed.

app.views.report

app.views.report.csp_report() Response[source]

Post Content Security Report to report-uri.

Log CSP violations JSON payload.

Returns:

Response object with HTTP Status 204 (No Content) status.

app.views.security

Define app’s security functionality.

app.views.security.admin_required(view: t.Callable[..., str | Response]) t.Callable[..., str | Response][source]

Handle views that require an admin be signed in.

Admin needs to be logged in to create, edit, and delete posts.

The new function checks if an admin is loaded and returns a 401 Unauthorized error otherwise. If an admin user is loaded the original view is called and continues normally.

Parameters:
view: t.Callable[..., str | Response]

View function to wrap.

Returns:

The wrapped function supplied to this decorator.

app.views.security.authorization_required(view: t.Callable[..., str | Response]) t.Callable[..., str | Response][source]

Handle views that require an authorized user be signed in.

The new function checks if an authorized user is loaded and returns a 401 Unauthorized error otherwise. If an authorized user is loaded the original view is called and continues normally.

Parameters:
view: t.Callable[..., str | Response]

View function to wrap.

Returns:

The wrapped function supplied to this decorator.

app.views.security.confirm_token(token: str, max_age: int = 3600) str[source]

Confirm token for verifying new user’s email.

Parameters:
token: str

Token to serialize with secret key.

max_age: int = 3600

Max age of token before considered invalid.

Returns:

Return user’s email if valid.

app.views.security.confirmation_required(view: t.Callable[..., str | Response]) t.Callable[..., str | Response][source]

Handle views that require a verified logged-in user.

The new function checks if a user is confirmed or not and redirects The user to the auth/unconfirmed page otherwise. If a confirmed user is loaded the original view is called and continues normally.

Parameters:
view: t.Callable[..., str | Response]

View function to wrap.

Returns:

The wrapped function supplied to this decorator.

app.views.security.generate_confirmation_token(email: str) str | bytes[source]

Generate unique token for verifying new user’s email.

Parameters:
email: str

Email of recipient.

Returns:

Unique token for user to authenticate with.

app.views.security.generate_reset_password_token(user_id: int, max_age: int = 600) str[source]

Generate a hashed token leading to the reset password route.

Parameters:
user_id: int

The ID of the user requesting a password reset.

max_age: int = 600

The maximum time the token is valid for.

Returns:

A unique token hashed from user’s ID.

app.views.security.get_requested_reset_password_user(token: str) User[source]

Decode the user’s token and return the user model.

Parameters:
token: str

Token to decrypt.

Returns:

User, if the token is valid, else None.

app.views.upload

app.views.upload.favicon() str | Response[source]

Upload a favicon.

Returns:

Template or response object.

app.views.upload.validate_image(stream: t.IO[bytes]) str | None[source]

Confirm an image is what it claims to be.

Parameters:
stream: t.IO[bytes]

Bytes stream.

Returns:

String if file valid, else None for invalid file.

app.views.user

app.views.user.edit_profile() str | Response[source]

Edit a user’s personal profile page.

Returns:

Rendered profile/edit template on GET. Response object redirect to index view on successful POST.

app.views.user.messages() str[source]

View received messages delivered to logged in user.

Returns:

Rendered user/messages template to view received messages.

app.views.user.notifications() Response[source]

Retrieve notifications for logged-in user.

Returns:

Response containing JSON payload.

app.views.user.send_message(recipient: str) str | Response[source]

Send message to another user.

Parameters:
recipient: str

Email address to send email to.

Returns:

Rendered user/send_message template on GET. Response object redirect to recipient’s view on successful POST.