Skip to content

Reflection

In large projects, especially those with legacy databases, you often need to represent existing database tables and views in your code without recreating them. Edgy's reflection feature provides a solution for this.

What is Reflection?

Reflection involves reading existing database tables and views and representing them as models in your code, effectively mirroring their structure.

Let's illustrate with an example.

Suppose you have a users table in your database, created using the following Edgy model:

import edgy
from edgy import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class User(edgy.Model):
    age: int = edgy.IntegerField(minimum=18)
    is_active: bool = edgy.BooleanField(default=True)

    class Meta:
        registry = models

This code snippet creates a users table in the database.

Note

This example is for demonstration purposes. If you already have tables in your database, you don't need to create them again.

Now, you want to reflect this existing users table into your Edgy models:

import edgy
from edgy import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class User(edgy.ReflectModel):
    age: int = edgy.IntegerField(minimum=18)
    is_active: bool = edgy.BooleanField(default=True)

    class Meta:
        tablename = "users"
        registry = models

Here's what happens:

  • ReflectModel connects to the database.
  • It reads the existing tables.
  • It identifies the users table.
  • It converts the users table columns into Edgy model fields.

Key Feature

ReflectModel works with both database tables and views. This allows you to represent any existing data structure in your code.

ReflectModel: Representing Existing Data

ReflectModel is similar to Edgy's Model but it does not generate migrations.

from edgy import ReflectModel

It supports standard database operations like inserting, deleting, updating, and creating records.

Parameters:

  • Meta.registry: The registry instance. This is mandatory.
  • Meta.tablename: The name of the table or view to reflect. Defaults to the pluralized class name.

Example:

import edgy
from edgy import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class User(edgy.ReflectModel):
    age: int = edgy.IntegerField(minimum=18)
    is_active: bool = edgy.BooleanField(default=True)

    class Meta:
        tablename = "users"
        registry = models

Fields: Mapping Database Columns

Fields in ReflectModel are declared like regular fields, representing the columns of the reflected table or view.

Example:

import edgy
from edgy import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class User(edgy.ReflectModel):
    age: int = edgy.IntegerField(minimum=18)
    is_active: bool = edgy.BooleanField(default=True)

    class Meta:
        tablename = "users"
        registry = models

Key Difference from Regular Models

Unlike regular Edgy models, ReflectModel allows you to specify only the fields you need, rather than requiring all fields from the database table or view.

Example:

Suppose you have a users table with the following structure:

import edgy
from edgy import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class User(edgy.Model):
    age: int = edgy.IntegerField(minimum=18, null=True)
    is_active: bool = edgy.BooleanField(default=True, null=True)
    description: str = edgy.CharField(max_length=255, null=True)
    profile_type: str = edgy.CharField(max_length=255, null=True)
    username: str = edgy.CharField(max_length=255, null=True)

    class Meta:
        registry = models

And you want to reflect only a few fields:

import edgy
from edgy import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class Profile(edgy.ReflectModel):
    is_active: bool = edgy.BooleanField(default=True, null=True)
    profile_type: str = edgy.CharField(max_length=255, null=True)
    username: str = edgy.CharField(max_length=255, null=True)

    class Meta:
        tablename = "users"
        registry = models

This flexibility allows you to work with only the necessary fields, simplifying your code.

Operations: CRUD Functionality

ReflectModel supports standard CRUD operations, just like regular Edgy models.

However, it only performs operations on the fields declared in the ReflectModel. If you attempt to update a field that is not declared in the ReflectModel, the operation will not occur.

Warning

SQL views may have limitations on write operations (create, update, etc.).

Reflection Outside of Defaults

To reflect tables from a different database or schema, set __using_schema__ and database after model creation.

import edgy

registry = edgy.Registry(...)

class AdvancedReflected(edgy.ReflectModel):
    __using_schema__ = "foo"
    a = edgy.CharField(max_length=40)

    class Meta:
        registry = registry

AdvancedReflected.database = otherdb
AdvancedReflected.__using_schema__ = "foo"

This technique is used by AutoReflectModel for automatic reflection.