From 3566f8b064efc63e7de18f2f5f05c0c861fc41a0 Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 15:39:02 +0100 Subject: [PATCH 1/8] refs #54, add django perf rec into django-formidable until the sqllite driver is not compatible with django_perf_rec https://github.com/YPlan/django-perf-rec/issues/50, we have to provide an overrided sqllite driver backend to be able to record verbosely each recorded SQL line. --- demo/demo/settings.py | 14 ++++++- demo/django18_sqlite3_backend/__init__.py | 3 ++ demo/django18_sqlite3_backend/base.py | 14 +++++++ demo/django18_sqlite3_backend/operations.py | 43 +++++++++++++++++++++ demo/requirements-demo.pip | 1 + 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 demo/django18_sqlite3_backend/__init__.py create mode 100644 demo/django18_sqlite3_backend/base.py create mode 100644 demo/django18_sqlite3_backend/operations.py diff --git a/demo/demo/settings.py b/demo/demo/settings.py index 043ae08e..4e0b88d0 100644 --- a/demo/demo/settings.py +++ b/demo/demo/settings.py @@ -12,6 +12,7 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os +import django BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -80,9 +81,14 @@ # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases +if django.VERSION[:2] == (1, 8): + engine = 'django18_sqlite3_backend' +else: + engine = 'django.db.backends.sqlite3', + DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', + 'ENGINE': engine, 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } @@ -122,3 +128,9 @@ FORMIDABLE_POST_UPDATE_CALLBACK_SUCCESS = 'demo.callback_success_message' FORMIDABLE_POST_CREATE_CALLBACK_FAIL = 'demo.callback_fail_message' FORMIDABLE_POST_UPDATE_CALLBACK_FAIL = 'demo.callback_fail_message' + + +# django-perf-rec settings +PERF_REC = { + 'MODE': 'all' +} diff --git a/demo/django18_sqlite3_backend/__init__.py b/demo/django18_sqlite3_backend/__init__.py new file mode 100644 index 00000000..4ef7f3f9 --- /dev/null +++ b/demo/django18_sqlite3_backend/__init__.py @@ -0,0 +1,3 @@ +# -*- coding:utf-8 -*- +# flake8: noqa +from __future__ import absolute_import, division, print_function, unicode_literals diff --git a/demo/django18_sqlite3_backend/base.py b/demo/django18_sqlite3_backend/base.py new file mode 100644 index 00000000..042a1545 --- /dev/null +++ b/demo/django18_sqlite3_backend/base.py @@ -0,0 +1,14 @@ +# -*- coding:utf-8 -*- +# flake8: noqa +from __future__ import absolute_import, division, print_function, unicode_literals + +from django.db.backends.sqlite3.base import DatabaseWrapper as OrigDatabaseWrapper + +from .operations import DatabaseOperations + + +class DatabaseWrapper(OrigDatabaseWrapper): + + def __init__(self, *args, **kwargs): + super(DatabaseWrapper, self).__init__(*args, **kwargs) + self.ops = DatabaseOperations(self) diff --git a/demo/django18_sqlite3_backend/operations.py b/demo/django18_sqlite3_backend/operations.py new file mode 100644 index 00000000..e5b3fa40 --- /dev/null +++ b/demo/django18_sqlite3_backend/operations.py @@ -0,0 +1,43 @@ +# -*- coding:utf-8 -*- +# flake8: noqa +from __future__ import absolute_import, division, print_function, unicode_literals + +from django.db.backends.sqlite3.operations import DatabaseOperations as OrigDatabaseOperations + + +class DatabaseOperations(OrigDatabaseOperations): + + # From Django commit 4f6a7663bcddffb114f2647f9928cbf1fdd8e4b5 + + def _quote_params_for_last_executed_query(self, params): + """ + Only for last_executed_query! Don't use this to execute SQL queries! + """ + sql = 'SELECT ' + ', '.join(['QUOTE(?)'] * len(params)) + # Bypass Django's wrappers and use the underlying sqlite3 connection + # to avoid logging this query - it would trigger infinite recursion. + cursor = self.connection.connection.cursor() + # Native sqlite3 cursors cannot be used as context managers. + try: + return cursor.execute(sql, params).fetchone() + finally: + cursor.close() + + def last_executed_query(self, cursor, sql, params): + # Python substitutes parameters in Modules/_sqlite/cursor.c with: + # pysqlite_statement_bind_parameters(self->statement, parameters, allow_8bit_chars); + # Unfortunately there is no way to reach self->statement from Python, + # so we quote and substitute parameters manually. + if params: + if isinstance(params, (list, tuple)): + params = self._quote_params_for_last_executed_query(params) + else: + keys = params.keys() + values = tuple(params.values()) + values = self._quote_params_for_last_executed_query(values) + params = dict(zip(keys, values)) + return sql % params + # For consistency with SQLiteCursorWrapper.execute(), just return sql + # when there are no parameters. See #13648 and #17158. + else: + return sql diff --git a/demo/requirements-demo.pip b/demo/requirements-demo.pip index 22dba92d..1ed1dd76 100644 --- a/demo/requirements-demo.pip +++ b/demo/requirements-demo.pip @@ -2,3 +2,4 @@ django-cors-headers ipdb django-extensions freezegun +django-perf-rec From ed4ec6b847f1fc194bef550b42f01b8528188af6 Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 15:41:34 +0100 Subject: [PATCH 2/8] refs #54, add sql test recorder on retrieve context form from API --- demo/tests/perfs/tests_integration.perf.yml | 9 ++++++++ demo/tests/tests_integration.py | 23 +++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100755 demo/tests/perfs/tests_integration.perf.yml diff --git a/demo/tests/perfs/tests_integration.perf.yml b/demo/tests/perfs/tests_integration.perf.yml new file mode 100755 index 00000000..8910da1a --- /dev/null +++ b/demo/tests/perfs/tests_integration.perf.yml @@ -0,0 +1,9 @@ +TestContextFormEndPoint.test_queryset: +- db: 'SELECT ... FROM "django_session" WHERE ("django_session"."expire_date" > # AND "django_session"."session_key" = #)' +- db: 'SELECT ... FROM "formidable_formidable" WHERE "formidable_formidable"."id" = #' +- db: SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" IN (#) ORDER BY "formidable_field"."order" ASC +- db: 'SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" = #' +- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."access_id" = # AND NOT ("formidable_access"."level" = #) AND "formidable_access"."field_id" IN (...))' +- db: SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" IN (...) ORDER BY "formidable_item"."order" ASC +- db: SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" IN (...) +- db: SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" IN (...) diff --git a/demo/tests/tests_integration.py b/demo/tests/tests_integration.py index 684cdcdc..56ccf33b 100644 --- a/demo/tests/tests_integration.py +++ b/demo/tests/tests_integration.py @@ -248,6 +248,29 @@ class MyForm(FormidableForm): ) +class TestContextFormEndPoint(APITestCase): + + @classmethod + def setUpClass(cls): + class MyTestForm(MyForm): + phone = fields.IntegerField() + + super(TestContextFormEndPoint, cls).setUpClass() + cls.form = MyForm.to_formidable(label='test') + + def test_queryset(self): + import django_perf_rec + + session = self.client.session + session['role'] = 'padawan' + session.save() + + with django_perf_rec.record(path='perfs/'): + self.client.get(reverse( + 'formidable:context_form_detail', args=[self.form.pk]) + ) + + class TestValidationEndPoint(APITestCase): def setUp(self): From 60d028abe3e0380814c5fed983b7c5c70d90ff1e Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 15:42:07 +0100 Subject: [PATCH 3/8] refs #54, disambiguate the URL's in order to fetch context form --- demo/tests/perfs/tests_integration.perf.yml | 15 +++++++++++---- formidable/urls.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/demo/tests/perfs/tests_integration.perf.yml b/demo/tests/perfs/tests_integration.perf.yml index 8910da1a..2868ddb7 100755 --- a/demo/tests/perfs/tests_integration.perf.yml +++ b/demo/tests/perfs/tests_integration.perf.yml @@ -2,8 +2,15 @@ TestContextFormEndPoint.test_queryset: - db: 'SELECT ... FROM "django_session" WHERE ("django_session"."expire_date" > # AND "django_session"."session_key" = #)' - db: 'SELECT ... FROM "formidable_formidable" WHERE "formidable_formidable"."id" = #' - db: SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" IN (#) ORDER BY "formidable_field"."order" ASC -- db: 'SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" = #' +- db: 'SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" = # ORDER BY "formidable_field"."order" ASC' - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."access_id" = # AND NOT ("formidable_access"."level" = #) AND "formidable_access"."field_id" IN (...))' -- db: SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" IN (...) ORDER BY "formidable_item"."order" ASC -- db: SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" IN (...) -- db: SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" IN (...) +- db: 'SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" = #' +- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' +- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' +- db: 'SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" = # ORDER BY "formidable_item"."order" ASC' +- db: 'SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" = #' +- db: 'SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" = #' +- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' +- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' +- db: 'SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" = # ORDER BY "formidable_item"."order" ASC' +- db: 'SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" = #' diff --git a/formidable/urls.py b/formidable/urls.py index 57259a9c..3d6357e7 100644 --- a/formidable/urls.py +++ b/formidable/urls.py @@ -4,7 +4,7 @@ urlpatterns = [ url(r'^forms/(?P\d+)/$', views.ContextFormDetail.as_view(), - name='form_detail'), + name='context_form_detail'), url(r'^forms/(?P\d+)/validate/$', views.ValidateView.as_view(), name='form_validation'), url(r'^builder/forms/(?P\d+)/$', views.FormidableDetail.as_view(), From 96f3930c225f662c38e82b382346579abb28c0a2 Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 15:43:57 +0100 Subject: [PATCH 4/8] refs #54, order items in the fields serializer --- demo/tests/perfs/tests_integration.perf.yml | 9 +++------ formidable/serializers/fields.py | 8 ++++++-- formidable/serializers/items.py | 5 ----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/demo/tests/perfs/tests_integration.perf.yml b/demo/tests/perfs/tests_integration.perf.yml index 2868ddb7..748aa897 100755 --- a/demo/tests/perfs/tests_integration.perf.yml +++ b/demo/tests/perfs/tests_integration.perf.yml @@ -4,13 +4,10 @@ TestContextFormEndPoint.test_queryset: - db: SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" IN (#) ORDER BY "formidable_field"."order" ASC - db: 'SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" = # ORDER BY "formidable_field"."order" ASC' - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."access_id" = # AND NOT ("formidable_access"."level" = #) AND "formidable_access"."field_id" IN (...))' -- db: 'SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" = #' +- db: SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" IN (...) ORDER BY "formidable_item"."order" ASC +- db: SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" IN (...) +- db: SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" IN (...) - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' -- db: 'SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" = # ORDER BY "formidable_item"."order" ASC' -- db: 'SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" = #' -- db: 'SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" = #' - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' -- db: 'SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" = # ORDER BY "formidable_item"."order" ASC' -- db: 'SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" = #' diff --git a/formidable/serializers/fields.py b/formidable/serializers/fields.py index 12963696..3a82f1eb 100644 --- a/formidable/serializers/fields.py +++ b/formidable/serializers/fields.py @@ -8,7 +8,7 @@ from rest_framework import serializers from formidable import constants -from formidable.models import Access, Field +from formidable.models import Access, Field, Item from formidable.register import FieldSerializerRegister, load_serializer from formidable.serializers.access import AccessSerializer from formidable.serializers.child_proxy import LazyChildProxy @@ -95,7 +95,11 @@ def get_attribute(self, instance): qs = super(ListContextFieldSerializer, self).get_attribute(instance) access_qs = Access.objects.filter(access_id=self.role) access_qs = access_qs.exclude(level=constants.HIDDEN) - qs = qs.prefetch_related(Prefetch('accesses', queryset=access_qs)) + qs = qs.prefetch_related( + Prefetch('accesses', queryset=access_qs), + Prefetch('items', queryset=Item.objects.order_by('order')), + 'validations', 'defaults', + ) return qs def to_representation(self, fields): diff --git a/formidable/serializers/items.py b/formidable/serializers/items.py index 8aab39e9..c6f3f4eb 100644 --- a/formidable/serializers/items.py +++ b/formidable/serializers/items.py @@ -20,11 +20,6 @@ def validate(self, data): return data - def to_representation(self, items): - return super(ItemListSerializer, self).to_representation( - items.order_by('order') - ) - class ItemSerializer(serializers.ModelSerializer): From 63d9cf877ff161d1f1520e4449c165de2fa7e444 Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 15:44:55 +0100 Subject: [PATCH 5/8] refs #54, order fields in form serializer Conflicts: demo/tests/perfs/tests_integration.perf.yml --- demo/tests/perfs/tests_integration.perf.yml | 5 ----- formidable/serializers/fields.py | 20 ++++++++++++++------ formidable/views.py | 5 ----- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/demo/tests/perfs/tests_integration.perf.yml b/demo/tests/perfs/tests_integration.perf.yml index 748aa897..7a2a18c7 100755 --- a/demo/tests/perfs/tests_integration.perf.yml +++ b/demo/tests/perfs/tests_integration.perf.yml @@ -1,13 +1,8 @@ TestContextFormEndPoint.test_queryset: - db: 'SELECT ... FROM "django_session" WHERE ("django_session"."expire_date" > # AND "django_session"."session_key" = #)' - db: 'SELECT ... FROM "formidable_formidable" WHERE "formidable_formidable"."id" = #' -- db: SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" IN (#) ORDER BY "formidable_field"."order" ASC - db: 'SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" = # ORDER BY "formidable_field"."order" ASC' - db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."access_id" = # AND NOT ("formidable_access"."level" = #) AND "formidable_access"."field_id" IN (...))' - db: SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" IN (...) ORDER BY "formidable_item"."order" ASC - db: SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" IN (...) - db: SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" IN (...) -- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' -- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' -- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' -- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."field_id" = # AND "formidable_access"."access_id" = #)' diff --git a/formidable/serializers/fields.py b/formidable/serializers/fields.py index 3a82f1eb..0a9b646b 100644 --- a/formidable/serializers/fields.py +++ b/formidable/serializers/fields.py @@ -100,11 +100,11 @@ def get_attribute(self, instance): Prefetch('items', queryset=Item.objects.order_by('order')), 'validations', 'defaults', ) - return qs + return qs.order_by('order') def to_representation(self, fields): res = [] - for field in fields.order_by('order').all(): + for field in fields.all(): if field.accesses.exists(): res.append(self.child.to_representation(field)) @@ -133,12 +133,20 @@ def role(self): return self._context['role'] def get_disabled(self, obj): - return obj.accesses.get(access_id=self.role).level == \ - constants.READONLY + # accesses object are already loaded, a "get" a related object will + # hit the database, a "all" not. + for access in obj.accesses.all(): + if self.role == access.access_id: + return access.level == constants.READONLY + return False def get_required(self, obj): - return obj.accesses.get(access_id=self.role).level == \ - constants.REQUIRED + # accesses object are already loaded, a "get" a related object will + # hit the database, a "all" not. + for access in obj.accesses.all(): + if self.role == access.access_id: + return access.level == constants.REQUIRED + return False class FieldItemMixin(object): diff --git a/formidable/views.py b/formidable/views.py index 11127dbf..055fdbd8 100644 --- a/formidable/views.py +++ b/formidable/views.py @@ -197,11 +197,6 @@ class ContextFormDetail(six.with_metaclass(MetaClassView, RetrieveAPIView)): serializer_class = ContextFormSerializer settings_permission_key = 'FORMIDABLE_PERMISSION_USING' - def get_queryset(self): - qs = super(ContextFormDetail, self).get_queryset() - field_qs = Field.objects.order_by('order') - return qs.prefetch_related(Prefetch('fields', queryset=field_qs)) - def get_serializer_context(self): context = super(ContextFormDetail, self).get_serializer_context() context['role'] = get_context(self.request, self.kwargs) From 3fd8f123277d6b2acd9edf2ca01bce66757b2f81 Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 16:07:47 +0100 Subject: [PATCH 6/8] refs #54, add sql tests on serializer only --- demo/tests/perfs/test_end_point.perf.yml | 6 ++++++ demo/tests/test_end_point.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100755 demo/tests/perfs/test_end_point.perf.yml diff --git a/demo/tests/perfs/test_end_point.perf.yml b/demo/tests/perfs/test_end_point.perf.yml new file mode 100755 index 00000000..4d3ec59a --- /dev/null +++ b/demo/tests/perfs/test_end_point.perf.yml @@ -0,0 +1,6 @@ +RenderContextSerializer.test_queryset: +- db: 'SELECT ... FROM "formidable_field" WHERE "formidable_field"."form_id" = # ORDER BY "formidable_field"."order" ASC' +- db: 'SELECT ... FROM "formidable_access" WHERE ("formidable_access"."access_id" = # AND NOT ("formidable_access"."level" = #) AND "formidable_access"."field_id" IN (...))' +- db: SELECT ... FROM "formidable_item" WHERE "formidable_item"."field_id" IN (...) ORDER BY "formidable_item"."order" ASC +- db: SELECT ... FROM "formidable_validation" WHERE "formidable_validation"."field_id" IN (...) +- db: SELECT ... FROM "formidable_default" WHERE "formidable_default"."field_id" IN (...) diff --git a/demo/tests/test_end_point.py b/demo/tests/test_end_point.py index 9c67a49d..afba1e50 100644 --- a/demo/tests/test_end_point.py +++ b/demo/tests/test_end_point.py @@ -5,6 +5,7 @@ from functools import reduce from django.test import TestCase +import django_perf_rec from formidable import constants from formidable.models import Formidable @@ -298,6 +299,21 @@ class TestForm(FormidableForm): defaults = field['defaults'] self.assertEqual(defaults, ['Roméo']) + def test_queryset(self): + + class TestForm(FormidableForm): + name = fields.CharField(label='Your name', default='Roméo') + label = fields.CharField(label='label', default='Roméo') + salary = fields.IntegerField() + birthdate = fields.DateField() + + form = TestForm.to_formidable(label='title') + + serializer = ContextFormSerializer(form, context={'role': 'jedi'}) + + with django_perf_rec.record(path='perfs/'): + serializer.data + class CreateSerializerTestCase(TestCase): From 128902d7ad9f01ba907edd9a7db0203f355ab4e2 Mon Sep 17 00:00:00 2001 From: Guillaume Camera Date: Sun, 15 Jan 2017 16:40:36 +0100 Subject: [PATCH 7/8] refs #54, use the prefetch to check access directly --- formidable/serializers/fields.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/formidable/serializers/fields.py b/formidable/serializers/fields.py index 0a9b646b..1f03e954 100644 --- a/formidable/serializers/fields.py +++ b/formidable/serializers/fields.py @@ -105,7 +105,9 @@ def get_attribute(self, instance): def to_representation(self, fields): res = [] for field in fields.all(): - if field.accesses.exists(): + # Avoid to hit the database, the righ access is currently loaded, + # unless its an hidden access + if field.accesses.count() > 0: res.append(self.child.to_representation(field)) return res @@ -133,20 +135,24 @@ def role(self): return self._context['role'] def get_disabled(self, obj): - # accesses object are already loaded, a "get" a related object will - # hit the database, a "all" not. - for access in obj.accesses.all(): - if self.role == access.access_id: - return access.level == constants.READONLY - return False + # accesses object are already loaded through prefetch inside the + # "get_attribute" method, a "get" on related object will + # hit the database, a "all" method not. + # With the prefetch method and the "exists" check at the + # ListContextFieldSerializer.to_representation method, you are sure + # to have the access matching the role + access = obj.accesses.all()[0] + return access.level == constants.READONLY def get_required(self, obj): - # accesses object are already loaded, a "get" a related object will - # hit the database, a "all" not. - for access in obj.accesses.all(): - if self.role == access.access_id: - return access.level == constants.REQUIRED - return False + # accesses object are already loaded through prefetch inside the + # "get_attribute" method, a "get" on related object will + # hit the database, a "all" method not. + # With the prefetch method and the "exists" check at the + # ListContextFieldSerializer.to_representation method, you are sure + # to have the access matching the role + access = obj.accesses.all()[0] + return access.level == constants.REQUIRED class FieldItemMixin(object): From b04343a2edf85b73b35ae2bcb0b4040a38d492f8 Mon Sep 17 00:00:00 2001 From: Bruno Bord Date: Mon, 16 Jan 2017 15:59:54 +0100 Subject: [PATCH 8/8] Added django-perf-rec module for tests and improved SQL queries in `ContextFormDetailView` --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0b91285c..eb118cac 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,7 @@ master (unreleased) =================== * Added a make target to install the demo site. +* Added django-perf-rec module for tests and improved SQL queries in `ContextFormDetailView` (#54, #154, #160). Release 0.5.0 (2017-01-10)