Dynamic Data Tables Concept in Flask

Dynamic Data Table Pattern applied in Flask - can be used to manage information with ease via pagination, search, and filters.

Dynamic Data Table Pattern - can be used to manage information with ease via pagination, search, and filters.
Dynamic Data Table Pattern applied in Flask - can be used to manage information with ease via pagination, search, and filters.

This article explains the Dynamic Data Table Pattern and how it can be used to manage information with ease via pagination, search, and filters without any coding effort. For newcomers, Dynamic Programming is a method for solving complex problems by breaking them down into simpler subproblems.

Free Sample that incorporates the concept: Flask Datta Able (contains DEMO Link)

Let's see how the dynamic programming concept applies to Dynamic Data Tables, which is the solution we're trying to achieve in this article. 

Data Tables

Data Tables represent a structured way of organizing and displaying data in rows and columns with built-in functionality for managing large datasets. Think of it as an enhanced table that provides:

Pagination

  • Breaking down large datasets into smaller, manageable pages
  • Allows users to navigate through data without loading everything at once
  • Typically shows X records per page (e.g., 10, 25, 50 entries)

Data Organization

  • Rows represent individual records
  • Columns represent attributes or fields
  • Headers define the structure and can enable sorting

  Client-Side Processing

  • Suitable for small datasets
  • All records are pulled once on the client side
  • Data is processed locally in the browser or phone application

Server-Side processing

  • Database queries fetch only the required records
  • Reduces memory usage and improves performance
  • Handles large datasets efficiently

The goal of our research is to provide the above features out of the box using a minimal configuration and only the dynamic features of Python that can detect and manipulate data at runtime. Let's break down the task into smaller pieces and start applying the dynamic programming principles to our specific use case. 

Configuration

Users should be able to activate the dynamic pattern for any model using a simple and intuitive syntax. For this, we can use a map that uses the URL as the key, and the target model as value. 

DYNAMIC_DATATB = {
        "products": "apps.models.Product"
}

The above definition will use the routing "products" to build the dynamic table for the Product Model defined in the apps/models.py

The path for the model is now used to load the model class and analyze the fields and the type. 

Load Model Definition

The model can be analyzed using the importlib.import_module and getattr helpers as showcased below: 

def name_to_class(name: str):
        module_name = '.'.join(name.split('.')[:-1])
        class_name = name.split('.')[-1] 

        module = importlib.import_module(module_name)
        return getattr(module, class_name)

If we call the name_to_class() with the apps.models.Product input, we should get the Product Model Class returned by the getattr() helper.

Class/Model Fields

Having the class is the first step. Next is to get all fields, the associated types, and also all the foreign keys mapped to external types we don't know yet.   

The ordinary fields can be pulled from the class definition using this code:

db_fields = [field.name for field in aModelClass.__table__.columns if not field.foreign_keys] 

The above snippet will return all fields defined using ordinary types like Integer, String, DateTime, and Text. 

For the Foreign key discovery, the code needs to use the model metadata that saves the distant relationships with other Models. The code that provides all associated FKs is the one below: 

def get_model_fk_values(aModelClass):
    fk_values = {} 
    current_table_name = aModelClass.__tablename__
    for relationship in aModelClass.__mapper__.relationships:
        if relationship.direction.name == 'MANYTOONE':
            related_model = relationship.mapper.class_
            foreign_key_column = list(relationship.local_columns)[0]
            referenced_table_name = list(foreign_key_column.foreign_keys)[0].column.table.name

            if referenced_table_name != current_table_name:
                field_name = relationship.key
                related_instances = related_model.query.all()
                fk_values[field_name] = related_instances

    return fk_values

After solving the FKs case, the next challenge is to detect the ENUMS used by the model for mapping the information into combos. Without this detection, the ENUMS will be mapped to input fields and we might have issues during creation or update. 

choices_dict = {}
for column in aModelClass.__table__.columns:
	if isinstance(column.type, db.Enum):
		choices_dict[column.name] = [(choice.name, choice.value) for choice in column.type.enum_class]

The above code snippet iterates on the model fields and saves all values for each field detected as ENUM. 

At this point, we've achieved the following:

  • The model is automatically discovered using the URI segment and the configuration mapping
  • Fields information (name and type) covers all cases: simple types, ENUMS, foreign keys
  • The information is injected into the Jinja files to be presented to the user

Dynamic Table View

The page provided by the dynamic data table provides the usual controls like search, show hide columns control, filters, and export in CSV format.

Dynamic Tables in Python/Flask - Generated View

The item creation page is displayed with as below:

Dynamic Tables in Python/Flask - Item Creation Page

For more inputs regarding the concept feel free to contact the team in support or simply download and inspect the code in your local system: