在Django中使用外部只读SQL Server数据库的规范实现问询
用Django规范方式连接只读外部SQL Server数据库
1. 多数据库配置(settings.py)
首先在settings.py中配置外部SQL Server数据库(保留原主数据库的同时添加新配置),使用Django官方支持的SQL Server后端:
import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent DATABASES = { 'default': { # 你的主数据库配置(比如默认的SQLite) 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', }, 'external_mssql': { 'ENGINE': 'django.db.backends.sqlserver', 'NAME': os.getenv('EXTERNAL_DB_NAME'), 'USER': os.getenv('EXTERNAL_DB_USER'), 'PASSWORD': os.getenv('EXTERNAL_DB_PWD'), 'HOST': os.getenv('EXTERNAL_DB_HOST'), 'PORT': '1433', # SQL Server默认端口,根据实际修改 'OPTIONS': { 'driver': 'ODBC Driver 17 for SQL Server', # 匹配你的ODBC驱动版本 }, } }
注意:需要先安装依赖包:
pip install pyodbc django-mssql-backend,并确保系统已安装对应版本的ODBC驱动。
2. 处理数百张表:两种实现方案
方案A:自动生成ORM模型(适合需ORM操作的场景)
如果想用Django ORM进行查询(避免写原生SQL),可以用inspectdb命令自动生成所有外部表的模型:
# 生成模型文件到指定app python manage.py inspectdb --database=external_mssql > your_app/models_external.py
将生成的模型整合到models.py或单独文件中,需给每个模型添加managed = False(禁止Django修改表结构):
from django.db import models class ExternalSales(models.Model): sale_id = models.IntegerField(primary_key=True) product_name = models.CharField(max_length=255) sale_date = models.DateTimeField() # 其他字段由inspectdb自动生成 class Meta: managed = False db_table = 'sales' # 对应数据库实际表名 app_label = 'your_app'
之后就可以用ORM查询,还能快速转成Pandas DataFrame:
from .models import ExternalSales import pandas as pd # 从外部库查询数据 queryset = ExternalSales.objects.using('external_mssql').filter(sale_date__year=2024) # 转DataFrame df = pd.DataFrame(queryset.values())
方案B:用Django连接执行原生SQL(适合灵活查询)
如果不需要定义所有表的模型,直接用Django的数据库连接执行原生SQL,比手动写pyodbc更规范(复用配置、连接池):
# 在utils.py中封装函数 from django.db import connections import pandas as pd def sql_to_df(query): with connections['external_mssql'].cursor() as cursor: cursor.execute(query) # 获取列名 columns = [col[0] for col in cursor.description] # 获取数据 data = cursor.fetchall() return pd.DataFrame(data, columns=columns)
调用示例:df = sql_to_df("SELECT TOP 100 * FROM sales WHERE region='North'")
3. 确保数据库只读(双重保障)
核心:数据库用户权限
给SQL Server的连接用户仅授予SELECT权限,禁止INSERT/UPDATE/DELETE/ALTER等操作,这是最根本的只读保障(通过SQL Server管理工具配置)。
Django层面限制
- 模型设置
managed = False,防止Django尝试创建/修改表结构 - 自定义只读模型管理器,从代码层面禁止写操作:
class ReadOnlyManager(models.Manager): def create(self, **kwargs): raise NotImplementedError("该数据库为只读,禁止写入") def update(self, **kwargs): raise NotImplementedError("该数据库为只读,禁止更新") def delete(self): raise NotImplementedError("该数据库为只读,禁止删除") class ExternalSales(models.Model): # ...字段 objects = ReadOnlyManager() class Meta: managed = False db_table = 'sales'
4. views.py示例
ORM查询示例
from django.shortcuts import render from .models import ExternalSales import pandas as pd def sales_dashboard(request): # 从外部库查询 queryset = ExternalSales.objects.using('external_mssql').filter(region='North') df = pd.DataFrame(queryset.values()) # 转HTML表格传给模板 html_table = df.to_html(classes='table table-striped') return render(request, 'sales/dashboard.html', {'sales_table': html_table})
原生SQL查询示例
from django.shortcuts import render from .utils import sql_to_df def custom_report(request): query = """ SELECT region, COUNT(sale_id) as total_sales FROM sales WHERE sale_date BETWEEN '2024-01-01' AND '2024-06-30' GROUP BY region """ df = sql_to_df(query) html_table = df.to_html() return render(request, 'reports/custom.html', {'report_table': html_table})
对比原实现的优势
- 统一用
settings.py管理数据库配置,避免硬编码敏感信息(可结合环境变量) - 复用Django的连接池,提升性能
- 支持ORM和原生SQL两种方式,适配不同场景
- 更符合Django生态,便于团队维护和扩展
内容的提问来源于stack exchange,提问作者spg719
相关产品推荐
相关产品推荐

