Django 中有没有办法始终将特定字段转换为 VARCHAR?
Is there a way in Django to always CAST a specific Field to VARCHAR?
背景情况说明:
我正在创建一个 Django2 应用程序,它使用 Microsoft SQL 服务器作为后端数据库。该数据库有几个包含 SQL_VARIANT
字段的 table(我无法管理或更改它们)。此字段包含空字符串或数字(它们用作主键或外键)
问题:
在 MSSQL 上,当您使用 SQL_VARIANT
字段和 VARCHAR
字段制作 JOIN
时,它会产生一行空结果,要解决这个问题,您必须使用 CAST([Table Name].[SQL_VARIANT Field Name] AS VARCHAR
将 SQL_VARIANT 字段显式转换为 VARCHAR
目标:
每次 Django 在 QuerySet 上创建 JOIN
或在 QuerySet
上调用 SQL_VARIANT
字段时,我想找到一种方法来执行 CAST([Table Name].[SQL_VARIANT Field Name] AS VARCHAR
我怎样才能做到这一点?
代码参考:
在我的问题中,我使用了来自 MSSQL 数据库的 3 tables(供应商、产品和产品分类帐条目)
供应商Table
create table [Vendor]
(
Id sql_variant not null primary key,
Name varchar(30) not null,
Address varchar(30) not null,
City varchar(30) not null,
Contact varchar(30) not null,
Type int not null,
Category int not null,
)
产品Table
create table [Product]
(
Id varchar(20) not null primary key,
Description varchar(60) not null,
Class varchar(10) not null,
[Vendor Id] varchar(20) not null,
)
产品分类帐条目table
create table [Product Ledger Entry]
(
Id int not null primary key,
[Product Id] varchar(20) not null,
[Posting Date] datetime not null,
[Entry Type] int not null,
[Source Id] varchar(20) not null,
[Document Id] varchar(20) not null,
Quantity decimal(38,20) not null,
)
如果我想横穿所有 3 个 table,我会做 ...
SELECT
[Vendor].[Id],
[Vendor].[Name],
[Product].[Id],
[Product Ledger Entry].Quantity
FROM [Vendor]
INNER JOIN [Product] ON ([Vendor].[Id] = [Product].[Vendor Id])
INNER JOIN [Product Ledger Entry] ON ([Product].[Id] = [Product Ledger Entry].[Product Id])
但是这个查询没有产生任何行。仅通过对 SQL_VARIANT
字段进行显式转换,此查询显示异常结果。
SELECT
[Vendor].[Id],
[Vendor].[Name],
[Product].[Id],
[Product Ledger Entry].Quantity
FROM [Vendor]
INNER JOIN [Product] ON (CAST([Vendor].[Id] AS VARCHAR(20)) = [Product].[Vendor Id])
INNER JOIN [Product Ledger Entry] ON ([Product].[Id] = [Product Ledger Entry].[Product Id])
当你使用 Django 时也会发生同样的事情,这里是模型:
class Vendor(models.Model):
id = models.CharField(db_column='Id', primary_key=True, , max_length=20)
name = models.CharField(db_column='Name', max_length=30)
# Address Description
address = models.CharField(db_column='Address', max_length=30)
# Province/City/Municipality
city = models.CharField(db_column='City', max_length=30)
class Meta:
managed = False
db_table = 'Vendor'
def __str__(self):
return f'{self.id} | {self.name}'
class Product(models.Model):
id = models.CharField(db_column='No_', primary_key=True, max_length=20)
description = models.CharField(db_column='Description', max_length=60)
vendor_id = models.ForeignKey(
'Vendor', on_delete=models.CASCADE,
db_column='Vendor Id', to_field='id', related_name='products', db_index=False, blank=True
)
class Meta:
managed = False
db_table = 'Product'
def __str__(self):
return f'{self.id}'
class ProductLedgerEntry(models.Model):
id = models.IntegerField(db_column='Id', primary_key=True)
product_id = models.ForeignKey(
'Product', on_delete=models.CASCADE,
db_column='Product Id', to_field='id', related_name='ledger_entries', db_index=False
)
posting_date = models.DateTimeField(db_column='Posting Date')
ENTRY_TYPE = [
(0, 'Compra'),
(1, 'Venta'),
(2, 'Ajuste positivo'),
(3, 'Ajuste negativo'),
(4, 'Transferencia'),
(5, 'Consumo'),
(6, 'Salida desde fab.'),
]
entry_type = models.IntegerField(
db_column='Entry Type', choices=ENTRY_TYPE
)
quantity = models.DecimalField(db_column='Quantity', max_digits=38, decimal_places=20)
class Meta:
managed = False
db_table = 'Product Ledger Entry'
def __str__(self):
return f'{self.product_id} | {self.variant_code} | {self.posting_date}'
只要我不使用 annotate(...)、select_related(...) 或任何其他 Django API 方法使 JOIN
在引擎盖,我可以设法获得正确的结果。但是,我想使用注释。
请帮帮我
AFAIK,使这成为可能的唯一方法是分叉您正在使用的 SQL 服务器后端(大概是 django-pyodbc-azure
),以便在检测到 SQL_VARIANT
场.
但是,这感觉是个很糟糕的主意;正如书 "Two Scoops of Django" 所说,避免创建 FrankenDjango 的诱惑!您的基础表不是由 Django 创建的;根据我的经验,最好对不是由 Django 创建和管理的表使用原始 SQL。您可以将 ORM 混合用于由 Django 管理或创建的表,将原始 SQL 用于在 Django 外部创建的遗留表。
通过 Django 的 execute()
方法使用绑定参数仍然为您提供 SQL 注入保护:https://docs.djangoproject.com/en/2.2/topics/db/sql/#executing-custom-sql-directly
祝你好运!
背景情况说明:
我正在创建一个 Django2 应用程序,它使用 Microsoft SQL 服务器作为后端数据库。该数据库有几个包含 SQL_VARIANT
字段的 table(我无法管理或更改它们)。此字段包含空字符串或数字(它们用作主键或外键)
问题:
在 MSSQL 上,当您使用 SQL_VARIANT
字段和 VARCHAR
字段制作 JOIN
时,它会产生一行空结果,要解决这个问题,您必须使用 CAST([Table Name].[SQL_VARIANT Field Name] AS VARCHAR
目标:
每次 Django 在 QuerySet 上创建 JOIN
或在 QuerySet
SQL_VARIANT
字段时,我想找到一种方法来执行 CAST([Table Name].[SQL_VARIANT Field Name] AS VARCHAR
我怎样才能做到这一点?
代码参考:
在我的问题中,我使用了来自 MSSQL 数据库的 3 tables(供应商、产品和产品分类帐条目)
供应商Table
create table [Vendor]
(
Id sql_variant not null primary key,
Name varchar(30) not null,
Address varchar(30) not null,
City varchar(30) not null,
Contact varchar(30) not null,
Type int not null,
Category int not null,
)
产品Table
create table [Product]
(
Id varchar(20) not null primary key,
Description varchar(60) not null,
Class varchar(10) not null,
[Vendor Id] varchar(20) not null,
)
产品分类帐条目table
create table [Product Ledger Entry]
(
Id int not null primary key,
[Product Id] varchar(20) not null,
[Posting Date] datetime not null,
[Entry Type] int not null,
[Source Id] varchar(20) not null,
[Document Id] varchar(20) not null,
Quantity decimal(38,20) not null,
)
如果我想横穿所有 3 个 table,我会做 ...
SELECT
[Vendor].[Id],
[Vendor].[Name],
[Product].[Id],
[Product Ledger Entry].Quantity
FROM [Vendor]
INNER JOIN [Product] ON ([Vendor].[Id] = [Product].[Vendor Id])
INNER JOIN [Product Ledger Entry] ON ([Product].[Id] = [Product Ledger Entry].[Product Id])
但是这个查询没有产生任何行。仅通过对 SQL_VARIANT
字段进行显式转换,此查询显示异常结果。
SELECT
[Vendor].[Id],
[Vendor].[Name],
[Product].[Id],
[Product Ledger Entry].Quantity
FROM [Vendor]
INNER JOIN [Product] ON (CAST([Vendor].[Id] AS VARCHAR(20)) = [Product].[Vendor Id])
INNER JOIN [Product Ledger Entry] ON ([Product].[Id] = [Product Ledger Entry].[Product Id])
当你使用 Django 时也会发生同样的事情,这里是模型:
class Vendor(models.Model):
id = models.CharField(db_column='Id', primary_key=True, , max_length=20)
name = models.CharField(db_column='Name', max_length=30)
# Address Description
address = models.CharField(db_column='Address', max_length=30)
# Province/City/Municipality
city = models.CharField(db_column='City', max_length=30)
class Meta:
managed = False
db_table = 'Vendor'
def __str__(self):
return f'{self.id} | {self.name}'
class Product(models.Model):
id = models.CharField(db_column='No_', primary_key=True, max_length=20)
description = models.CharField(db_column='Description', max_length=60)
vendor_id = models.ForeignKey(
'Vendor', on_delete=models.CASCADE,
db_column='Vendor Id', to_field='id', related_name='products', db_index=False, blank=True
)
class Meta:
managed = False
db_table = 'Product'
def __str__(self):
return f'{self.id}'
class ProductLedgerEntry(models.Model):
id = models.IntegerField(db_column='Id', primary_key=True)
product_id = models.ForeignKey(
'Product', on_delete=models.CASCADE,
db_column='Product Id', to_field='id', related_name='ledger_entries', db_index=False
)
posting_date = models.DateTimeField(db_column='Posting Date')
ENTRY_TYPE = [
(0, 'Compra'),
(1, 'Venta'),
(2, 'Ajuste positivo'),
(3, 'Ajuste negativo'),
(4, 'Transferencia'),
(5, 'Consumo'),
(6, 'Salida desde fab.'),
]
entry_type = models.IntegerField(
db_column='Entry Type', choices=ENTRY_TYPE
)
quantity = models.DecimalField(db_column='Quantity', max_digits=38, decimal_places=20)
class Meta:
managed = False
db_table = 'Product Ledger Entry'
def __str__(self):
return f'{self.product_id} | {self.variant_code} | {self.posting_date}'
只要我不使用 annotate(...)、select_related(...) 或任何其他 Django API 方法使 JOIN
在引擎盖,我可以设法获得正确的结果。但是,我想使用注释。
请帮帮我
AFAIK,使这成为可能的唯一方法是分叉您正在使用的 SQL 服务器后端(大概是 django-pyodbc-azure
),以便在检测到 SQL_VARIANT
场.
但是,这感觉是个很糟糕的主意;正如书 "Two Scoops of Django" 所说,避免创建 FrankenDjango 的诱惑!您的基础表不是由 Django 创建的;根据我的经验,最好对不是由 Django 创建和管理的表使用原始 SQL。您可以将 ORM 混合用于由 Django 管理或创建的表,将原始 SQL 用于在 Django 外部创建的遗留表。
通过 Django 的 execute()
方法使用绑定参数仍然为您提供 SQL 注入保护:https://docs.djangoproject.com/en/2.2/topics/db/sql/#executing-custom-sql-directly
祝你好运!