Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo é um guia detalhado de migração para aplicações Django que passam de PostgreSQL (psycopg2ou psycopg) para SQL Server (mssql-django). Para uma visão geral da migração de qualquer base de dados, veja Migrar aplicações Django de outras bases de dados para o SQL Server.
Pré-requisitos
- Python 3.8 ou posterior
- Microsoft ODBC Driver 17 ou 18 para SQL Server. Ver Instalar mssql-django.
- SQL Server 2016 ou posterior, ou Base de Dados SQL do Azure
Mudar o backend da base de dados
Substitua a sua configuração do PostgreSQL em settings.py:
# Before (PostgreSQL)
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "mydb",
"USER": "myuser",
"PASSWORD": "mypassword",
"HOST": "localhost",
"PORT": "5432",
},
}
# After (SQL Server)
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "mydb",
"USER": "myuser",
"PASSWORD": "mypassword",
"HOST": "localhost",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
},
}
Atualização requirements.txt:
# Remove
# psycopg2-binary>=2.9
# or psycopg[binary]>=3.1
# Add
mssql-django>=1.5
Substitua funcionalidades de django.contrib.postgres
O django.contrib.postgres módulo fornece campos, funções e consultas específicas do PostgreSQL. Estes não funcionam com o SQL Server. As secções seguintes mostram como substituir cada funcionalidade.
Campo de matriz
O PostgreSQL ArrayField armazena arrays nativamente. O SQL Server não tem um tipo de coluna de array.
Opção 1: JSONField (funciona com Django 3.2 e versões posteriores)
# Before
from django.contrib.postgres.fields import ArrayField
class Product(models.Model):
tags = ArrayField(models.CharField(max_length=50), default=list)
# After
class Product(models.Model):
tags = models.JSONField(default=list)
Consultar alterações:
# Before (PostgreSQL)
Product.objects.filter(tags__contains=["sale"])
Product.objects.filter(tags__overlap=["sale", "new"])
Product.objects.filter(tags__len=3)
# After (SQL Server with JSONField)
# Use __contains for exact list matching
Product.objects.filter(tags__contains=["sale"])
# For overlap-style queries, use raw SQL
from django.db.models.expressions import RawSQL
Product.objects.filter(
pk__in=RawSQL(
"""
SELECT p.id FROM products_product p
CROSS APPLY OPENJSON(p.tags) t
WHERE t.value IN (%s, %s)
""",
["sale", "new"],
)
)
Opção 2: Tabela relacionada (normalizada, melhor para arrays grandes ou filtragem frequente)
class Product(models.Model):
name = models.CharField(max_length=200)
class ProductTag(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="tags")
tag = models.CharField(max_length=50, db_index=True)
class Meta:
unique_together = [("product", "tag")]
HStoreField
Substituir por JSONField:
# Before
from django.contrib.postgres.fields import HStoreField
class Profile(models.Model):
metadata = HStoreField(default=dict)
# After
class Profile(models.Model):
metadata = models.JSONField(default=dict)
JSONField Suporta a mesma sintaxe de pesquisa de chaves:
# Both backends support this
Profile.objects.filter(metadata__theme="dark")
Campos de intervalo
Os tipos de intervalo PostgreSQL (IntegerRangeField, BigIntegerRangeField, DateRangeField, DateTimeRangeField, DecimalRangeField) não têm equivalente ao SQL Server. Use dois campos separados:
# Before
from django.contrib.postgres.fields import DateRangeField
class Event(models.Model):
dates = DateRangeField()
# After
class Event(models.Model):
start_date = models.DateField()
end_date = models.DateField()
Atualize as consultas para usar comparações separadas de campos. Antes, com PostgreSQL: DateRangeField
from django.contrib.postgres.fields import DateRangeField
from psycopg2.extras import DateRange
Event.objects.filter(dates__contains=DateRange(start, end))
Depois, com duas colunas DateField no SQL Server:
from datetime import date
start = date(2026, 1, 1)
end = date(2026, 12, 31)
Event.objects.filter(start_date__lte=start, end_date__gte=end)
CITextField e CIEmailField
Os tipos de texto insensíveis a maiúsculas minúsculas do PostgreSQL usam a citext extensão. A colação predefinida do SQL Server (SQL_Latin1_General_CP1_CI_AS) já é insensível a maiúsculas e minúsculas, pelo que as opções padrão CharField e EmailField se comportam da mesma forma:
# Before
from django.contrib.postgres.fields import CITextField
class Tag(models.Model):
name = CITextField(max_length=100)
# After - already case-insensitive with default SQL Server collation
class Tag(models.Model):
name = models.CharField(max_length=100)
SearchVector, SearchQuery, SearchRank
A pesquisa em texto completo PostgreSQL está profundamente integrada com o Django. O SQL Server tem o seu próprio motor de busca em texto completo, mas não tem integração com o Django ORM. Veja Migração da pesquisa de texto completo mais adiante neste artigo.
Funções agregadas
Substitua agregados específicos do PostgreSQL:
# Before
from django.contrib.postgres.aggregates import ArrayAgg, StringAgg
Product.objects.values("category").annotate(
all_names=ArrayAgg("name"),
name_list=StringAgg("name", delimiter=", "),
)
# After - use SQL Server equivalents via RawSQL
from django.db.models.expressions import RawSQL
Product.objects.values("category").annotate(
name_list=RawSQL(
"STRING_AGG(name, ', ') WITHIN GROUP (ORDER BY name)",
[],
),
)
Note
STRING_AGGrequer o SQL Server 2017 ou posterior, ou o Base de Dados SQL do Azure.
Migração para pesquisa em texto completo
A pesquisa de texto completo do PostgreSQL usa índices tsvector, tsquery e GIN. O SQL Server tem um motor de busca de texto completo separado.
Ativar a pesquisa em texto completo no SQL Server
-- Create a full-text catalog
CREATE FULLTEXT CATALOG [MyAppCatalog] AS DEFAULT;
-- Create a full-text index (table must have a unique index)
CREATE FULLTEXT INDEX ON [products_product]([name], [description])
KEY INDEX [PK_products_product]
WITH CHANGE_TRACKING AUTO;
Consultar a pesquisa de texto completo no Django
Utilize SQL em bruto para aceder às funções CONTAINS e FREETEXT do SQL Server:
from django.db.models.expressions import RawSQL
# Equivalent of PostgreSQL SearchVector + SearchQuery
def search_products(query):
return Product.objects.filter(
pk__in=RawSQL(
"""
SELECT p.id FROM products_product p
WHERE CONTAINS((p.name, p.description), %s)
""",
[query],
)
)
Para resultados classificados (equivalentes a SearchRank):
def search_products_ranked(query):
return Product.objects.raw(
"""
SELECT p.*, ft.[RANK]
FROM products_product p
INNER JOIN CONTAINSTABLE(products_product, (name, description), %s) ft
ON p.id = ft.[KEY]
ORDER BY ft.[RANK] DESC
""",
[query],
)
Guia de manutenção do índice de texto completo
Manutenção do plano para índices de texto completo do SQL Server após a migração:
- Use
CHANGE_TRACKING AUTOpara atualizações quase em tempo real. - Utilize
CHANGE_TRACKING MANUALpara janelas de carregamento em massa e, em seguida, execute uma população completa. - Acompanhe o estado do rastreio e o backlog através de
sys.fulltext_indexesesys.dm_fts_index_population.
Verificar o estado:
SELECT
OBJECT_NAME(i.object_id) AS table_name,
i.change_tracking_state_desc,
i.has_crawl_completed,
i.crawl_type_desc
FROM sys.fulltext_indexes AS i;
Após grandes cargas de dados com rastreamento manual:
ALTER FULLTEXT INDEX ON [products_product] START FULL POPULATION;
Sugestão
Reconstruir ou repovoar índices de texto completo durante janelas de baixo tráfego. Populações completas podem ser caras em mesas grandes.
Crie um gestor de pesquisa
Envolve o SQL bruto num gestor para acesso limpo:
class ProductSearchManager(models.Manager):
def search(self, query):
if not query:
return self.none()
return self.filter(
pk__in=RawSQL(
"""
SELECT p.id FROM products_product p
WHERE CONTAINS((p.name, p.description), %s)
""",
[query],
)
)
class Product(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
objects = ProductSearchManager()
# Usage: Product.objects.search("mountain bike")
Pós-SIG e dados espaciais
mssql-django não inclui um backend GIS do GeoDjango. Se a tua aplicação PostgreSQL utiliza PostGIS através de django.contrib.gis, não podes migrar consultas geoespaciais diretamente para o ORM do Django no SQL Server.
O SQL Server suporta nativamente tipos de dados de geografia e geometria. Para trabalhar com dados espaciais após a migração:
- Armazene dados espaciais usando SQL bruto ou campos de modelos personalizados que correspondam às colunas de geografia ou geometria do SQL Server.
- Consulta de dados espaciais usando SQL bruto com as funções espaciais integradas do SQL Server:
from django.db import connection
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT id, name
FROM stores
WHERE location.STDistance(geography::Point(%s, %s, 4326)) <= %s
""",
[latitude, longitude, radius_meters],
)
- Considere bibliotecas de terceiros que adicionam suporte espacial SQL Server ao Django, ou mantenham as consultas espaciais como SQL puro enquanto usam o ORM para tudo o resto.
Note
Se a sua aplicação depende fortemente das consultas espaciais do GeoDjango, avalie cuidadosamente o custo da migração. Mover consultas espaciais para SQL bruto requer reescrever cada filtro espacial GeoDjango.
Migração da agregação de conexões
Se a sua aplicação PostgreSQL utiliza pgbouncer para agrupamento de ligações, substitua-o pela gestão de ligações integrada do Django ou pelo agrupamento de ligações ODBC.
Reutilização de conexões no Django
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
"CONN_MAX_AGE": 600, # Reuse connections for 10 minutes
"CONN_HEALTH_CHECKS": True, # Django 4.1+
},
}
Para mais informações, consulte Connection pooling in mssql-django.
substituto de DISTINCT ON
O PostgreSQL suporta a utilização de DISTINCT ON para obter uma linha por grupo. O SQL Server não suporta esta sintaxe. Use as funções de janela em vez disso:
# Before (PostgreSQL)
Entry.objects.order_by("blog_id", "-pub_date").distinct("blog_id")
# After (SQL Server) - use raw SQL with ROW_NUMBER
Entry.objects.raw(
"""
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY blog_id ORDER BY pub_date DESC) AS rn
FROM blog_entry
) sub
WHERE rn = 1
"""
)
Consultas JSONB
O tipo jsonb do PostgreSQL suporta operadores avançados de consulta. O SQL Server armazena o JSON como nvarchar(max), com funções de consulta disponíveis desde o SQL Server 2016.
A sintaxe de procura JSONField do Django funciona em ambos os backends para operações básicas:
# Works on both PostgreSQL and SQL Server
Config.objects.filter(data__settings__theme="dark")
Config.objects.filter(data__has_key="settings")
Para consultas JSON avançadas não suportadas pelo ORM do Django, utilize as funções JSON_VALUE e OPENJSON do SQL Server:
from django.db.models.expressions import RawSQL
# Query nested JSON values
Config.objects.annotate(
theme=RawSQL("JSON_VALUE(data, '$.settings.theme')", [])
).filter(theme="dark")
Remover dependências do PostgreSQL
Após a migração, remova os pacotes PostgreSQL do seu projeto:
pip uninstall psycopg2-binary psycopg2 psycopg
Remover django.contrib.postgres de INSTALLED_APPS em settings.py:
INSTALLED_APPS = [
# Remove this line:
# "django.contrib.postgres",
"django.contrib.admin",
"django.contrib.auth",
# ...
]
Lista de verificação da migração
| Step | Detalhes |
|---|---|
| Mudar a infraestrutura de backend | Substitua django.db.backends.postgresql por mssql em settings.py. |
| Substituir contrib.postgres | Troca ArrayField, HStoreField, campos de alcance e campos de CI. |
| Atualizar pesquisa de texto completo | Migrar de tsvector/tsquery para o SQL Server CONTAINS/FREETEXT. |
| Atualizar consultas espaciais | Reescreva as consultas do GeoDjango como SQL bruto usando funções espaciais do SQL Server. |
Substituir DISTINCT ON |
Usa ROW_NUMBER() funções de janela. |
| Atualizar SQL bruto | Mude a sintaxe PostgreSQL (LIMIT, ||, NOW()) para SQL Server. Ver Atualizar SQL personalizado. |
| Ativar RCSI | Definir READ_COMMITTED_SNAPSHOT ON para corresponder ao comportamento do MVCC do PostgreSQL. Veja Diferenças de isolamento de transações. |
| Colação de testes | Verifique se o comportamento de sensibilidade ao caso corresponde às suas expectativas. Ver Diferenças de Collation. |
| Remover psycopg2 | Desinstalar psycopg2-binary ou psycopg. Remover django.contrib.postgres. |
| Regenerar migrações | Apague os ficheiros de migração antigos e execute makemigrations e migrate novamente, do zero. |
| Migrar dados | Utilize dumpdata/loaddata ou uma ferramenta ETL para grandes conjuntos de dados. |