PaulGilmartin / django-queryhunter

Hunt down the lines of your Django application code which are responsible for executing the most queries.
BSD 3-Clause "New" or "Revised" License
138 stars 4 forks source link

Enable query hunter to setup custom connection #3

Open waldemarstal opened 2 months ago

waldemarstal commented 2 months ago

Hi,

I had a problem that queries aren't captured, but I realised that I need to setup connection to listen on one of my database which wasn't default. I changed locally this line of code: https://github.com/PaulGilmartin/django-queryhunter/blob/main/queryhunter/context_manager.py#L27 and it started to works. (I did locally small workaround like this self._pre_execute_hook = connection._connections["MY_DB"].execute_wrapper(self._query_hunter), but generally I can prepare PR if you want to enable possibility to setup connection to custom DB)

PaulGilmartin commented 2 months ago

@waldemarstal Thanks for your interest in the library! I would be happy to review a PR for such functionality. How are you thinking one would configure the db they want queryhunter to use?

waldemarstal commented 2 months ago

The simplest way in my opinio, which is backward compatibility is to change it like this:

from django.db import connections
...
class queryhunter(contextlib.ContextDecorator):
    def __init__(self, reporting_options: ReportingOptions = None, meta_data: dict[str, str] = None):
        self.database_name = getattr(settings, "QUERYHUNTER_DATABASE_NAME", "default")
        ...
        self._pre_execute_hook = connections[self.database_name].execute_wrapper(self._query_hunter)

default behaviour will be the same, and when somebody want to use your own database can setup in your settings optional env var QUERYHUNTER_DATABASE_NAME.

What do you think @PaulGilmartin?

PaulGilmartin commented 2 months ago

That seems like a good idea to me :)

waldemarstal commented 2 months ago

@PaulGilmartin I can't push my local branch to create a PR, so please give me a access or just create PR based on this:

===================================================================
diff --git a/queryhunter/tests/tests.py b/queryhunter/tests/tests.py
--- a/queryhunter/tests/tests.py    (revision afa54b3918d197e3ec16cf30d48f2d2d2b6e9550)
+++ b/queryhunter/tests/tests.py    (revision 0aa8956eb0f9c5e78c7a781c430335edfc23de9a)
@@ -34,6 +34,21 @@
     assert second_line.code == 'authors.append(post.author.name)'

+@pytest.mark.django_db(transaction=True)
+def test_queryhunter_with_custom_db_name(settings):
+    settings.QUERYHUNTER_DATABASE_NAME = "custom"
+    settings.DATABASES['custom'] = {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': 'custom_db.sqlite3',
+    }
+
+    create_posts()
+    with queryhunter(meta_data=dict(func='get_authors', username='Paul')) as qh:
+        get_authors()
+        assert qh.database_name == "custom"
+        assert len(qh.query_info) == 0
+
+
 @pytest.mark.django_db(transaction=True)
 def test_queryhunter_modules_reporting_options():
     create_posts()

===================================================================
diff --git a/queryhunter/context_manager.py b/queryhunter/context_manager.py
--- a/queryhunter/context_manager.py    (revision afa54b3918d197e3ec16cf30d48f2d2d2b6e9550)
+++ b/queryhunter/context_manager.py    (revision 0aa8956eb0f9c5e78c7a781c430335edfc23de9a)
@@ -1,7 +1,7 @@
 import contextlib

 from django.conf import settings
-from django.db import connection
+from django.db import connections

 from .queryhunter import QueryHunter
 from queryhunter.reporting import ReportingOptions, QueryHunterReporter, PrintingOptions
@@ -11,6 +11,7 @@
     def __init__(self, reporting_options: ReportingOptions = None, meta_data: dict[str, str] = None):
         if not hasattr(settings, 'QUERYHUNTER_BASE_DIR'):
             raise ValueError('QUERYHUNTER_BASE_DIR setting is required')
+        self.database_name = getattr(settings, "QUERYHUNTER_DATABASE_NAME", "default")

         self.meta_data = meta_data
         if reporting_options is None:
@@ -24,7 +25,7 @@
         self._query_hunter = QueryHunter(reporting_options=self._reporting_options, meta_data=self.meta_data)
         self.query_info = self._query_hunter.query_info
         self.reporter = QueryHunterReporter.create(queryhunter=self._query_hunter)
-        self._pre_execute_hook = connection.execute_wrapper(self._query_hunter)
+        self._pre_execute_hook = connections[self.database_name].execute_wrapper(self._query_hunter)

     def __enter__(self):
         self._pre_execute_hook.__enter__()

===================================================================
diff --git a/pyproject.toml b/pyproject.toml
--- a/pyproject.toml    (revision afa54b3918d197e3ec16cf30d48f2d2d2b6e9550)
+++ b/pyproject.toml    (revision 0aa8956eb0f9c5e78c7a781c430335edfc23de9a)
@@ -18,7 +18,7 @@
 packages = [
   { include = "queryhunter" }
 ]
-version = "0.1.6"
+version = "0.2.0"
 description = "Map your Django application code to the SQL queries it produces."
 authors = ["Paul Gilmartin"]

===================================================================
diff --git a/README.md b/README.md
--- a/README.md (revision afa54b3918d197e3ec16cf30d48f2d2d2b6e9550)
+++ b/README.md (revision 0aa8956eb0f9c5e78c7a781c430335edfc23de9a)
@@ -276,3 +276,8 @@

 Adding custom meta data can be particularly useful when you want to associate an 
 identifier with the profiling data.
+
+## Custom Database Backends
+
+`QUERYHUNTER_DATABASE_NAME` can be set in the settings.py file to specify the name of the database to profile.
+Default database is used if not specified.
PaulGilmartin commented 1 month ago

@waldemarstal That's odd, it's a public repository so I thought you'd have access automatically. What error do you get? I'll try to see if I have any permissions set.

Edit: I checked and it seems you should only need read permissions to write to a branch and propose a PR: see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request and https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork

Did you do something similar to what's described in those links?

waldemarstal commented 1 month ago

@PaulGilmartin I see this error:

ERROR: Permission to PaulGilmartin/django-queryhunter.git denied to waldemarstal. fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

From the lin which you shared:

Note: To open a pull request in a public repository, you must have write access to the head or the source branch or, for organization-owned repositories, you must be a member of the organization that owns the repository to open a pull request.

You have to add me to repo as collaborator to enable me creating PR

PaulGilmartin commented 1 month ago

@waldemarstal I believe you first need to fork the repository, as was mentioned in the link and also in https://stackoverflow.com/questions/44415291/how-to-create-a-git-pull-request-on-a-public-github-repository. Did you fork the repo and try that method? I have never needed to add individual permissions before when people contribute to my repos, so I think it must be possible without it.