Background Situation Description:
I'm creating a Django2 application which use a Microsoft SQL Server as back-end database. This database has several tables (which I cannot manage or change them) containing SQL_VARIANT
fields. This fields contains either an empty string or a number (They are used as Primary Key or as Foreign Key)
Problem:
On MSSQL when you make a JOIN
using a SQL_VARIANT
field and a VARCHAR
field, it yields an row empty result, to solve this you have to explicitly convert the SQL_VARIANT field to VARCHAR using CAST([Table Name].[SQL_VARIANT Field Name] AS VARCHAR
Goal:
I want to find a way to do the CAST([Table Name].[SQL_VARIANT Field Name] AS VARCHAR
every time Django makes a JOIN
on the QuerySet or calls a SQL_VARIANT
field on the QuerySet
How can I accomplish this?
Code as reference:
In my problem I'm using 3 tables from MSSQL Database (Vendor, Product and Product Ledger Entry)
Vendor 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,
)
Product 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,
)
Product Ledger Entry 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,
)
If I wanted to transverse all 3 tables, I would do ...
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])
However this query doesn't yield any rows. Only by making a explicit cast over the SQL_VARIANT
field, this query shows the excepted result.
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])
The same thing happends when you use Django, here are the models:
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}'
As long as I don't make use of annotate(...), select_related(...) or any other Django API methond that makes JOIN
under the hood, I can manage to get proper results. However, I want to use annotate.
Please help me