Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Dit artikel is een gedetailleerde migratiehandleiding voor Django-toepassingen die overstappen van PostgreSQL (psycopg2of psycopg) naar SQL Server (mssql-django). Zie Django-apps migreren van andere databases naar SQL Server voor een algemeen overzicht van de migratie vanuit een database.
Prerequisites
- Python 3.8 of hoger
- Microsoft ODBC-stuurprogramma 17 of 18 voor SQL Server. Zie Mssql-django installeren.
- SQL Server 2016 of hoger of Azure SQL Database
De back-end van de database wisselen
Vervang uw PostgreSQL-configuratie in 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",
},
},
}
requirements.txtbijwerken:
# Remove
# psycopg2-binary>=2.9
# or psycopg[binary]>=3.1
# Add
mssql-django>=1.5
Django.contrib.postgres-functies vervangen
De django.contrib.postgres module biedt postgreSQL-specifieke velden, functies en zoekacties. Deze werken niet met SQL Server. In de volgende secties ziet u hoe u elke functie vervangt.
ArrayField
PostgreSQL ArrayField slaat matrices systeemeigen op. SQL Server heeft geen matrixkolomtype.
Optie 1: JSONField (werkt met Django 3.2 en latere versies)
# 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)
Wijzigingen opvragen:
# 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"],
)
)
Optie 2: Gerelateerde tabel (genormaliseerd, beter voor grote matrices of frequent filteren)
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
Vervangen door 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 ondersteunt dezelfde syntaxis voor sleutelzoekacties:
# Both backends support this
Profile.objects.filter(metadata__theme="dark")
Bereikvelden
PostgreSQL-bereiktypen (IntegerRangeField, BigIntegerRangeField, DateRangeField, DateTimeRangeField) DecimalRangeFieldhebben geen SQL Server equivalent. Gebruik twee afzonderlijke velden:
# 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()
Werk query's bij om afzonderlijke veldvergelijkingen te gebruiken. Vóór, met PostgreSQL's DateRangeField:
from django.contrib.postgres.fields import DateRangeField
from psycopg2.extras import DateRange
Event.objects.filter(dates__contains=DateRange(start, end))
Na, met twee DateField kolommen op 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 en CIEmailField
De hoofdlettergevoelige teksttypen van PostgreSQL gebruiken de citext extensie. De standaardsortering van SQL Server (SQL_Latin1_General_CP1_CI_AS) is al hoofdletterongevoelig, dus standaard CharField en EmailField gedragen zich hetzelfde:
# 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
Zoeken in volledige tekst in PostgreSQL is diep geïntegreerd met Django. SQL Server heeft een eigen zoekprogramma voor volledige tekst, maar geen Django ORM-integratie. Zie de migratie voor zoeken in volledige tekst verderop in dit artikel.
Gecompileerde functies
Vervang PostgreSQL-specifieke aggregaties:
# 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_AGGvereist SQL Server 2017 of hoger of Azure SQL Database.
Migratie van zoeken in volledige tekst
PostgreSQL-zoekopdrachten in volledige tekst gebruiken tsvector, tsqueryen GIN indexen. SQL Server heeft een afzonderlijke zoekmachine voor volledige tekst.
Zoeken in volledige tekst inschakelen in 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;
Query's uitvoeren voor volledige-tekstzoekopdrachten in Django
Gebruik onbewerkte SQL voor toegang tot SQL Server CONTAINS en FREETEXT functies:
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],
)
)
Voor gerangschikte resultaten (gelijk aan 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],
)
Onderhoudsrunbook voor volledige tekstindex
Plan het onderhoud voor SQL Server full-text-indexen na migratie:
- Gebruik
CHANGE_TRACKING AUTOvoor bijna realtime-updates. - Gebruik
CHANGE_TRACKING MANUALvoor vensters voor bulksgewijs laden en voer vervolgens een volledige populatie uit. - De crawlstatus en achterstand volgen via
sys.fulltext_indexesensys.dm_fts_index_population.
Status controleren:
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;
Na grote dataladingen met handmatige tracering:
ALTER FULLTEXT INDEX ON [products_product] START FULL POPULATION;
Tip
Bouw indexen in volledige tekst opnieuw of vul deze opnieuw in tijdens vensters met weinig verkeer. Volledige populaties kunnen duur zijn voor grote tabellen.
Een search manager maken
Verpakt de onbewerkte SQL in een manager voor schone toegang:
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")
PostGIS en ruimtelijke gegevens
mssql-django bevat geen GeoDjango GIS-back-end. Als uw PostgreSQL-toepassing Gebruikmaakt van PostGIS viadjango.contrib.gis, kunt u ruimtelijke query's niet rechtstreeks migreren naar de Django ORM op SQL Server.
SQL Server biedt systeemeigen ondersteuning voor geografie- en geometriegegevenstypen. Werken met ruimtelijke gegevens na migratie:
- Sla ruimtelijke gegevens op met behulp van onbewerkte SQL- of aangepaste modelvelden die zijn toegewezen aan SQL Server geografie- of geometriekolommen.
- Query's uitvoeren op ruimtelijke gegevens met behulp van onbewerkte SQL met ingebouwde ruimtelijke functies van 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],
)
- Overweeg bibliotheken van derden die SQL Server ruimtelijke ondersteuning toevoegen aan Django of ruimtelijke query's als onbewerkte SQL bewaren terwijl u de ORM voor alles gebruikt.
Note
Als uw toepassing sterk afhankelijk is van ruimtelijke geodjango-zoekacties, evalueert u de migratiekosten zorgvuldig. Voor het verplaatsen van ruimtelijke query's naar onbewerkte SQL moet elk ruimtelijk GeoDjango-filter opnieuw worden geschreven.
Migratie van groepsgewijze verbindingen
Als uw PostgreSQL-toepassing pgbouncer gebruikt voor connection pooling, vervangt u dit door Django's ingebouwde verbindingsbeheer of ODBC-verbindingspooling.
Django-verbinding hergebruiken
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+
},
}
Zie Verbindingspooling in mssql-django voor meer informatie.
alternatief voor DISTINCT ON
PostgreSQL biedt ondersteuning DISTINCT ON voor het ophalen van één rij per groep. SQL Server biedt geen ondersteuning voor deze syntaxis. Gebruik in plaats daarvan vensterfuncties:
# 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
"""
)
JSONB-query’s
Het type van jsonb PostgreSQL ondersteunt uitgebreide queryoperators. SQL Server slaat JSON op als nvarchar(max) met queryfuncties die beschikbaar zijn sinds SQL Server 2016.
De zoeksyntaxis van JSONField Django werkt op beide back-ends voor basisbewerkingen:
# Works on both PostgreSQL and SQL Server
Config.objects.filter(data__settings__theme="dark")
Config.objects.filter(data__has_key="settings")
Voor geavanceerde JSON-query's die niet worden ondersteund door Django's ORM, gebruikt u SQL Server JSON_VALUE en OPENJSON functies:
from django.db.models.expressions import RawSQL
# Query nested JSON values
Config.objects.annotate(
theme=RawSQL("JSON_VALUE(data, '$.settings.theme')", [])
).filter(theme="dark")
PostgreSQL-afhankelijkheden verwijderen
Verwijder na de migratie PostgreSQL-pakketten uit uw project:
pip uninstall psycopg2-binary psycopg2 psycopg
Verwijder django.contrib.postgres uit INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
# Remove this line:
# "django.contrib.postgres",
"django.contrib.admin",
"django.contrib.auth",
# ...
]
Controlelijst voor migratie
| Stap | Details |
|---|---|
| Backend wijzigen | Vervang django.db.backends.postgresql door mssql in settings.py. |
| Vervang contrib.postgres | Verwissel ArrayField, HStoreField, bereikvelden en CI-velden. |
| Zoeken in volledige tekst bijwerken | Migreren van tsvector/tsquery naar SQL Server CONTAINS/FREETEXT. |
| Ruimtelijke query's bijwerken | GeoDjango-zoekopdrachten opnieuw schrijven als onbewerkte SQL met behulp van SQL Server ruimtelijke functies. |
Vervangen DISTINCT ON |
Gebruik ROW_NUMBER() vensterfuncties. |
| Onbewerkte SQL bijwerken | Wijzig de Syntaxis van PostgreSQL (LIMIT, ||, NOW()) in SQL Server syntaxis. Zie Aangepaste SQL bijwerken. |
| RCSI inschakelen | Ingesteld READ_COMMITTED_SNAPSHOT ON op overeenstemming met het gedrag van PostgreSQL MVCC. Zie verschillen in transactieisolatie. |
| Sortering testen | Controleer of het gedrag van hoofdlettergevoeligheid overeenkomt met uw verwachtingen. Zie Sorteringsverschillen. |
| Psycopg2 verwijderen | Verwijderen psycopg2-binary of psycopg. Verwijder django.contrib.postgres. |
| Migraties opnieuw genereren | Verwijder oude migratiebestanden en voer makemigrations en migrate opnieuw uit. |
| Gegevens migreren | Gebruik dumpdata/loaddata of een ETL-hulpprogramma voor grote gegevenssets. |