or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdauthorization.mdcore-setup.mddatabase.mdindex.mdpassword-management.mdregistration.mdtwo-factor.mdunified-signin.mdutilities.mdwebauthn.md

database.mddocs/

0

# Database Integration

1

2

Datastore classes and database abstraction layer supporting multiple ORMs (SQLAlchemy, MongoEngine, Peewee, Pony) for flexible database integration with Flask-Security.

3

4

## Capabilities

5

6

### Base Datastore Classes

7

8

Abstract base classes providing the foundation for Flask-Security's database abstraction layer.

9

10

```python { .api }

11

class Datastore:

12

"""

13

Abstract base datastore class defining the interface for all datastores.

14

"""

15

16

def __init__(self, db):

17

"""

18

Initialize datastore with database connection.

19

20

Parameters:

21

- db: Database connection or session object

22

"""

23

24

def put(self, obj):

25

"""

26

Save object to database.

27

28

Parameters:

29

- obj: Object to save

30

31

Returns:

32

Saved object

33

"""

34

35

def delete(self, obj):

36

"""

37

Delete object from database.

38

39

Parameters:

40

- obj: Object to delete

41

42

Returns:

43

Deleted object

44

"""

45

46

def commit(self):

47

"""

48

Commit current transaction.

49

"""

50

51

class UserDatastore(Datastore):

52

"""

53

Core user management datastore with CRUD operations for users and roles.

54

"""

55

56

def __init__(self, db, user_model, role_model=None):

57

"""

58

Initialize user datastore.

59

60

Parameters:

61

- db: Database connection or session

62

- user_model: User model class

63

- role_model: Role model class (optional)

64

"""

65

66

def get_user(self, identifier):

67

"""

68

Get user by identifier (ID, email, username).

69

70

Parameters:

71

- identifier: User ID, email, or username

72

73

Returns:

74

User object if found, None otherwise

75

"""

76

77

def find_user(self, **kwargs):

78

"""

79

Find user by arbitrary attributes.

80

81

Parameters:

82

- kwargs: Attribute filters for user lookup

83

84

Returns:

85

User object if found, None otherwise

86

"""

87

88

def create_user(self, **kwargs):

89

"""

90

Create new user with specified attributes.

91

92

Parameters:

93

- kwargs: User attributes (email, password, etc.)

94

95

Returns:

96

Created user object

97

"""

98

99

def delete_user(self, user):

100

"""

101

Delete user from database.

102

103

Parameters:

104

- user: User object to delete

105

106

Returns:

107

True if deleted successfully, False otherwise

108

"""

109

110

def activate_user(self, user):

111

"""

112

Activate user account.

113

114

Parameters:

115

- user: User object to activate

116

117

Returns:

118

Activated user object

119

"""

120

121

def deactivate_user(self, user):

122

"""

123

Deactivate user account.

124

125

Parameters:

126

- user: User object to deactivate

127

128

Returns:

129

Deactivated user object

130

"""

131

132

def create_role(self, **kwargs):

133

"""

134

Create new role with specified attributes.

135

136

Parameters:

137

- kwargs: Role attributes (name, description, permissions)

138

139

Returns:

140

Created role object

141

"""

142

143

def find_role(self, role):

144

"""

145

Find role by name or role object.

146

147

Parameters:

148

- role: Role name (string) or role object

149

150

Returns:

151

Role object if found, None otherwise

152

"""

153

154

def add_role_to_user(self, user, role):

155

"""

156

Add role to user.

157

158

Parameters:

159

- user: User object

160

- role: Role object or role name

161

162

Returns:

163

True if role added successfully, False if user already had role

164

"""

165

166

def remove_role_from_user(self, user, role):

167

"""

168

Remove role from user.

169

170

Parameters:

171

- user: User object

172

- role: Role object or role name

173

174

Returns:

175

True if role removed successfully, False if user didn't have role

176

"""

177

```

178

179

### SQLAlchemy Datastores

180

181

Datastores for SQLAlchemy ORM integration with various Flask-SQLAlchemy versions and configurations.

182

183

```python { .api }

184

class SQLAlchemyUserDatastore(UserDatastore):

185

"""

186

SQLAlchemy integration datastore for Flask-Security.

187

"""

188

189

def __init__(self, db, user_model, role_model):

190

"""

191

Initialize SQLAlchemy datastore.

192

193

Parameters:

194

- db: SQLAlchemy database instance

195

- user_model: SQLAlchemy user model class

196

- role_model: SQLAlchemy role model class

197

"""

198

199

def get_user(self, identifier):

200

"""Get user by ID, email, or username using SQLAlchemy query."""

201

202

def find_user(self, case_insensitive=False, **kwargs):

203

"""

204

Find user with SQLAlchemy filters.

205

206

Parameters:

207

- case_insensitive: Whether to perform case-insensitive search

208

- kwargs: Filter attributes

209

210

Returns:

211

User object if found, None otherwise

212

"""

213

214

def toggle_active(self, user):

215

"""

216

Toggle user active status.

217

218

Parameters:

219

- user: User object to toggle

220

221

Returns:

222

Updated user object

223

"""

224

225

class SQLAlchemySessionUserDatastore(SQLAlchemyUserDatastore):

226

"""

227

SQLAlchemy datastore with explicit session management.

228

"""

229

230

def __init__(self, session, user_model, role_model):

231

"""

232

Initialize SQLAlchemy session datastore.

233

234

Parameters:

235

- session: SQLAlchemy session object

236

- user_model: SQLAlchemy user model class

237

- role_model: SQLAlchemy role model class

238

"""

239

240

class FSQLALiteUserDatastore(UserDatastore):

241

"""

242

Lightweight SQLAlchemy datastore for Flask-SQLAlchemy-Lite.

243

"""

244

245

def __init__(self, db, user_model, role_model):

246

"""

247

Initialize Flask-SQLAlchemy-Lite datastore.

248

249

Parameters:

250

- db: Flask-SQLAlchemy-Lite database instance

251

- user_model: User model class

252

- role_model: Role model class

253

"""

254

```

255

256

### Alternative ORM Datastores

257

258

Datastores for other popular Python ORMs providing flexible database backend options.

259

260

```python { .api }

261

class MongoEngineUserDatastore(UserDatastore):

262

"""

263

MongoDB/MongoEngine integration datastore.

264

"""

265

266

def __init__(self, db, user_model, role_model=None):

267

"""

268

Initialize MongoEngine datastore.

269

270

Parameters:

271

- db: MongoEngine database connection

272

- user_model: MongoEngine user document class

273

- role_model: MongoEngine role document class (optional)

274

"""

275

276

def get_user(self, identifier):

277

"""Get user from MongoDB using MongoEngine queries."""

278

279

def find_user(self, case_insensitive=False, **kwargs):

280

"""

281

Find user in MongoDB with case-insensitive option.

282

283

Parameters:

284

- case_insensitive: Enable case-insensitive search

285

- kwargs: Query filters

286

287

Returns:

288

User document if found, None otherwise

289

"""

290

291

class PeeweeUserDatastore(UserDatastore):

292

"""

293

Peewee ORM integration datastore.

294

"""

295

296

def __init__(self, db, user_model, role_model=None, role_link=None):

297

"""

298

Initialize Peewee datastore.

299

300

Parameters:

301

- db: Peewee database instance

302

- user_model: Peewee user model class

303

- role_model: Peewee role model class (optional)

304

- role_link: Peewee many-to-many link model (optional)

305

"""

306

307

def get_user(self, identifier):

308

"""Get user using Peewee ORM queries."""

309

310

def find_user(self, **kwargs):

311

"""

312

Find user with Peewee query filters.

313

314

Parameters:

315

- kwargs: Query filter attributes

316

317

Returns:

318

User model instance if found, None otherwise

319

"""

320

321

class PonyUserDatastore(UserDatastore):

322

"""

323

Pony ORM integration datastore.

324

"""

325

326

def __init__(self, db, user_model, role_model=None):

327

"""

328

Initialize Pony ORM datastore.

329

330

Parameters:

331

- db: Pony database instance

332

- user_model: Pony user entity class

333

- role_model: Pony role entity class (optional)

334

"""

335

336

def get_user(self, identifier):

337

"""Get user using Pony ORM queries."""

338

339

def find_user(self, **kwargs):

340

"""

341

Find user with Pony ORM query syntax.

342

343

Parameters:

344

- kwargs: Entity attribute filters

345

346

Returns:

347

User entity if found, None otherwise

348

"""

349

```

350

351

### Database Helper Classes

352

353

Utility classes for database compatibility and data type management.

354

355

```python { .api }

356

class AsaList:

357

"""

358

Custom list type for database storage compatibility.

359

Handles serialization/deserialization of list data for database storage.

360

"""

361

362

def __init__(self, data=None):

363

"""

364

Initialize AsaList with optional data.

365

366

Parameters:

367

- data: Initial list data (optional)

368

"""

369

370

def append(self, item):

371

"""

372

Append item to list.

373

374

Parameters:

375

- item: Item to append

376

"""

377

378

def remove(self, item):

379

"""

380

Remove item from list.

381

382

Parameters:

383

- item: Item to remove

384

"""

385

386

def __contains__(self, item):

387

"""Check if item is in list."""

388

389

def __iter__(self):

390

"""Iterate over list items."""

391

```

392

393

## Usage Examples

394

395

### SQLAlchemy Setup

396

397

```python

398

from flask import Flask

399

from flask_sqlalchemy import SQLAlchemy

400

from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin

401

402

app = Flask(__name__)

403

app.config['SECRET_KEY'] = 'super-secret'

404

app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:pass@localhost/mydb'

405

406

db = SQLAlchemy(app)

407

408

# Define many-to-many relationship table

409

roles_users = db.Table('roles_users',

410

db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),

411

db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))

412

)

413

414

# Define User model

415

class User(db.Model, UserMixin):

416

__tablename__ = 'user'

417

418

id = db.Column(db.Integer, primary_key=True)

419

email = db.Column(db.String(255), unique=True, nullable=False)

420

username = db.Column(db.String(80), unique=True, nullable=True)

421

password = db.Column(db.String(255), nullable=False)

422

active = db.Column(db.Boolean(), default=True)

423

confirmed_at = db.Column(db.DateTime())

424

425

# Two-factor authentication fields

426

tf_totp_secret = db.Column(db.String(255))

427

tf_primary_method = db.Column(db.String(20))

428

429

# Unified signin fields

430

us_phone_number = db.Column(db.String(20))

431

us_totp_secrets = db.Column(db.Text) # JSON field

432

433

# Relationships

434

roles = db.relationship('Role', secondary=roles_users,

435

backref=db.backref('users', lazy='dynamic'))

436

437

# Define Role model

438

class Role(db.Model, RoleMixin):

439

__tablename__ = 'role'

440

441

id = db.Column(db.Integer(), primary_key=True)

442

name = db.Column(db.String(80), unique=True, nullable=False)

443

description = db.Column(db.String(255))

444

445

# Initialize Flask-Security with SQLAlchemy datastore

446

user_datastore = SQLAlchemyUserDatastore(db, User, Role)

447

security = Security(app, user_datastore)

448

449

# Create tables

450

with app.app_context():

451

db.create_all()

452

```

453

454

### MongoEngine Setup

455

456

```python

457

from flask import Flask

458

from flask_mongoengine import MongoEngine

459

from flask_security import Security, MongoEngineUserDatastore, UserMixin, RoleMixin

460

461

app = Flask(__name__)

462

app.config['SECRET_KEY'] = 'super-secret'

463

app.config['MONGODB_DB'] = 'mydatabase'

464

app.config['MONGODB_HOST'] = 'localhost'

465

app.config['MONGODB_PORT'] = 27017

466

467

db = MongoEngine(app)

468

469

# Define Role document

470

class Role(db.Document, RoleMixin):

471

name = db.StringField(max_length=80, unique=True, required=True)

472

description = db.StringField(max_length=255)

473

474

# Define User document

475

class User(db.Document, UserMixin):

476

email = db.EmailField(unique=True, required=True)

477

username = db.StringField(max_length=80, unique=True)

478

password = db.StringField(max_length=255, required=True)

479

active = db.BooleanField(default=True)

480

confirmed_at = db.DateTimeField()

481

482

# Two-factor fields

483

tf_totp_secret = db.StringField(max_length=255)

484

tf_primary_method = db.StringField(max_length=20)

485

486

# Role relationship

487

roles = db.ListField(db.ReferenceField(Role), default=[])

488

489

# Initialize Flask-Security with MongoEngine datastore

490

user_datastore = MongoEngineUserDatastore(db, User, Role)

491

security = Security(app, user_datastore)

492

```

493

494

### Peewee Setup

495

496

```python

497

from flask import Flask

498

from peewee import *

499

from flask_security import Security, PeeweeUserDatastore, UserMixin, RoleMixin

500

501

app = Flask(__name__)

502

app.config['SECRET_KEY'] = 'super-secret'

503

504

# Initialize Peewee database

505

database = SqliteDatabase('myapp.db')

506

507

# Define base model

508

class BaseModel(Model):

509

class Meta:

510

database = database

511

512

# Define Role model

513

class Role(BaseModel, RoleMixin):

514

name = CharField(unique=True, max_length=80)

515

description = CharField(max_length=255, null=True)

516

517

# Define User model

518

class User(BaseModel, UserMixin):

519

email = CharField(unique=True, max_length=255)

520

username = CharField(unique=True, max_length=80, null=True)

521

password = CharField(max_length=255)

522

active = BooleanField(default=True)

523

confirmed_at = DateTimeField(null=True)

524

525

# Two-factor fields

526

tf_totp_secret = CharField(max_length=255, null=True)

527

tf_primary_method = CharField(max_length=20, null=True)

528

529

# Define many-to-many relationship

530

class UserRole(BaseModel):

531

user = ForeignKeyField(User, backref='user_roles')

532

role = ForeignKeyField(Role, backref='role_users')

533

534

class Meta:

535

indexes = (

536

(('user', 'role'), True), # Unique constraint

537

)

538

539

# Initialize Flask-Security with Peewee datastore

540

user_datastore = PeeweeUserDatastore(database, User, Role, UserRole)

541

security = Security(app, user_datastore)

542

543

# Create tables

544

with app.app_context():

545

database.create_tables([User, Role, UserRole], safe=True)

546

```

547

548

### Pony ORM Setup

549

550

```python

551

from flask import Flask

552

from pony.orm import *

553

from flask_security import Security, PonyUserDatastore, UserMixin, RoleMixin

554

555

app = Flask(__name__)

556

app.config['SECRET_KEY'] = 'super-secret'

557

558

# Initialize Pony database

559

db = Database()

560

561

# Define User entity

562

class User(db.Entity, UserMixin):

563

_table_ = 'user'

564

565

id = PrimaryKey(int, auto=True)

566

email = Required(str, unique=True, max_len=255)

567

username = Optional(str, unique=True, max_len=80)

568

password = Required(str, max_len=255)

569

active = Required(bool, default=True)

570

confirmed_at = Optional(datetime)

571

572

# Two-factor fields

573

tf_totp_secret = Optional(str, max_len=255)

574

tf_primary_method = Optional(str, max_len=20)

575

576

# Role relationship

577

roles = Set('Role')

578

579

# Define Role entity

580

class Role(db.Entity, RoleMixin):

581

_table_ = 'role'

582

583

id = PrimaryKey(int, auto=True)

584

name = Required(str, unique=True, max_len=80)

585

description = Optional(str, max_len=255)

586

587

# User relationship

588

users = Set(User)

589

590

# Bind database and generate mapping

591

db.bind('sqlite', 'myapp.db')

592

db.generate_mapping(create_tables=True)

593

594

# Initialize Flask-Security with Pony datastore

595

user_datastore = PonyUserDatastore(db, User, Role)

596

security = Security(app, user_datastore)

597

```

598

599

### Custom Datastore Operations

600

601

```python

602

from flask_security import current_user

603

604

@app.route('/admin/users')

605

def list_users():

606

"""List all users using datastore."""

607

users = user_datastore.user_model.query.all() # SQLAlchemy example

608

return render_template('admin_users.html', users=users)

609

610

@app.route('/admin/create-user', methods=['POST'])

611

def create_user():

612

"""Create user via datastore."""

613

email = request.form.get('email')

614

password = request.form.get('password')

615

616

# Create user using datastore

617

user = user_datastore.create_user(

618

email=email,

619

password=hash_password(password),

620

active=True

621

)

622

623

# Assign default role

624

default_role = user_datastore.find_role('user')

625

if default_role:

626

user_datastore.add_role_to_user(user, default_role)

627

628

user_datastore.commit()

629

630

flash(f'User {email} created successfully')

631

return redirect(url_for('list_users'))

632

633

@app.route('/admin/toggle-user/<int:user_id>')

634

def toggle_user_active(user_id):

635

"""Toggle user active status."""

636

user = user_datastore.get_user(user_id)

637

638

if user:

639

if hasattr(user_datastore, 'toggle_active'):

640

# SQLAlchemy datastore has toggle_active method

641

user_datastore.toggle_active(user)

642

else:

643

# Manual toggle for other datastores

644

user.active = not user.active

645

user_datastore.put(user)

646

647

user_datastore.commit()

648

flash(f'User {user.email} {"activated" if user.active else "deactivated"}')

649

650

return redirect(url_for('list_users'))

651

```

652

653

### Role and Permission Management

654

655

```python

656

@app.route('/admin/setup-roles')

657

def setup_roles():

658

"""Initialize default roles and permissions."""

659

660

# Create roles if they don't exist

661

roles_to_create = [

662

('admin', 'Administrator with full access'),

663

('moderator', 'Content moderator'),

664

('user', 'Regular user'),

665

]

666

667

for role_name, description in roles_to_create:

668

if not user_datastore.find_role(role_name):

669

user_datastore.create_role(

670

name=role_name,

671

description=description

672

)

673

674

user_datastore.commit()

675

flash('Default roles created')

676

return redirect(url_for('admin_dashboard'))

677

678

@app.route('/admin/assign-role/<int:user_id>/<role_name>')

679

def assign_role(user_id, role_name):

680

"""Assign role to user."""

681

user = user_datastore.get_user(user_id)

682

role = user_datastore.find_role(role_name)

683

684

if user and role:

685

if user_datastore.add_role_to_user(user, role):

686

user_datastore.commit()

687

flash(f'Role {role_name} assigned to {user.email}')

688

else:

689

flash(f'{user.email} already has role {role_name}')

690

else:

691

flash('User or role not found')

692

693

return redirect(url_for('list_users'))

694

```

695

696

### Database Migration Helpers

697

698

```python

699

def migrate_user_data():

700

"""Example migration function for updating user data."""

701

702

# Add new field to existing users (example)

703

for user in user_datastore.user_model.query.all():

704

if not hasattr(user, 'created_at') or user.created_at is None:

705

user.created_at = datetime.utcnow()

706

user_datastore.put(user)

707

708

user_datastore.commit()

709

710

def cleanup_inactive_users():

711

"""Clean up old inactive user accounts."""

712

from datetime import datetime, timedelta

713

714

cutoff_date = datetime.utcnow() - timedelta(days=365)

715

716

# Find inactive users older than cutoff

717

if hasattr(user_datastore, 'find_user'):

718

# Use datastore method if available

719

inactive_users = user_datastore.user_model.query.filter(

720

user_datastore.user_model.active == False,

721

user_datastore.user_model.confirmed_at < cutoff_date

722

).all()

723

else:

724

# Manual query for other datastores

725

inactive_users = []

726

727

for user in inactive_users:

728

print(f"Would delete inactive user: {user.email}")

729

# user_datastore.delete_user(user) # Uncomment to actually delete

730

731

# user_datastore.commit() # Uncomment when ready to commit changes

732

```

733

734

## Datastore Configuration

735

736

### Connection Configuration

737

738

```python

739

# SQLAlchemy configurations

740

app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:pass@localhost/mydb'

741

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

742

app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {

743

'pool_size': 10,

744

'pool_recycle': 3600,

745

'pool_pre_ping': True

746

}

747

748

# MongoEngine configurations

749

app.config['MONGODB_SETTINGS'] = {

750

'db': 'mydatabase',

751

'host': 'localhost',

752

'port': 27017,

753

'username': 'myuser',

754

'password': 'mypassword',

755

'authentication_source': 'admin'

756

}

757

758

# Peewee configurations (handled in code)

759

database_config = {

760

'engine': 'peewee.PostgresqlDatabase',

761

'name': 'mydatabase',

762

'user': 'myuser',

763

'password': 'mypassword',

764

'host': 'localhost',

765

'port': 5432

766

}

767

```

768

769

### Model Configuration

770

771

```python

772

# Configure Flask-Security model field names

773

app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = ['email', 'username']

774

app.config['SECURITY_USERNAME_ENABLE'] = True

775

app.config['SECURITY_USERNAME_REQUIRED'] = False

776

777

# Password configuration

778

app.config['SECURITY_PASSWORD_HASH'] = 'bcrypt'

779

app.config['SECURITY_PASSWORD_SALT'] = 'your-password-salt'

780

781

# Role configuration

782

app.config['SECURITY_ROLE_HIERARCHY'] = {

783

'admin': ['moderator', 'user'],

784

'moderator': ['user']

785

}

786

```

787

788

## Performance Considerations

789

790

### Query Optimization

791

- Use appropriate database indexes on frequently queried fields

792

- Implement connection pooling for high-traffic applications

793

- Consider read replicas for read-heavy workloads

794

- Use database-specific optimizations (e.g., PostgreSQL partial indexes)

795

796

### Caching Strategies

797

- Implement user session caching to reduce database lookups

798

- Cache role and permission data for authorization checks

799

- Use Redis or Memcached for distributed caching

800

- Implement query result caching for expensive operations

801

802

### Scalability

803

- Design models to support horizontal scaling

804

- Use database sharding for very large user bases

805

- Implement proper connection pooling and timeout settings

806

- Monitor database performance and query patterns