Serialize Haystack SearchQuerySet - python

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'
},
}

Related

I can connect an application to 2 databases in Django?

I have a web application in Python django. I need to import users and display data about them from another database, from another existing application. All I need is the user to be able to login and display information about them. What solutions are?
You can set 2 DATABASES in settings.py.
DATABASES = {
'default': {
...
},
'user_data': {
...
}
}
Then in one database store User models with authentication and stuff, in another rest information. You can connect information about specific User with a field that is storing id of User from another database.
If you have multiple databases and create a model, you should declare on which db it is going to be stored. If you didn't, it will be in default one (if you have it declared).
class UserModel(models.Model):
class Meta:
db_table = 'default'
class UserDataModel(models.Model):
class Meta:
db_table = 'user_data'
the answer from #NixonSparrow was wrong.
_meta.db_table defined only table_name in database and not the database self.
for switch database you can use manager.using('database_name'), for every model, it is good declared here: https://docs.djangoproject.com/en/4.0/topics/db/multi-db/#topics-db-multi-db-routing
in my project i use multiple router.
https://docs.djangoproject.com/en/4.0/topics/db/multi-db/#topics-db-multi-db-routing
it help don't override every manager with using. But in your case:
DATABASES = {
'default': {
...
},
'other_users_data': {
...
}
}
and somethere in views:
other_users = otherUserModel.objects.using('other_users_data')
Probably, otherUserModel should define in meta, which table you want to use db_table = 'other_users_table_name' and also probably it should have managed=False, to hide this model from migration manager.

How to use PostgreSQL's stored procedures or functions in Django project

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)

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/

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 haystack testing

I am trying to test that my search View renders the results from my search correctly, however, the search uses the indexes from my live database and not my test database, so when I create some objects for my test case, they are not reflected on the search page.
How can I make haystack use an index of the search database, or better still, just fake it and not use an index, but instead use the database as is. This would be fine for that test case and probably faster.
I could only seem to find this article when googling: http://reliablybroken.com/b/2012/12/testing-django-haystack-whoosh/
and it does not work with the current versions.
Pip versions:
django==1.7.5
django-haystack==2.4.0
I've encountered a similar use case in our project. Here's the rough idea of our implementation. Note that if you use the simple_backend (SB), some of your custom filters/prepare method may not work as expected. Hence, it's advisable to use non-SB backend (e.g. Elastic Search) even in testing mode.
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.utils import override_settings
from haystack import connections
from haystack.utils.loading import ConnectionHandler, UnifiedIndex
from myapp.models import MyModel
from myapp.search_indexes import MyModelIndex
TEST_INDEX = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:9200/',
'INDEX_NAME': 'test_index',
},
}
#override_settings(HAYSTACK_CONNECTIONS=TEST_INDEX )
class SearchViewTest(TestCase):
def setUp(self):
"""
Some ideas taken from here:
https://github.com/django-haystack/django-haystack/blob/v2.6.0/test_haystack/test_views.py#L40
"""
connections = ConnectionHandler(TEST_INDEX )
super(SearchViewTest, self).setUp()
self.mm = MyModel.objects.create(name='Dummy Title')
# Stow.
self.old_unified_index = connections['default']._index
self.ui = UnifiedIndex()
self.mmi = MyModelIndex()
self.ui.build(indexes=[self.mmi])
connections['default']._index = self.ui
# Update the 'index'.
backend = connections['default'].get_backend()
backend.clear()
backend.update(self.mmi, MyModel.objects.all())
def tearDown(self):
connections['default']._index = self.old_unified_index
super(SearchViewTest, self).tearDown()
def test_search_results(self):
response = self.client.get('/search?q=dummy')
self.assertIn(self.mm.name, response)
create a test index and make sure that your haystack configs point to the test index when in test mode.
Something like: if 'test' in sys.argv:
Besides this you can either add another index in the search engine or use another search engine (such as whoosh) for your tests.
The other option is to actually mock the response that you get from the search engine and pass that to the views / forms you are trying to test.

Categories