Model Field Conventions

Developer guide for the Enezaframework. Part of the Developer documentation.

Table of Contents

  1. Overview
  2. The Three Rules
  3. Correct and Incorrect Examples
  4. Model-Level Image Size Definition
  5. Configuration Keys
  6. Change History
  7. Related Guides

Overview

Every model in this project follows three mandatory field conventions. They exist so migrations, diffs, and reviews stay readable and predictable. Apply them to every field you write, including the samples you copy from other guides.

There is no diagram in this guide; the rules and tables are the whole content.


The Three Rules

  1. One field per line. Never declare two or more fields on the same line.
  2. Always set verbose_name. Every field must include a verbose_name keyword argument.
  3. Multi-line fields close ) on its own line. When a field declaration spans multiple lines (for example a ForeignKey with related_name, on_delete, or help_text), the closing parenthesis goes on its own line, aligned with the start of the field declaration.

Correct and Incorrect Examples

Correct

python
class VendorTable(EnezaMaster):
    vendorno = models.CharField(
        max_length=50,
        unique=True,
        verbose_name="Vendor No.",
    )
    searchname = models.CharField(
        max_length=255,
        verbose_name="Search Name",
    )
    vendorgroup = models.ForeignKey(
        "VendorGroup",
        on_delete=models.PROTECT,
        related_name="vendors",
        verbose_name="Vendor Group",
    )

Incorrect

python
class VendorTable(EnezaMaster):
    # Two fields on one line:
    vendorno = models.CharField(max_length=50); searchname = models.CharField(max_length=255)

    # Missing verbose_name:
    blocked = models.BooleanField(default=False)

    # Closing parenthesis not on its own aligned line:
    vendorgroup = models.ForeignKey("VendorGroup", on_delete=models.PROTECT,
        verbose_name="Vendor Group")

The first block is the one used across this documentation series.


Model-Level Image Size Definition

Enezaframework lets you define image display sizes directly in the model field definition. A single generic template can then serve many models with different image requirements, because the widget reads the size configuration off the bound field.

flowchart TD
    MODEL[Model field:<br/>ConfigurableImageField<br/>image_size_config] --> FORMFIELD[formfield exposes<br/>image_size_config]
    FORMFIELD --> TAG[Template tag:<br/>is_image_field / smart_field]
    TAG --> WIDGET[image_upload_widget]
    WIDGET --> PREVIEW[Preview + responsive sizing]
    WIDGET --> CLIENT[Client-side validation:<br/>type + size]

Import ConfigurableImageField from enezaframework.models and pass image_size_config.

python
from django.db import models

from enezaframework.models import ConfigurableImageField


class Profile(models.Model):
    avatar = ConfigurableImageField(
        upload_to="profile_images/",
        image_size_config={
            "max_height": 200,
            "max_width": 200,
            "mobile_max_height": 150,
            "mobile_max_width": 150,
        },
        verbose_name="Avatar",
    )
    banner = ConfigurableImageField(
        upload_to="banner_images/",
        image_size_config={
            "max_height": 300,
            "max_width": 800,
            "mobile_max_height": 200,
            "mobile_max_width": 600,
        },
        verbose_name="Banner",
    )

ConfigurableImageField subclasses models.ImageField, so all normal ImageField options (upload_to, blank, null, verbose_name, and so on) still apply.

Multiple images

For a field that stores several images, use ConfigurableMultipleImageField. It subclasses models.JSONField and stores a list of image paths.

python
from enezaframework.models import ConfigurableMultipleImageField


class Listing(models.Model):
    photos = ConfigurableMultipleImageField(
        upload_to="listing_photos/",
        max_images=5,
        image_size_config={
            "max_height": 400,
            "max_width": 400,
            "mobile_max_height": 250,
            "mobile_max_width": 250,
        },
        verbose_name="Photos",
    )
  • max_images defaults to 5 and may not exceed 20.
  • The default value is an empty list.

Configuration Keys

image_size_config accepts these keys:

Key Type Description
max_height int Maximum display height in pixels (desktop).
max_width int Maximum display width in pixels (desktop).
mobile_max_height int Maximum display height on mobile.
mobile_max_width int Maximum display width on mobile.

The configuration is validated when the model class is created:

  • Only the keys above are allowed.
  • Every value must be a positive integer.
  • Values above 10000 pixels are rejected.
  • mobile_max_height must not exceed max_height, and mobile_max_width must not exceed max_width.

The field exposes helpers:

python
field = Profile._meta.get_field("avatar")
field.has_size_config()   # bool
field.get_size_config()   # copy of the config dict

For ConfigurableMultipleImageField, max_images must be a positive integer no greater than 20. Client-side, the widget validates file type (JPG, PNG, GIF) and file size (2 MB limit) before upload; server-side validation still applies through the underlying ImageField/form field.

Template integration uses the form_extras tags; the simplest is {% smart_field form "avatar" %}. See Frontend Templates & UI Reuse.


Change History

Dated changes that affect this guide, taken from docs/Release note/CHANGELOG.md and the release notes. For the complete history, see the changelog.

Date Version What changed
2026-06-30 0.2.8 Field configuration expanded with full_width, column width: "full", display: "label", rich text, and button-group widgets.

The three mandatory field conventions above are long-standing project rules and are not tied to a changelog entry. The model-level image size fields (ConfigurableImageField, ConfigurableMultipleImageField) also have no dated changelog entry in the 0.2.7–0.2.19 window.