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.
Related
I am working on one Django project. And I decided to write logic code in PostgreSQL instead of writing in Python. So, I created a stored procedure in PostgreSQL. For example, a stored procedure looks like this:
create or replace procedure close_credit(id_loan int)
language plpgsql
as $$
begin
update public.loan_loan
set sum = 0
where id = id_loan;
commit;
end;$$
Then in settings.py, I made the following changes:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'pawnshop',
'USER': 'admin',
'PASSWORD': password.database_password,
'HOST': 'localhost',
'PORT': '',
}
}
So the question is, How can I call this stored procedure in views.py?
p.s.
Maybe it sounds like a dumb question, but I really couldn't find any solution in Django.
I would recommend storing the procedure definition in a migration file. For example, in the directory myapp/migrations/sql.py:
from django.db import migrations
SQL = """
create procedure close_credit(id_loan int)
language plpgsql
as $$
begin
update public.loan_loan
set sum = 0
where id = id_loan;
commit;
end;$$
"""
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [migrations.RunSQL(SQL)]
Note: you will need to replace myapp with the name of your application, and you will need to include only the most recent migration file for your app as a dependency.
Now you can install the procedure using python3 manage.py migrate.
Once your procedure is defined in the database, you can call it using cursor.callproc:
from django.db import connection
def close_credit(id_loan):
with connection.cursor() as cursor:
cursor.callproc('close_credit', [id_loan])
All that being said, if your procedure is really as trivial as the example you provided, it would cost much less in maintenance to write the equivalent using the ORM:
Loan.objects.filter(id=id_loan).update(sum=0)
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/
I have some Django queries dumped in files that are delayed so I pass as parameter sql_with_params to later execute in the delayed a raw query.
I have migrated all queries to haystack so I wan't to do the same with SearchQuerySet.
Is there any way to get the raw_query of an already constructed SearchQuerySet?
PD: I am using ElasticSearch
Sure, here's one way that unfortunately requires a bit of plumbing. You can create a custom search engine and set its query to your own query definition inheriting from ElasticsearchSearchQuery:
from haystack.backends.elasticsearch_backend import ElasticsearchSearchEngine, ElasticsearchSearchQuery
class ExtendedElasticsearchSearchQuery(ElasticsearchSearchQuery):
def build_query(self):
raw_query = super(ExtendedElasticsearchSearchQuery, self).build_query()
# TODO: Do something with raw query
return raw_query
class ExtendedElasticsearchSearchEngine(ElasticsearchSearchEngine):
query = ExtendedElasticsearchSearchQuery
and reference that from your settings:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'myapp.mymodule.ExtendedElasticsearchSearchEngine',
'URL': 'http://localhost:9200/',
'INDEX_NAME': 'haystack'
},
}
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')
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()