Django: Automatic assignment of primary key in MySql failing - python

I am following the Effective Django tutorial with the change of using MySql instead of sqlite3.
Following the official recommendation of Django and given that I am working with Python3.4 I am using mysqlclient driver.
I have created the following model as indicated in the tutorial:
class Contact(models.Model):
fName = models.CharField(max_length = 30)
lName = models.CharField(max_length = 100)
eMail = models.EmailField()
def str(self):
return ' '.join([self.fName, self.lName])
db has sync'ed (migrated) ok.Nonetheless, when creating through the shell a contact
a = Contact('John', 'Doe')
and then saving it
a.save()
I get the following error:
ValueError: invalid literal for int() with base 10: 'John'
which does not appear when I provide an explicit integer (taken as primary key???) as first argument
a = Contact(1, 'John', 'Doe')
Why isn't automatic pk assignment working as it should?
I am using Django 1.7.7 / Python 3.4 on virtualenv
Here are the settings:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'addressbook',
'USER': 'abuser',
'PASSWORD': '1234',
'HOST': 'localhost',
'PORT': '3306',
}
}
abuser has been granted all privileges with grant option on the addressbook (as also on the test_addressbook) db

To create a model instance you should use keyword arguments instead of positional:
a = Contact(fName='John', lName='Doe')
a.save()

Related

Django ORM: How to query secondary database which is in read-only mode and no reference in model.py file

I want to query secondary database which is basically a production database. Currently I am using direct query but want to use ORM.
My current models.py file looks like below. Here user is providing TABLE name. For simplicity consider TABLE is "SERVER_LIST".
from django.db import connections
# Create your models here.
def my_custom_sql(TABLE):
with connections["my_oracle"].cursor() as cursor:
cursor.execute("select * from {0} where server = 'XYZ';".format(TABLE))
row = cursor.fetchall()
return row
Database entry:setttings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase',
},
'my_oracle': {
'ENGINE': 'django.db.backends.oracle',
'NAME': 'xyz:1234/ABCDB',
'USER': 'ABC',
'PASSWORD': '1234'
},
}
I want to run same query using Django ORM. Can someone help how to connect to secondary database and create models.py file for this database. I refer to this link but it imports model.py file which i dont think is possible in my case as database is already existing and in read-only mode.
Your DATABASES variable in your settings.py file should look something like this:
DATABASES = {
'default': DEFAULT_DB_CONFIG,
'slave': SLAVE_DB_CONFIG,
}
and when you are using ORM and want to query a table, you can use using interface like below:
SampleModel.objects.using('slave').all()
Check documentation: https://docs.djangoproject.com/en/3.0/topics/db/multi-db/

How to Change Django Database schema in a View?

How can I change the database schema from a Django view?
I want the user to select a database field type.
First, settings.py should look something like this:
DATABASES = {
'default': {
'NAME': 'schema_name',
'ENGINE': 'django.db.backends.postgresql',
'USER': 'postgres',
'PASSWORD': 'password'
},
'foo': {
'NAME': 'schema_name',
'ENGINE': 'django.db.backends.mysql',
'USER': 'root',
'PASSWORD': 'password'
},
...
}
Now, in your code, you can manually decide which schema/database to use with the .using method:
SomeModel.objects.using('foo').all() #this uses "foo"
SomeModel.objects.all() #this uses "default"
For more complex logic, you can create a database router do decide which schema to use in a given scenario.
Update
You can execute raw queries by importing connection.
Here is an example create statement:
from django.db import connection
with connection.cursor() as cursor:
sql = '''
CREATE TABLE foo (
col1 INT,
col2 VARCHAR(45)
)'''
cursor.execute(sql)
You should be extremely careful when creating tables using user input. Doing so in an unsafe way can lead to SQL injection and potentially other vulnerabilities like stored xss.

django with multiple databases and foreignkeys for User

Suppose I have a django app on my server, but I wish to do authentication using django.contrib.auth.models where the User and Group models/data are on another server in another database. In Django, my DATABASES setting would be something like this:
DATABASES = {
'default': {},
'auth_db': {
'NAME' : 'my_auth_db',
'ENGINE' : 'django.db.backends.mysql',
'USER' : 'someuser',
'PASSWORD' : 'somepassword',
'HOST' : 'some.host.com',
'PORT' : '3306',
},
'myapp': {
'NAME': 'myapp_db',
'ENGINE': 'django.db.backends.mysql',
'USER': 'localuser',
'PASSWORD': 'localpass',
}
}
DATABASE_ROUTERS = ['pathto.dbrouters.AuthRouter', 'pathto.dbrouters.MyAppRouter']
First question: will this work, ie will it allow me to login to my Django app using users that are stored in the remote DB 'my_auth_db'?
Assuming the answer to the above is yes, what happens if in my local DB (app 'myapp') I have models that have a ForeignKey to User? In other words, my model SomeModel is defined in myapp and should exist in the myapp_db, but it have a ForeignKey to a User in my_auth_db:
class SomeModel(models.model):
user = models.ForeignKey(User, unique=False, null=False)
description = models.CharField(max_length=255, null=True)
dummy = models.CharField(max_length=32, null=True)
etc.
Second question: Is this possible or is it simply not possible for one DB table to have a ForeignKey to a table in another DB?
If I really wanted to make this work, could I replace the ForeignKey field 'user' with an IntegerField 'user_id' and then if I needed somemodel.user I would instead get somemodel.user_id and use models.User.objects.get(pk=somemodel.user_id), where the router knows to query auth_db for the User? Is this a viable approach?
The answer to question 1 is: Yes.
What you will need in any case is a database router (The example in the Django docs is exactly about the auth app, so there's no need to copy this code here).
The answer to question 2 is: Maybe. Not officially. It depends on how you have set up MySQL:
https://docs.djangoproject.com/en/dev/topics/db/multi-db/#limitations-of-multiple-databases
Django doesn’t currently provide any support for foreign key or many-to-many relationships spanning multiple databases.
This is because of referential integrity.
However, if you’re using SQLite or MySQL with MyISAM tables, there is no enforced referential integrity; as a result, you may be able to ‘fake’ cross database foreign keys. However, this configuration is not officially supported by Django.
I have a setup with several legacy MySQL DBs (readonly). This answer shows How to use django models with foreign keys in different DBs?
I later ran into troubles with Django ManyToMany through with multiple databases and the solution (as stated in the accepted answer there) is to set the table name with quotes:
class Meta:
db_table = '`%s`.`table2`' % db2_name
Related questions that might provide some additional information:
How to work around lack of support for foreign keys across databases in Django
How to use django models with foreign keys in different DBs?
It would be nice if somebody would take all this information and put in into the official Django doc :-)

LiveServerTestCase - settings.Database is improperly configured

I'm trying to set up a LiveServerTestCase.
For this I'm creating a User within my Testclass with
class ExampleTest(LiveServerTestCase):
user = User.objects.create_superuser('Testuser','test#user.com','1234')
user.save()
.
.
.(rest of the test)
without this line the server and test starts but obviously it can't login because there is no User created before.
But with this line I'm getting a
django.core.exceptions.ImproperlyConfigured: settings.DATABASES
is improperly configured. Please supply the NAME value.
error.
Do I need to set up the Server in settings.py for LiveServerTestCase, and in case yes, with which values or where do I find them?
UPDATE:
I'm running this test with
python manage.py test
so it sets up a database itself which I don't have to define in the settings.py, or am I wrong.
UPDATE2:
I already defined a 'production' database (before I even asked the question), it looks like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'HOST': 'localhost', # 10.1.2.41
'NAME': 'pim_testdatabase',
'USER': 'postgres',
'PASSWORD': '1234',
'PORT': '5432',
'HAS_HSTORE': True,
'TEST':{
'NAME': 'test_pim_testdatabase'
},
},
}
Still the exception appears.
You need to set up the database in your DATABASES settings.
Django sets up a test database corresponding to every database that is
defined in the DATABASES definition in your settings file.
By default the test databases get their names by prepending test_ to
the value of the NAME settings for the databases defined in DATABASES.
If you want to use a different database name, specify NAME in the TEST dictionary for any given database in DATABASES.
An example test database configuration:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'USER': 'mydatabaseuser',
'NAME': 'mydatabase',
'TEST': { # test database settings
'NAME': 'mytestdatabase', # test database name
},
},
}
The problem is that you are creating the user in the class definition. This runs when the test class is loaded, before the database has been created.
class ExampleTest(LiveServerTestCase):
user = User.objects.create_superuser('Testuser','test#user.com','1234')
user.save() # This save isn't required -- it has been saved already
You can fix the problem by moving the user creation into an individual test. Then the user will be created when the test method runs, after the database has been created.
class ExampleTest(LiveServerTestCase):
def test_user(self):
self.user = User.objects.create_superuser('Testuser','test#user.com','1234')
...
Django 1.8 has a setUpTestData method where you can set up initial data once for the entire test case. This is quicker, and less repetitive.
class ExampleTest(LiveServerTestCase):
#classmethod
def setUpTestData(cls):
# Set up data for the whole TestCase
self.user = User.objects.create_superuser('Testuser','test#user.com','1234')
def test_user(self):
# access the user with self.user
...
In earlier versions of Django which don't have setUpTestData, you can create the user in the setUp method.
class ExampleTest(LiveServerTestCase):
def setUp(self):
self.user = User.objects.create_superuser('Testuser','test#user.com','1234')

Django testing MS-SQL legacy database with stored procedures

In Django I would like to use Unit Test for testing a MS-SQL Server legacy database. The database is using stored procedures for adding data. The situation is as follow:
The MS-SQL database has the following settings in Django:
DATABASES['vadain_import'] = {
'ENGINE': 'sql_server.pyodbc',
'USER': 'xx',
'PASSWORD': 'xxx',
'HOST': '192.168.103.102',
'PORT': '',
'NAME': 'Vadain_import',
'OPTIONS': {
'driver': 'SQL Server Native Client 11.0',
'MARS_Connection': True,
}
}
The models of the database are made with inspectdb, example:
class WaOrders(models.Model):
order_id = models.IntegerField(primary_key=True, db_column='intOrderId')
type = models.TextField(db_column='chvType', blank=True)
class Meta:
db_table = 'WA_ORDERS'
database_name = 'vadain_import'
managed = False
# (There's a lot more of properties and models)
In models is executing the stored procedures. I cann't use the save
functionality of Django, like WAOrders.save(), because in the MS-SQL
database the primary key's are generated in the stored procedure.
#classmethod
def export(cls, **kwargs):
# Stored procedure for adding data into the db
sql = "declare #id int \
set #id=0\
declare #error varchar(1000)\
set #error=''\
exec UspWA_OrderImport\
#intOrderId=#id out\
,#chvType=N'%s'" % kwargs['type'] + " \
,#chvErrorMsg=#error output\
select #id as id, #error as 'error' \
"
# Connection Vadain db
cursor = connections['vadain_import'].cursor()
# Execute sql stored procedure, no_count is needed otherwise it's returning an error
# Return value primary key of WAOrders
try:
cursor.execute(no_count + sql)
result = cursor.fetchone()
# Check if primary key is set and if there are no errors:
if result[0] > 1 and result[1] == '':
# Commit SP
cursor.execute('COMMIT')
return result[0]
There is a mapping for creating the models, because the MS-SQL
database expect different data then the normal objects, like ‘order’.
def adding_data_into_vadain(self, order):
for curtain in order.curtains.all():
order_id = WaOrders.export(
type=format(curtain.type)
)
# relation with default and vadain db.
order.exported_id = order_id
order.save()
The function is working proper by running the program, but by running ‘manage.py test’ will be created a test databases. This is given the following problems:
By creating test database is missing the south tables (this is also not needed in the legacy database)
By changing the SOUTH_TESTS_MIGRATE to False I’m getting the error message that the tables are already exists of the default database.
My test is as follow:
class AddDataServiceTest(TestCase):
fixtures = ['order']
def test_data_service(self):
# add data into vadain_import data
for order in Order.objects.all():
AddingDataService.adding_data_into_vadain(order)
# test the exported values
for order in Order.objects.all():
exported_order = WaOrders.objects.get(order_exported_id=order.exported_id)
self.assertEqual(exported_order.type, 'Pleat curtain')
Can somebody advise me how I can test the situation?
maybe inside your WaOrders you can extend save() method and call there export function.

Categories