claude-code-templates 1.14.12 → 1.14.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-claude-config.js +1 -0
- package/package.json +1 -2
- package/src/file-operations.js +239 -36
- package/src/index.js +347 -9
- package/templates/common/.claude/commands/git-workflow.md +0 -239
- package/templates/common/.claude/commands/project-setup.md +0 -316
- package/templates/common/.mcp.json +0 -41
- package/templates/common/CLAUDE.md +0 -109
- package/templates/common/README.md +0 -96
- package/templates/go/.mcp.json +0 -78
- package/templates/go/README.md +0 -25
- package/templates/javascript-typescript/.claude/commands/api-endpoint.md +0 -51
- package/templates/javascript-typescript/.claude/commands/debug.md +0 -52
- package/templates/javascript-typescript/.claude/commands/lint.md +0 -48
- package/templates/javascript-typescript/.claude/commands/npm-scripts.md +0 -48
- package/templates/javascript-typescript/.claude/commands/refactor.md +0 -55
- package/templates/javascript-typescript/.claude/commands/test.md +0 -61
- package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +0 -51
- package/templates/javascript-typescript/.claude/settings.json +0 -142
- package/templates/javascript-typescript/.mcp.json +0 -80
- package/templates/javascript-typescript/CLAUDE.md +0 -185
- package/templates/javascript-typescript/README.md +0 -259
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +0 -63
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +0 -62
- package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +0 -46
- package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +0 -56
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -61
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -57
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -102
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -29
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -44
- package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +0 -45
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -81
- package/templates/javascript-typescript/examples/react-app/agents/react-performance-optimization.md +0 -530
- package/templates/javascript-typescript/examples/react-app/agents/react-state-management.md +0 -295
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +0 -46
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +0 -51
- package/templates/python/.claude/commands/lint.md +0 -111
- package/templates/python/.claude/commands/test.md +0 -73
- package/templates/python/.claude/settings.json +0 -153
- package/templates/python/.mcp.json +0 -78
- package/templates/python/CLAUDE.md +0 -276
- package/templates/python/examples/django-app/.claude/commands/admin.md +0 -264
- package/templates/python/examples/django-app/.claude/commands/django-model.md +0 -124
- package/templates/python/examples/django-app/.claude/commands/views.md +0 -222
- package/templates/python/examples/django-app/CLAUDE.md +0 -313
- package/templates/python/examples/django-app/agents/django-api-security.md +0 -642
- package/templates/python/examples/django-app/agents/django-database-optimization.md +0 -752
- package/templates/python/examples/fastapi-app/.claude/commands/api-endpoints.md +0 -513
- package/templates/python/examples/fastapi-app/.claude/commands/auth.md +0 -775
- package/templates/python/examples/fastapi-app/.claude/commands/database.md +0 -657
- package/templates/python/examples/fastapi-app/.claude/commands/deployment.md +0 -160
- package/templates/python/examples/fastapi-app/.claude/commands/testing.md +0 -927
- package/templates/python/examples/fastapi-app/CLAUDE.md +0 -229
- package/templates/python/examples/flask-app/.claude/commands/app-factory.md +0 -384
- package/templates/python/examples/flask-app/.claude/commands/blueprint.md +0 -243
- package/templates/python/examples/flask-app/.claude/commands/database.md +0 -410
- package/templates/python/examples/flask-app/.claude/commands/deployment.md +0 -620
- package/templates/python/examples/flask-app/.claude/commands/flask-route.md +0 -217
- package/templates/python/examples/flask-app/.claude/commands/testing.md +0 -559
- package/templates/python/examples/flask-app/CLAUDE.md +0 -391
- package/templates/ruby/.claude/commands/model.md +0 -360
- package/templates/ruby/.claude/commands/test.md +0 -480
- package/templates/ruby/.claude/settings.json +0 -146
- package/templates/ruby/.mcp.json +0 -83
- package/templates/ruby/CLAUDE.md +0 -284
- package/templates/ruby/examples/rails-app/.claude/commands/authentication.md +0 -490
- package/templates/ruby/examples/rails-app/CLAUDE.md +0 -376
- package/templates/rust/.mcp.json +0 -78
- package/templates/rust/README.md +0 -26
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
# Flask Blueprint Generator
|
|
2
|
-
|
|
3
|
-
Create organized Flask blueprints for modular application structure.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Create a new blueprint
|
|
9
|
-
flask create-blueprint users
|
|
10
|
-
flask create-blueprint api/v1
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Blueprint Structure
|
|
14
|
-
|
|
15
|
-
Generates a complete blueprint with:
|
|
16
|
-
- Routes and view functions
|
|
17
|
-
- Error handlers
|
|
18
|
-
- Template folder structure
|
|
19
|
-
- Static file organization
|
|
20
|
-
|
|
21
|
-
## Example Blueprint
|
|
22
|
-
|
|
23
|
-
```python
|
|
24
|
-
# app/blueprints/users/__init__.py
|
|
25
|
-
from flask import Blueprint
|
|
26
|
-
|
|
27
|
-
users_bp = Blueprint(
|
|
28
|
-
'users',
|
|
29
|
-
__name__,
|
|
30
|
-
url_prefix='/users',
|
|
31
|
-
template_folder='templates',
|
|
32
|
-
static_folder='static'
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
from . import routes, models
|
|
36
|
-
|
|
37
|
-
# app/blueprints/users/routes.py
|
|
38
|
-
from flask import render_template, request, redirect, url_for, flash
|
|
39
|
-
from . import users_bp
|
|
40
|
-
from .models import User
|
|
41
|
-
from .forms import UserForm
|
|
42
|
-
|
|
43
|
-
@users_bp.route('/')
|
|
44
|
-
def index():
|
|
45
|
-
"""List all users."""
|
|
46
|
-
users = User.query.all()
|
|
47
|
-
return render_template('users/index.html', users=users)
|
|
48
|
-
|
|
49
|
-
@users_bp.route('/create', methods=['GET', 'POST'])
|
|
50
|
-
def create():
|
|
51
|
-
"""Create a new user."""
|
|
52
|
-
form = UserForm()
|
|
53
|
-
if form.validate_on_submit():
|
|
54
|
-
user = User(
|
|
55
|
-
username=form.username.data,
|
|
56
|
-
email=form.email.data
|
|
57
|
-
)
|
|
58
|
-
user.save()
|
|
59
|
-
flash('User created successfully!', 'success')
|
|
60
|
-
return redirect(url_for('users.index'))
|
|
61
|
-
return render_template('users/create.html', form=form)
|
|
62
|
-
|
|
63
|
-
@users_bp.route('/<int:user_id>')
|
|
64
|
-
def detail(user_id):
|
|
65
|
-
"""Show user details."""
|
|
66
|
-
user = User.query.get_or_404(user_id)
|
|
67
|
-
return render_template('users/detail.html', user=user)
|
|
68
|
-
|
|
69
|
-
@users_bp.route('/<int:user_id>/edit', methods=['GET', 'POST'])
|
|
70
|
-
def edit(user_id):
|
|
71
|
-
"""Edit an existing user."""
|
|
72
|
-
user = User.query.get_or_404(user_id)
|
|
73
|
-
form = UserForm(obj=user)
|
|
74
|
-
if form.validate_on_submit():
|
|
75
|
-
user.username = form.username.data
|
|
76
|
-
user.email = form.email.data
|
|
77
|
-
user.save()
|
|
78
|
-
flash('User updated successfully!', 'success')
|
|
79
|
-
return redirect(url_for('users.detail', user_id=user.id))
|
|
80
|
-
return render_template('users/edit.html', form=form, user=user)
|
|
81
|
-
|
|
82
|
-
@users_bp.route('/<int:user_id>/delete', methods=['POST'])
|
|
83
|
-
def delete(user_id):
|
|
84
|
-
"""Delete a user."""
|
|
85
|
-
user = User.query.get_or_404(user_id)
|
|
86
|
-
user.delete()
|
|
87
|
-
flash('User deleted successfully!', 'success')
|
|
88
|
-
return redirect(url_for('users.index'))
|
|
89
|
-
|
|
90
|
-
# Error handlers
|
|
91
|
-
@users_bp.errorhandler(404)
|
|
92
|
-
def not_found(error):
|
|
93
|
-
return render_template('users/404.html'), 404
|
|
94
|
-
|
|
95
|
-
@users_bp.errorhandler(500)
|
|
96
|
-
def internal_error(error):
|
|
97
|
-
return render_template('users/500.html'), 500
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Blueprint Models
|
|
101
|
-
|
|
102
|
-
```python
|
|
103
|
-
# app/blueprints/users/models.py
|
|
104
|
-
from app.extensions import db
|
|
105
|
-
from werkzeug.security import generate_password_hash, check_password_hash
|
|
106
|
-
from datetime import datetime
|
|
107
|
-
|
|
108
|
-
class User(db.Model):
|
|
109
|
-
"""User model."""
|
|
110
|
-
__tablename__ = 'users'
|
|
111
|
-
|
|
112
|
-
id = db.Column(db.Integer, primary_key=True)
|
|
113
|
-
username = db.Column(db.String(80), unique=True, nullable=False)
|
|
114
|
-
email = db.Column(db.String(120), unique=True, nullable=False)
|
|
115
|
-
password_hash = db.Column(db.String(255), nullable=False)
|
|
116
|
-
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
117
|
-
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
118
|
-
is_active = db.Column(db.Boolean, default=True)
|
|
119
|
-
|
|
120
|
-
def __repr__(self):
|
|
121
|
-
return f'<User {self.username}>'
|
|
122
|
-
|
|
123
|
-
def set_password(self, password):
|
|
124
|
-
"""Set password hash."""
|
|
125
|
-
self.password_hash = generate_password_hash(password)
|
|
126
|
-
|
|
127
|
-
def check_password(self, password):
|
|
128
|
-
"""Check password hash."""
|
|
129
|
-
return check_password_hash(self.password_hash, password)
|
|
130
|
-
|
|
131
|
-
def save(self):
|
|
132
|
-
"""Save user to database."""
|
|
133
|
-
db.session.add(self)
|
|
134
|
-
db.session.commit()
|
|
135
|
-
|
|
136
|
-
def delete(self):
|
|
137
|
-
"""Delete user from database."""
|
|
138
|
-
db.session.delete(self)
|
|
139
|
-
db.session.commit()
|
|
140
|
-
|
|
141
|
-
def to_dict(self):
|
|
142
|
-
"""Convert to dictionary."""
|
|
143
|
-
return {
|
|
144
|
-
'id': self.id,
|
|
145
|
-
'username': self.username,
|
|
146
|
-
'email': self.email,
|
|
147
|
-
'created_at': self.created_at.isoformat(),
|
|
148
|
-
'is_active': self.is_active
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Blueprint Forms
|
|
153
|
-
|
|
154
|
-
```python
|
|
155
|
-
# app/blueprints/users/forms.py
|
|
156
|
-
from flask_wtf import FlaskForm
|
|
157
|
-
from wtforms import StringField, EmailField, PasswordField, BooleanField
|
|
158
|
-
from wtforms.validators import DataRequired, Email, Length, EqualTo
|
|
159
|
-
from .models import User
|
|
160
|
-
|
|
161
|
-
class UserForm(FlaskForm):
|
|
162
|
-
"""User creation/edit form."""
|
|
163
|
-
username = StringField(
|
|
164
|
-
'Username',
|
|
165
|
-
validators=[
|
|
166
|
-
DataRequired(),
|
|
167
|
-
Length(min=3, max=80)
|
|
168
|
-
]
|
|
169
|
-
)
|
|
170
|
-
email = EmailField(
|
|
171
|
-
'Email',
|
|
172
|
-
validators=[
|
|
173
|
-
DataRequired(),
|
|
174
|
-
Email(),
|
|
175
|
-
Length(max=120)
|
|
176
|
-
]
|
|
177
|
-
)
|
|
178
|
-
password = PasswordField(
|
|
179
|
-
'Password',
|
|
180
|
-
validators=[
|
|
181
|
-
DataRequired(),
|
|
182
|
-
Length(min=8)
|
|
183
|
-
]
|
|
184
|
-
)
|
|
185
|
-
confirm_password = PasswordField(
|
|
186
|
-
'Confirm Password',
|
|
187
|
-
validators=[
|
|
188
|
-
DataRequired(),
|
|
189
|
-
EqualTo('password', message='Passwords must match')
|
|
190
|
-
]
|
|
191
|
-
)
|
|
192
|
-
is_active = BooleanField('Active')
|
|
193
|
-
|
|
194
|
-
def validate_username(self, field):
|
|
195
|
-
"""Validate username uniqueness."""
|
|
196
|
-
if User.query.filter_by(username=field.data).first():
|
|
197
|
-
raise ValidationError('Username already exists.')
|
|
198
|
-
|
|
199
|
-
def validate_email(self, field):
|
|
200
|
-
"""Validate email uniqueness."""
|
|
201
|
-
if User.query.filter_by(email=field.data).first():
|
|
202
|
-
raise ValidationError('Email already registered.')
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Registration in Main App
|
|
206
|
-
|
|
207
|
-
```python
|
|
208
|
-
# app/__init__.py
|
|
209
|
-
from flask import Flask
|
|
210
|
-
from app.blueprints.users import users_bp
|
|
211
|
-
|
|
212
|
-
def create_app():
|
|
213
|
-
app = Flask(__name__)
|
|
214
|
-
|
|
215
|
-
# Register blueprints
|
|
216
|
-
app.register_blueprint(users_bp)
|
|
217
|
-
|
|
218
|
-
return app
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## Template Structure
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
templates/
|
|
225
|
-
├── base.html
|
|
226
|
-
└── users/
|
|
227
|
-
├── index.html
|
|
228
|
-
├── create.html
|
|
229
|
-
├── detail.html
|
|
230
|
-
├── edit.html
|
|
231
|
-
├── 404.html
|
|
232
|
-
└── 500.html
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Best Practices
|
|
236
|
-
|
|
237
|
-
- Use blueprints to organize related functionality
|
|
238
|
-
- Keep models, forms, and routes in separate files
|
|
239
|
-
- Implement proper error handling
|
|
240
|
-
- Use URL prefixes for namespacing
|
|
241
|
-
- Follow RESTful routing conventions
|
|
242
|
-
- Include comprehensive docstrings
|
|
243
|
-
- Add form validation and CSRF protection
|
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
# Flask Database Management
|
|
2
|
-
|
|
3
|
-
Complete database setup and management for Flask applications using SQLAlchemy.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Initialize database
|
|
9
|
-
flask db init
|
|
10
|
-
|
|
11
|
-
# Create migration
|
|
12
|
-
flask db migrate -m "Initial migration"
|
|
13
|
-
|
|
14
|
-
# Apply migrations
|
|
15
|
-
flask db upgrade
|
|
16
|
-
|
|
17
|
-
# Downgrade migration
|
|
18
|
-
flask db downgrade
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Database Configuration
|
|
22
|
-
|
|
23
|
-
```python
|
|
24
|
-
# config.py
|
|
25
|
-
import os
|
|
26
|
-
from urllib.parse import quote_plus
|
|
27
|
-
|
|
28
|
-
class Config:
|
|
29
|
-
"""Base configuration."""
|
|
30
|
-
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
|
|
31
|
-
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
32
|
-
SQLALCHEMY_RECORD_QUERIES = True
|
|
33
|
-
|
|
34
|
-
class DevelopmentConfig(Config):
|
|
35
|
-
"""Development configuration."""
|
|
36
|
-
DEBUG = True
|
|
37
|
-
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
|
|
38
|
-
'sqlite:///app.db'
|
|
39
|
-
|
|
40
|
-
class ProductionConfig(Config):
|
|
41
|
-
"""Production configuration."""
|
|
42
|
-
DEBUG = False
|
|
43
|
-
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
|
|
44
|
-
f"postgresql://{os.environ.get('DB_USER')}:{quote_plus(os.environ.get('DB_PASSWORD'))}@" \
|
|
45
|
-
f"{os.environ.get('DB_HOST')}:{os.environ.get('DB_PORT', '5432')}/{os.environ.get('DB_NAME')}"
|
|
46
|
-
|
|
47
|
-
class TestingConfig(Config):
|
|
48
|
-
"""Testing configuration."""
|
|
49
|
-
TESTING = True
|
|
50
|
-
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
|
|
51
|
-
WTF_CSRF_ENABLED = False
|
|
52
|
-
|
|
53
|
-
config = {
|
|
54
|
-
'development': DevelopmentConfig,
|
|
55
|
-
'production': ProductionConfig,
|
|
56
|
-
'testing': TestingConfig,
|
|
57
|
-
'default': DevelopmentConfig
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Database Extensions
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
# app/extensions.py
|
|
65
|
-
from flask_sqlalchemy import SQLAlchemy
|
|
66
|
-
from flask_migrate import Migrate
|
|
67
|
-
from flask_login import LoginManager
|
|
68
|
-
from flask_wtf.csrf import CSRFProtect
|
|
69
|
-
from flask_caching import Cache
|
|
70
|
-
from flask_limiter import Limiter
|
|
71
|
-
from flask_limiter.util import get_remote_address
|
|
72
|
-
|
|
73
|
-
# Initialize extensions
|
|
74
|
-
db = SQLAlchemy()
|
|
75
|
-
migrate = Migrate()
|
|
76
|
-
login_manager = LoginManager()
|
|
77
|
-
csrf = CSRFProtect()
|
|
78
|
-
cache = Cache()
|
|
79
|
-
limiter = Limiter(key_func=get_remote_address)
|
|
80
|
-
|
|
81
|
-
def init_extensions(app):
|
|
82
|
-
"""Initialize Flask extensions."""
|
|
83
|
-
db.init_app(app)
|
|
84
|
-
migrate.init_app(app, db)
|
|
85
|
-
login_manager.init_app(app)
|
|
86
|
-
csrf.init_app(app)
|
|
87
|
-
cache.init_app(app)
|
|
88
|
-
limiter.init_app(app)
|
|
89
|
-
|
|
90
|
-
# Configure login manager
|
|
91
|
-
login_manager.login_view = 'auth.login'
|
|
92
|
-
login_manager.login_message = 'Please log in to access this page.'
|
|
93
|
-
login_manager.login_message_category = 'info'
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Base Model
|
|
97
|
-
|
|
98
|
-
```python
|
|
99
|
-
# app/models/base.py
|
|
100
|
-
from app.extensions import db
|
|
101
|
-
from datetime import datetime
|
|
102
|
-
from sqlalchemy.ext.declarative import declared_attr
|
|
103
|
-
|
|
104
|
-
class TimestampMixin:
|
|
105
|
-
"""Add timestamp fields to model."""
|
|
106
|
-
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
|
107
|
-
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
|
108
|
-
|
|
109
|
-
class BaseModel(db.Model, TimestampMixin):
|
|
110
|
-
"""Base model with common functionality."""
|
|
111
|
-
__abstract__ = True
|
|
112
|
-
|
|
113
|
-
id = db.Column(db.Integer, primary_key=True)
|
|
114
|
-
|
|
115
|
-
@declared_attr
|
|
116
|
-
def __tablename__(cls):
|
|
117
|
-
return cls.__name__.lower()
|
|
118
|
-
|
|
119
|
-
def save(self, commit=True):
|
|
120
|
-
"""Save model to database."""
|
|
121
|
-
db.session.add(self)
|
|
122
|
-
if commit:
|
|
123
|
-
db.session.commit()
|
|
124
|
-
return self
|
|
125
|
-
|
|
126
|
-
def delete(self, commit=True):
|
|
127
|
-
"""Delete model from database."""
|
|
128
|
-
db.session.delete(self)
|
|
129
|
-
if commit:
|
|
130
|
-
db.session.commit()
|
|
131
|
-
|
|
132
|
-
def update(self, **kwargs):
|
|
133
|
-
"""Update model attributes."""
|
|
134
|
-
for key, value in kwargs.items():
|
|
135
|
-
if hasattr(self, key):
|
|
136
|
-
setattr(self, key, value)
|
|
137
|
-
return self.save()
|
|
138
|
-
|
|
139
|
-
def to_dict(self, exclude=None):
|
|
140
|
-
"""Convert model to dictionary."""
|
|
141
|
-
exclude = exclude or []
|
|
142
|
-
return {
|
|
143
|
-
column.name: getattr(self, column.name)
|
|
144
|
-
for column in self.__table__.columns
|
|
145
|
-
if column.name not in exclude
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
@classmethod
|
|
149
|
-
def get_or_404(cls, id):
|
|
150
|
-
"""Get model by ID or raise 404."""
|
|
151
|
-
return cls.query.get_or_404(id)
|
|
152
|
-
|
|
153
|
-
@classmethod
|
|
154
|
-
def create(cls, **kwargs):
|
|
155
|
-
"""Create new model instance."""
|
|
156
|
-
instance = cls(**kwargs)
|
|
157
|
-
return instance.save()
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Example Models
|
|
161
|
-
|
|
162
|
-
```python
|
|
163
|
-
# app/models/user.py
|
|
164
|
-
from app.extensions import db
|
|
165
|
-
from app.models.base import BaseModel
|
|
166
|
-
from werkzeug.security import generate_password_hash, check_password_hash
|
|
167
|
-
from flask_login import UserMixin
|
|
168
|
-
|
|
169
|
-
class User(UserMixin, BaseModel):
|
|
170
|
-
"""User model."""
|
|
171
|
-
__tablename__ = 'users'
|
|
172
|
-
|
|
173
|
-
username = db.Column(db.String(80), unique=True, nullable=False, index=True)
|
|
174
|
-
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
|
|
175
|
-
password_hash = db.Column(db.String(255), nullable=False)
|
|
176
|
-
first_name = db.Column(db.String(50), nullable=False)
|
|
177
|
-
last_name = db.Column(db.String(50), nullable=False)
|
|
178
|
-
is_active = db.Column(db.Boolean, default=True, nullable=False)
|
|
179
|
-
is_admin = db.Column(db.Boolean, default=False, nullable=False)
|
|
180
|
-
last_login = db.Column(db.DateTime)
|
|
181
|
-
|
|
182
|
-
# Relationships
|
|
183
|
-
posts = db.relationship('Post', backref='author', lazy='dynamic', cascade='all, delete-orphan')
|
|
184
|
-
|
|
185
|
-
def __repr__(self):
|
|
186
|
-
return f'<User {self.username}>'
|
|
187
|
-
|
|
188
|
-
def set_password(self, password):
|
|
189
|
-
"""Set password hash."""
|
|
190
|
-
self.password_hash = generate_password_hash(password)
|
|
191
|
-
|
|
192
|
-
def check_password(self, password):
|
|
193
|
-
"""Check password hash."""
|
|
194
|
-
return check_password_hash(self.password_hash, password)
|
|
195
|
-
|
|
196
|
-
@property
|
|
197
|
-
def full_name(self):
|
|
198
|
-
"""Get user's full name."""
|
|
199
|
-
return f"{self.first_name} {self.last_name}"
|
|
200
|
-
|
|
201
|
-
def to_dict(self, exclude=None):
|
|
202
|
-
"""Convert to dictionary excluding sensitive data."""
|
|
203
|
-
exclude = exclude or ['password_hash']
|
|
204
|
-
return super().to_dict(exclude=exclude)
|
|
205
|
-
|
|
206
|
-
# app/models/post.py
|
|
207
|
-
class Post(BaseModel):
|
|
208
|
-
"""Blog post model."""
|
|
209
|
-
__tablename__ = 'posts'
|
|
210
|
-
|
|
211
|
-
title = db.Column(db.String(200), nullable=False)
|
|
212
|
-
content = db.Column(db.Text, nullable=False)
|
|
213
|
-
slug = db.Column(db.String(200), unique=True, nullable=False, index=True)
|
|
214
|
-
status = db.Column(db.String(20), default='draft', nullable=False)
|
|
215
|
-
published_at = db.Column(db.DateTime)
|
|
216
|
-
|
|
217
|
-
# Foreign keys
|
|
218
|
-
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
|
219
|
-
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
|
|
220
|
-
|
|
221
|
-
# Relationships
|
|
222
|
-
category = db.relationship('Category', backref='posts')
|
|
223
|
-
tags = db.relationship('Tag', secondary='post_tags', backref='posts')
|
|
224
|
-
|
|
225
|
-
def __repr__(self):
|
|
226
|
-
return f'<Post {self.title}>'
|
|
227
|
-
|
|
228
|
-
@property
|
|
229
|
-
def is_published(self):
|
|
230
|
-
"""Check if post is published."""
|
|
231
|
-
return self.status == 'published' and self.published_at is not None
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## Database CLI Commands
|
|
235
|
-
|
|
236
|
-
```python
|
|
237
|
-
# app/cli.py
|
|
238
|
-
import click
|
|
239
|
-
from flask import current_app
|
|
240
|
-
from flask.cli import with_appcontext
|
|
241
|
-
from app.extensions import db
|
|
242
|
-
from app.models import User, Post, Category
|
|
243
|
-
|
|
244
|
-
@click.command()
|
|
245
|
-
@with_appcontext
|
|
246
|
-
def init_db():
|
|
247
|
-
"""Initialize database."""
|
|
248
|
-
db.create_all()
|
|
249
|
-
click.echo('Database initialized.')
|
|
250
|
-
|
|
251
|
-
@click.command()
|
|
252
|
-
@with_appcontext
|
|
253
|
-
def seed_db():
|
|
254
|
-
"""Seed database with sample data."""
|
|
255
|
-
# Create admin user
|
|
256
|
-
admin = User(
|
|
257
|
-
username='admin',
|
|
258
|
-
email='admin@example.com',
|
|
259
|
-
first_name='Admin',
|
|
260
|
-
last_name='User',
|
|
261
|
-
is_admin=True
|
|
262
|
-
)
|
|
263
|
-
admin.set_password('admin123')
|
|
264
|
-
admin.save()
|
|
265
|
-
|
|
266
|
-
# Create sample category
|
|
267
|
-
category = Category(
|
|
268
|
-
name='Technology',
|
|
269
|
-
description='Tech-related posts'
|
|
270
|
-
)
|
|
271
|
-
category.save()
|
|
272
|
-
|
|
273
|
-
# Create sample post
|
|
274
|
-
post = Post(
|
|
275
|
-
title='Welcome to Flask',
|
|
276
|
-
content='This is a sample blog post.',
|
|
277
|
-
slug='welcome-to-flask',
|
|
278
|
-
status='published',
|
|
279
|
-
user_id=admin.id,
|
|
280
|
-
category_id=category.id
|
|
281
|
-
)
|
|
282
|
-
post.save()
|
|
283
|
-
|
|
284
|
-
click.echo('Database seeded with sample data.')
|
|
285
|
-
|
|
286
|
-
@click.command()
|
|
287
|
-
@with_appcontext
|
|
288
|
-
def reset_db():
|
|
289
|
-
"""Reset database."""
|
|
290
|
-
if click.confirm('Are you sure you want to reset the database?'):
|
|
291
|
-
db.drop_all()
|
|
292
|
-
db.create_all()
|
|
293
|
-
click.echo('Database reset.')
|
|
294
|
-
|
|
295
|
-
def init_commands(app):
|
|
296
|
-
"""Register CLI commands."""
|
|
297
|
-
app.cli.add_command(init_db)
|
|
298
|
-
app.cli.add_command(seed_db)
|
|
299
|
-
app.cli.add_command(reset_db)
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## Connection Pooling
|
|
303
|
-
|
|
304
|
-
```python
|
|
305
|
-
# app/database.py
|
|
306
|
-
from sqlalchemy import create_engine
|
|
307
|
-
from sqlalchemy.pool import QueuePool
|
|
308
|
-
|
|
309
|
-
def configure_database(app):
|
|
310
|
-
"""Configure database with connection pooling."""
|
|
311
|
-
if app.config.get('SQLALCHEMY_DATABASE_URI', '').startswith('postgresql'):
|
|
312
|
-
# PostgreSQL configuration
|
|
313
|
-
engine = create_engine(
|
|
314
|
-
app.config['SQLALCHEMY_DATABASE_URI'],
|
|
315
|
-
poolclass=QueuePool,
|
|
316
|
-
pool_size=10,
|
|
317
|
-
max_overflow=20,
|
|
318
|
-
pool_recycle=3600,
|
|
319
|
-
pool_pre_ping=True
|
|
320
|
-
)
|
|
321
|
-
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
|
|
322
|
-
'pool_size': 10,
|
|
323
|
-
'max_overflow': 20,
|
|
324
|
-
'pool_recycle': 3600,
|
|
325
|
-
'pool_pre_ping': True
|
|
326
|
-
}
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
## Database Utilities
|
|
330
|
-
|
|
331
|
-
```python
|
|
332
|
-
# app/utils/database.py
|
|
333
|
-
from app.extensions import db
|
|
334
|
-
from sqlalchemy import text
|
|
335
|
-
from flask import current_app
|
|
336
|
-
|
|
337
|
-
def execute_sql(sql, params=None):
|
|
338
|
-
"""Execute raw SQL query."""
|
|
339
|
-
with db.engine.connect() as conn:
|
|
340
|
-
result = conn.execute(text(sql), params or {})
|
|
341
|
-
return result.fetchall()
|
|
342
|
-
|
|
343
|
-
def backup_database():
|
|
344
|
-
"""Create database backup."""
|
|
345
|
-
# Implementation depends on database type
|
|
346
|
-
pass
|
|
347
|
-
|
|
348
|
-
def check_database_health():
|
|
349
|
-
"""Check database connection health."""
|
|
350
|
-
try:
|
|
351
|
-
db.session.execute(text('SELECT 1'))
|
|
352
|
-
return True
|
|
353
|
-
except Exception as e:
|
|
354
|
-
current_app.logger.error(f'Database health check failed: {e}')
|
|
355
|
-
return False
|
|
356
|
-
|
|
357
|
-
def get_table_info(table_name):
|
|
358
|
-
"""Get table information."""
|
|
359
|
-
inspector = db.inspect(db.engine)
|
|
360
|
-
return {
|
|
361
|
-
'columns': inspector.get_columns(table_name),
|
|
362
|
-
'indexes': inspector.get_indexes(table_name),
|
|
363
|
-
'foreign_keys': inspector.get_foreign_keys(table_name)
|
|
364
|
-
}
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
## Testing Database
|
|
368
|
-
|
|
369
|
-
```python
|
|
370
|
-
# tests/conftest.py
|
|
371
|
-
import pytest
|
|
372
|
-
from app import create_app
|
|
373
|
-
from app.extensions import db
|
|
374
|
-
from app.models import User
|
|
375
|
-
|
|
376
|
-
@pytest.fixture(scope='session')
|
|
377
|
-
def app():
|
|
378
|
-
"""Create test app."""
|
|
379
|
-
app = create_app('testing')
|
|
380
|
-
with app.app_context():
|
|
381
|
-
db.create_all()
|
|
382
|
-
yield app
|
|
383
|
-
db.drop_all()
|
|
384
|
-
|
|
385
|
-
@pytest.fixture
|
|
386
|
-
def client(app):
|
|
387
|
-
"""Create test client."""
|
|
388
|
-
return app.test_client()
|
|
389
|
-
|
|
390
|
-
@pytest.fixture
|
|
391
|
-
def db_session(app):
|
|
392
|
-
"""Create database session for testing."""
|
|
393
|
-
with app.app_context():
|
|
394
|
-
db.session.begin()
|
|
395
|
-
yield db.session
|
|
396
|
-
db.session.rollback()
|
|
397
|
-
|
|
398
|
-
@pytest.fixture
|
|
399
|
-
def user(db_session):
|
|
400
|
-
"""Create test user."""
|
|
401
|
-
user = User(
|
|
402
|
-
username='testuser',
|
|
403
|
-
email='test@example.com',
|
|
404
|
-
first_name='Test',
|
|
405
|
-
last_name='User'
|
|
406
|
-
)
|
|
407
|
-
user.set_password('testpass')
|
|
408
|
-
user.save()
|
|
409
|
-
return user
|
|
410
|
-
```
|