Field-level visibility control for Pydantic models
Project description
Pydantic Visible Fields
A flexible field-level visibility control system for Pydantic models. This library allows you to define which fields of your models are visible to different user roles, making it easy to implement role-based access control at the data model level.
Overview
pydantic-visible-fields provides a simple way to control which fields of your Pydantic models are visible to different user roles. It is particularly useful for API responses where you want to return different data depending on the user's role.
It also provides a PaginatedResponse class which makes it easy to generate paginated user responses with automatic conversion of objects to the correct visibility level.
Key Features
- 🔒 Field-level visibility control using a simple decorator
- 🧩 Class-level visibility control for more complex scenarios
- 👥 Role inheritance to simplify permission management
- 🔄 Nested model support with full recursive visibility control
- 🌲 Circular reference handling to safely process complex object graphs
- 🚀 Simple integration with FastAPI and other web frameworks
Installation
pip install pydantic-visible-fields
Basic Usage
1. Define Your Roles
First, define your roles as an enum:
from enum import Enum
class Role(str, Enum):
VIEWER = "viewer"
EDITOR = "editor"
ADMIN = "admin"
2. Configure Role System
Configure the role inheritance hierarchy:
from pydantic_visible_fields import configure_roles
configure_roles(
role_enum=Role,
inheritance={
Role.ADMIN: [Role.EDITOR],
Role.EDITOR: [Role.VIEWER],
},
default_role=Role.VIEWER
)
3. Create Models with Visibility Rules
Use the VisibleFieldsModel base class and field decorator to control field visibility:
from pydantic_visible_fields import VisibleFieldsModel, field
class User(VisibleFieldsModel):
id: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
username: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
email: str = field(visible_to=[Role.EDITOR, Role.ADMIN])
hashed_password: str = field(visible_to=[Role.ADMIN])
is_active: bool = field(visible_to=[Role.ADMIN])
4. Use in API Responses
In your API handlers, convert models to role-specific responses:
from fastapi import Depends
from pydantic_visible_fields import visible_fields_response
@app.get("/users/{user_id}")
async def get_user(user_id: str, current_user = Depends(get_current_user)):
user = await get_user_by_id(user_id)
# Return different fields based on user's role
role = get_user_role(current_user)
return visible_fields_response(user, role=role)
Advanced Usage
Class-Level Visibility
For more complex scenarios, you can define visibility at the class level:
from pydantic import BaseModel
from pydantic_visible_fields import VisibleFieldsMixin
from typing import ClassVar, Dict, Set
class UserSettings(BaseModel, VisibleFieldsMixin):
_role_visible_fields: ClassVar[Dict[str, Set[str]]] = {
Role.VIEWER: {"id", "theme"},
Role.EDITOR: {"notifications"},
Role.ADMIN: {"advanced_options", "debug_mode"},
}
id: str
theme: str
notifications: bool
advanced_options: dict
debug_mode: bool
Nested Models
Visibility control works recursively with nested models:
class Address(VisibleFieldsModel):
street: str = field(visible_to=[Role.EDITOR, Role.ADMIN])
city: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
country: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
postal_code: str = field(visible_to=[Role.EDITOR, Role.ADMIN])
class FullUser(VisibleFieldsModel):
id: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
username: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
email: str = field(visible_to=[Role.EDITOR, Role.ADMIN])
address: Address = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
Working with Collections
Visibility control works with lists and dictionaries of models:
class Team(VisibleFieldsModel):
id: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
name: str = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
members: List[User] = field(visible_to=[Role.VIEWER, Role.EDITOR, Role.ADMIN])
settings: Dict[str, Setting] = field(visible_to=[Role.EDITOR, Role.ADMIN])
Dynamic Visibility Configuration
You can dynamically configure visibility rules:
# Add a field to the VIEWER role
User.configure_visibility(Role.VIEWER.value, {"id", "username", "email"})
# Change all visible fields for ADMIN
User.configure_visibility(Role.ADMIN.value, {"id", "username", "email", "hashed_password", "is_active", "last_login"})
FastAPI Integration Example
Here's a complete example of how to use the library with FastAPI:
from enum import Enum
from fastapi import FastAPI, Depends, HTTPException
from typing import List
from pydantic_visible_fields import (
VisibleFieldsModel, visible_fields_response, field, configure_roles
)
# Define roles
class Role(str, Enum):
PUBLIC = "public"
USER = "user"
ADMIN = "admin"
# Configure role system
configure_roles(
role_enum=Role,
inheritance={
Role.ADMIN: [Role.USER],
Role.USER: [Role.PUBLIC],
},
default_role=Role.PUBLIC
)
# Models with visibility rules
class Item(VisibleFieldsModel):
id: int = field(visible_to=[Role.PUBLIC, Role.USER, Role.ADMIN])
name: str = field(visible_to=[Role.PUBLIC, Role.USER, Role.ADMIN])
description: str = field(visible_to=[Role.USER, Role.ADMIN])
price: float = field(visible_to=[Role.USER, Role.ADMIN])
cost: float = field(visible_to=[Role.ADMIN])
supplier: str = field(visible_to=[Role.ADMIN])
# Sample database
items_db = [
Item(id=1, name="Item 1", description="Description 1", price=10.99, cost=5.50, supplier="Supplier A"),
Item(id=2, name="Item 2", description="Description 2", price=20.99, cost=12.75, supplier="Supplier B"),
]
# FastAPI app
app = FastAPI()
# Dependency to get user role (in a real app, this would use auth logic)
def get_user_role(role_name: str = "public"):
if role_name == "admin":
return Role.ADMIN
elif role_name == "user":
return Role.USER
return Role.PUBLIC
@app.get("/items/", response_model=List)
async def get_items(role = Depends(get_user_role)):
# Convert items to role-specific responses
return [visible_fields_response(item, role=role) for item in items_db]
@app.get("/items/{item_id}")
async def get_item(item_id: int, role = Depends(get_user_role)):
for item in items_db:
if item.id == item_id:
# Return different fields based on user's role
return visible_fields_response(item, role=role)
raise HTTPException(status_code=404, detail="Item not found")
API Reference
Core Classes
VisibleFieldsModel- Base model class with field-level visibility controlVisibleFieldsMixin- Mixin class that can be added to any Pydantic modelPaginatedResponse- Class for handling pagination, automatically converts models to the correct visibility level
Functions
field(visible_to=None, **kwargs)- Field decorator to specify visibilityconfigure_roles(role_enum, inheritance=None, default_role=None)- Configure the role systemvisible_fields_response(model, role=None)- Create a response with only visible fields
Methods
model.visible_dict(role=None)- Convert a model to a dictionary with only visible fieldsmodel.to_response_model(role=None)- Convert a model to a response model classModel.configure_visibility(role, visible_fields)- Configure visibility rules for a role
Development
Increasing version numbers
Use bump2version to increase the version number (included in the dev dependencies).
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantic_visible_fields-0.2.3.tar.gz.
File metadata
- Download URL: pydantic_visible_fields-0.2.3.tar.gz
- Upload date:
- Size: 21.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5dac43dd868c650ed3b72f76415f8322b8d994b72edea7b78cdcd9e5a75e424e
|
|
| MD5 |
90bc109b3da9b7eeb251ca0ac34ace3d
|
|
| BLAKE2b-256 |
ee39914a35e8571cc700f4bf98b6517b915c55482d82645d18df349d848d4afc
|
File details
Details for the file pydantic_visible_fields-0.2.3-py3-none-any.whl.
File metadata
- Download URL: pydantic_visible_fields-0.2.3-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a467ec04edbc232801f292d7dfc456de179812f812faf0304c60667ddc47e16
|
|
| MD5 |
9f607191d68d90536d97cac5cebdeec1
|
|
| BLAKE2b-256 |
bcb068df200160e9fb8586b6b22d655575b6bdde7e7b57cbfe8edb9990e83c90
|