Django SQL Server ForeignKey that allows NULL - sql-server

I've been struggling with this issue for some time. I'm switching one of my apps from Postgres to SQL Server database and I'm facing an issue with ForeignKey field. I'm running latest SQL Server version with Django 1.11 and using django-pyodbc-azure app.
class Owner(models.Model):
name = models.TextField()
dog = models.ForeignKey('Dog', related_name='+')
class Dog(models.Model):
name = models.TextField()
owner = models.ForeignKey('Owner', null=True, related_name='+')
When I try to insert a new record I get the following message:
dog = Dog.objects.create(name='Rex')
owner = Owner.objects.create(name='Mike', dog=dog)
dog.owner = owner
dog.save()
('23000', u"[23000] [Microsoft][ODBC Driver 13 for SQL Server][SQL
Server]Violation of UNIQUE KEY constraint
'UQ__owner__EF6DECB9214EF1D9'. Cannot insert duplicate key in object
'dbo.owner'. The duplicate key value is (NULL). (2627)
(SQLExecDirectW)")

The label owner is the problem, try:
dog.owner_id = owner.id

Related

"SQLSTATE[42S02]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server] Invalid 'users' object name

I migrated my MySQL database to SQL Server 2019.
I made the required configurations:
php.ini
extension=php_pdo_sqlsrv_80_ts_x64
extension=php_sqlsrv_80_ts_x64
.env
DB_CONNECTION=sqlsrv
DB_HOST=servername
DB_PORT=null
DB_DATABASE=dbname
DB_USERNAME=sa
DB_PASSWORD=password
The code below is working fine. O test this in web.php and i get db connect successfully and all (2) tables.
try {
DB::connection()->getPdo();
echo DB::connection()->getDatabaseName()." db connect successfully";
} catch (Exception $e) {
dd($e->getMessage());
}
$sql = DB::select("SELECT TOP (2) id, email, email_verified_at, password, remember_token, image, userable_id, userable_type, created_at, updated_at, deleted_at FROM soraeir.users", [1]);
die(print_r($sql));
Message when I try to login I get:
SQLSTATE[42S02]:
[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]
Invalid 'users' object name. (SQL: select top 1 * from [users] where [email] = admin and [users].[deleted_at] is null)"
I think eloquent must add an instance_name to all queries...I don't know how to proceed...?
Perhaps, instead of
select top 1 *
from [users]
where [email] = admin
and [users].[deleted_at] is null
We could use
select top 1 *
from [instance_name].[users]
where [email] = admin
and [users].[deleted_at] is null
I solved my problem
my problem was when I was doing the migration I misconfigured at the level of schema mapping with sql server migration assistant for mysql.
all my tables in sql server db was : mysql_db_name.table_name
Solution
Source Schema: mysql_db_name
Target Schema : sqlsrv_db_name.dbo
dbo is required
now all tables are as follows : dbo.table_name

Creating flask-sqlalchemy table object of a table on a linked server

I am creating a small flask website with a Microsoft SQL database server.
The web server is on a linux machine and I connecting to the database via odbc -> pyodbc & flask-sqlalchemy.
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.engine import URL
connection_url = URL.create(
"mssql+pyodbc",
username="user",
password="password",
host="192.168.0.128",
port=1433,
database="Print",
query={"driver": "ODBC Driver 17 for SQL Server"})
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
app.config['SQLALCHEMY_DATABASE_URI'] = connection_url
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
The exsisting local database tables are no problem to connect to.
class Print(db.Model):
__tablename__ = 'Printers'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(24))
def __repr__(self):
return '<Name %r>' % self.name
But when I try to access a table from a linked server the query gets mangled somewhere in the translation.
class Collo(db.Model):
__tablename__ = 'collo'
__table_args__ = {'schema': 'sprod..sdba'}
type = db.Column(db.String(8), primary_key=True)
description = db.Column(db.String(30))
def __repr__(self):
return '<collo %r>' % self.description
The error:
sqlalchemy.exc.ProgrammingError: (pyodbc.ProgrammingError) ('42S02', "[42S02] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Invalid object name 'sprod..sdba.collo'. (208) (SQLExecDirectW)")
[SQL: SELECT TOP 1 [sprod.].sdba.collo.type AS sprod__sdba_collo_type, [sprod.].sdba.collo.description AS sprod__sdba_collo_description
FROM [sprod.].sdba.collo
WHERE [sprod.].sdba.collo.type = ?]
[parameters: ('GOOT2',)]
Under sql management studio the query is:
select * from [SPROD]..[sdba].[collo]
selecting in pyodbc:
cursor.execute("select * from sprod..sdba.collo where type = 'GOOT2'" )
works fine.
How can I prevent the inclusion of the square brackets in flask-sqlalchemy?
This is a known issue with SQLAlchemy and SQL Server "linked servers" as discussed in this GitHub issue. The current workaround is to create a SYNONYM on the local server for the table on the linked server as described here, e.g.,
CREATE SYNONYM [dbo].[hello_199] FOR [DOCKER199].[myDb].[dbo].[hello]

Django migrate column is not the same data type as referencing column

I have the below model which is an existing DB model and through Django's inspectdb management command the below model is created.
class ExistingLegacyModel(models.Model):
period = models.TextField(db_column="Period", blank=True, null=True)
key = models.AutoField(db_column="OutlookKey", primary_key=True)
class Meta:
managed = False
db_table = "table_name"
and currently, I'm trying to create a model with a field foreign key reference to the existing legacy DB model
class TestModel(models.Model):
period = models.ForeignKey(
ExistingLegacyModel,
on_delete=models.CASCADE,
db_column="OutlookKey",
)
so when I run the makemigrations command the migration file is successfully getting created with no issue. below is the migration file content.
class Migration(migrations.Migration):
initial = True
dependencies = [
('historical', '0011_phoenixcontractprice'),
]
operations = [
migrations.CreateModel(
name='TestModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('period', models.ForeignKey(db_column='OutlookKey', on_delete=django.db.models.deletion.CASCADE, to='app.ExistingLegacyModel')),
],
),
]
so now when i run the migrate command now, it is failing and giving the below error.
django.db.utils.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Column 'table_name.OutlookKey' is not the same data type as referencing column 'version_testmodel.OutlookKey' in foreign key 'version_testmodel_OutlookKey_eb16c31c_fk_table_name_OutlookKey'. (1778) (SQLExecDirectW); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Could not create constraint or index. See previous errors. (1750)")
I'm stuck with this issue for the past couple of days and I searched all over the internet but didn't get any resolution. I found a couple of StackOverflow questions that are very similar to my issue, but those questions are also unanswered.
Django - Migration foreign key field type not matching current type
Django 3.2 update AutoField to BigAutoField backward compatibility with foreign key relations
I'm currently using Django 3.2.13 and mssql-django to connect to the MSSQL database.
Any help on this will be highly appreciated! Thank you in advance.
UPDATE 1
I ran the sqlmigrate command for the initial migration. So for the period column, it is creating the table with a foreign key field with big int [OutlookKey] bigint NOT NULL whereas the existing legacy model has a normal integer field.
ALTER TABLE [<app>_<model>] ADD CONSTRAINT [<app>_<model>_OutlookKey_3505d410_fk_<existing_legacy_table>_OutlookKey] FOREIGN KEY ([OutlookKey]) REFERENCES [<existing_legacy_table>] ([OutlookKey]);

Cannot insert the value NULL into column 'id', table 'XXX'; column does not allow nulls

I have the below model:
class Loan(models.Model):
id = models.BigAutoField(primary_key=True)
date = models.DateField(default=timezone.now)
description = models.TextField(max_length=255)
When I try to save date and description I get the above error
below is my admin.py file:
#admin.register(Loan)
class LoanAdmin(admin.ModelAdmin):
pass
and below is my table created through migrations:
Django 3.2.6.
How can I solve this?
SQL Server version is Microsoft SQL Server 2019 (RTM-CU8-GDR)
I tried :
class Loan(models.Model):
date = models.DateField(default=timezone.now)
description = models.TextField(max_length=255)
The solution that worked to this problem was to delete all migrations and create new migrations.
you don't need to add id column specifically. Django creates id column itself.
class Loan(models.Model):
date = models.DateField(default=timezone.now)
description = models.TextField(max_length=255)
This should work.
Also, if you want to add custom id column check this

SQLAlchemy and mssql : how to create unique constraint with multiple null value

I am working with SQLAlchemy and MS SQL server and I would like to create a unique constraint that allows multiple NULL value.
I know that MS SQL server does not ignore the null value and considers it as violation for the UNIQUE KEY.
I also know how to fix it with SQL code (see here)
But is there a way to do the same thing with SQLAlchemy directly ?
Here is my code :
class Referential(db.Model):
__tablename__ = "REFERENTIAL"
id = db.Column("ID", Integer, primary_key=True, autoincrement=True)
name = db.Column("NAME", String(100), index=True, unique=True, nullable=False)
internal_code = db.Column("INTERNAL_CODE", String(50), unique=True, index=True)
Thanks in advance
MSSQL's implementation when it comes to allowing nulls in a unique column is a little odd.
import sqlalchemy as sa
sa.Table(
sa.Column('column', sa.String(50), nullable=True),
sa.Index('uq_column_allows_nulls', mssql_where=sa.text('column IS NOT NULL'),
)
If you are planning on using alembic like I was this is the code:
import sqlalchemy as sa
import alembic as op
op.create_index(
name='uq_column_name',
table_name='table',
columns=['column'],
mssql_where=sa.text('column IS NOT NULL'),
)
This uses the sql expression text for sqlalchemy and create_index's dialect_expression key word arguments mssql_where=

Resources