Package: django-queryset-feeler
django-queryset-feelerGet a better feel for how your django views and serializers are accessing your app’s database. Use django-queryset-feeler (dqf) to measure the count, execution time, and raw SQL of your queries from the command line, ipython shell, or jupyter notebook without any configuration.This extension is used differently than the populardjango-debug-toolbarin a few ways. First, dqf can be used to profile more than just views. You can pass functions, querysets, model instances, views, class based views, anddjango-rest-frameworkserializers to dqf for profiling. Second, dqf profiles queries with only one object and can be used in the command line, ipython shell, or jupyter notebook. This is especially useful for prototyping or learning how django querysets are executed.Usagepip install django-queryset-feelerfromdjango_queryset_feelerimportFeelCreate aFeel()instance by passing it any one of the following objects from your django project. No other configuration is required.Query TypeAboutFeel(view)Execute a view using an empty HttpRequest. Add arequestkey word argument to supply your own request.Feel(ClassBasedView)Execute an eligible class based view using an empty HttpRequest with aGETmethod. Add arequestkey word argument to supply your own request.Feel(serializer)Execute a serializer on the model specified by the serializer's Meta class.Feel(queryset)Execute a querysetFeel(model_instance)Execute a model instance by calling it again from the database using.refresh_from_db()Feel(function)Execute a functionProfile your queries using any of the following properties.PropertyAboutfeel.query_timeRepeat the query 100 times (adjust iterations with theiterationskey word argument) and return the average query duration in seconds.feel.query_countExecute the query and return the number of times that the database was accessed.feel.sql_queriesExecute the query and return a formatted copy of the raw SQL.feel.table_countsExecute the query and return a dictionary containing each table and how many times it was accessed.feel.reportPrint the query time, count, and table count summary.ExampleThe below example illustrates an easy to make django queryset mistake called an 'n + 1 query' and how to use dqf to find it.project / app / models.pyclassTopping(models.Model):name=CharField()vegetarian=BooleanField()classPizza(models.Model):name=CharField()toppings=ManyToManyField(Topping)project / app / views.pydefpizza_list(request):pizzas=Pizza.objects.all()returnrender(request,'pizza_list.html'context={'pizzas':pizzas})project / app / templates / app / pizza_list.html{% for pizza in pizzas %}<tr><td>{{ pizza.name }}</td><td>{% for topping in pizza.toppings %}
        {{ topping.name }}
    {% endfor %}</td><td>{% with last=pizza.toppings|dictsort:'vegetarian'|last %}
        {% if last.vegetarian %}
            🌱
        {% else %}
            🥩
        {% endif %}
    {% endwith %}</td><tr>{% endfor %}PizzaToppingsmediterraneanroasted eggplant, balsamic glaze🌱hawaiianpineapple, smoked ham🥩meat loverspepperoni, andouille sausage, capicola🥩project / dqf.ipynbNote that theDEBUGsetting inproject / settings.pymust beTruefor dqf to work.DEBUGis enabled by default when you create a django project.fromdjango_queryset_feelerimportFeelfromapp.viewsimportpizza_listfeel=Feel(pizza_list)print(f'query count:{feel.query_count}')print(f'average duration:{feel.duration}s')print(feel.sql_queries)'query count: 4''average duration: 0.00023 s'SELECT"app_pizza"."id","app_pizza"."name",FROM"app_pizza"SELECT"app_topping"."id","app_topping"."name","app_topping"."vegetarian"FROM"app_topping"WHERE"app_topping"."id"='0'SELECT"app_topping"."id","app_topping"."name","app_topping"."vegetarian"FROM"app_topping"WHERE"app_topping"."id"='1'SELECT"app_topping"."id","app_topping"."name","app_topping"."vegetarian"FROM"app_topping"WHERE"app_topping"."id"='2'In the above example django queried the database a total of 4 times: once to get a list of pizzas and then again for each pizza to find its toppings. As more pizzas are added to the menu n + 1 queries would be made to the database where n is the number of pizzas.Note that even though the pizza's toppings are accessed once in column 2 for the name and again in column 3 to determine if the pizza is vegetarian the database is still accessed only once in this period. This is because after evaluation the results are stored in the queryset object and used for subsequent calls.A more efficient way to render this template would be to fetch the list of pizzas and then query the toppings table once to get all the toppings for all the pizzas. Django makes this easy usingprefetch_related().project / app / views.pydefpizza_list(request):pizzas=Pizza.objects.all().prefetch_related('toppings')returnrender(request,'pizza_list.html'context={'pizzas':pizzas})project / dqf.ipynbfeel=Feel(pizza_list)feel.reportquerycount:2averageduration:0.069msuniquetables:2accessedRun Django in a Jupyter Notebookproject / dqf.ipynb# re-import modules when a cell is run. This ensures that changes made to# the django app are synced with your notebook%load_extautoreload%autoreload2importdjangoimportos# change 'project.settings' to '{your_project}.settings'os.environ['DJANGO_SETTINGS_MODULE']='project.settings'os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"]="true"django.setup()