def test_with_app_models(self): call_command('create_session', 'simple', '2') from .simple.models import Player with use_cache(): players = Player.objects.all() self.assertEqual(len(players), 2) group = players[0].group group.save = Mock() group.round_number += 1 # Query session object to test that it's loaded.. group.session participants = group.session.participant_set.all() all_instances = { players[0], players[1], group, group.session, participants[0], participants[1] } self.assertEqual(set(_get_save_objects_model_instances()), all_instances) # No queries are executed. The group model shall be saved, but we # mocked out the save method. All other models should be left # untouched. with self.assertNumQueries(0): save_objects() self.assertTrue(group.save.called)
def live_method_stuff(self, player_bot, submission): PageClass = submission['page_class'] live_method_name = PageClass.live_method if live_method_name: record = (player_bot.player.group_id, PageClass) if record not in self.executed_live_methods: with idmap.use_cache(): bots_module = inspect.getmodule(player_bot) method_calls_fn = getattr(bots_module, 'call_live_method', None) if method_calls_fn: players = { p.id_in_group: p for p in player_bot.group.get_players() } def method(id_in_group, data): return getattr(players[id_in_group], live_method_name)(data) method_calls_fn( method=method, case=player_bot.case, round_number=player_bot.round_number, page_class=PageClass, ) self.executed_live_methods.add(record)
def test_nested_changes(self): call_command('create_session', 'simple', '1') # Reset cache. with use_cache(): # Query participant via session. session = Session.objects.get() participant = session.participant_set.get() participant.is_on_wait_page = not participant.is_on_wait_page # Save participant. with self.assertNumQueries(1): save_objects()
def test_nested_changes(self): call_command('create_session', 'simple', '1') # Reset cache. with use_cache(): # Query participant via session. session = Session.objects.get() participant = session.participant_set.get() participant.is_on_wait_page = not participant.is_on_wait_page # Save participant. with self.assertNumQueries(1): save_objects()
def test_dont_save_if_no_change(self): call_command('create_session', 'simple', '1') with use_cache(): participant = Participant.objects.get() # We keep track of the participant. instances = _get_save_objects_model_instances() self.assertEqual(instances, [participant]) # But we won't save the participant since we didn't change it. with self.assertNumQueries(0): save_objects()
def test_save_only_changed_fields(self): call_command('create_session', 'simple', '1') with use_cache(): participant = Participant.objects.get() with mock.patch.object(django.db.models.Model, 'save') as patched_save: save_objects() # in save-the-change 2017 version, save is not called at all patched_save.assert_called_once_with(update_fields=[]) participant.code = 'hello' save_objects() # but is this necessarily the only argument? no patched_save.assert_called_with(update_fields=['code'])
def test_dont_save_if_no_change(self): call_command('create_session', 'simple', '1') with use_cache(): participant = Participant.objects.get() # We keep track of the participant. instances = _get_save_objects_model_instances() self.assertEqual(instances, [participant]) # But we won't save the participant since we didn't change it. with self.assertNumQueries(0): save_objects()
def test_save_only_changed_fields(self): call_command('create_session', 'simple', '1') with use_cache(): participant = Participant.objects.get() with mock.patch.object(django.db.models.Model, 'save') as patched_save: save_objects() # in save-the-change 2017 version, save is not called at all patched_save.assert_called_once_with(update_fields=[]) participant.code = 'hello' save_objects() # but is this necessarily the only argument? no patched_save.assert_called_with(update_fields=['code'])
def test_not_lazy(self): with use_cache(): page = Page() participant = Participant.objects.get() # 2 queries: for all objects, and for player_lookup with self.assertNumQueries(2): page.set_attributes(participant) with self.assertNumQueries(0): _ = page.player.id _ = page.group.id _ = page.session.id _ = page.subsession.id _ = page.participant.id
def mock_exogenous_data(self): ''' It's for any exogenous data: - participant labels (which are not passed in through REST API) - participant vars - session vars (if we enable that) ''' if self.config.get('mock_exogenous_data'): import shared_out as user_utils with idmap.use_cache(): user_utils.mock_exogenous_data(self) # need to save self because it's not in the idmap cache self.save()
def test_lazy(self): with use_cache(): page = Page() participant = Participant.objects.get() # set_attributes causes player_lookups # only makes 1 query, for player_lookup with self.assertNumQueries(1): page.set_attributes(participant, lazy=True) # query only when we need it with self.assertNumQueries(1): _ = page.player.id # cached in idmap with self.assertNumQueries(0): _ = page.player.id
def test_lazy(self): with use_cache(): page = Page() participant = Participant.objects.get() # set_attributes causes player_lookups # only makes 1 query, for player_lookup with self.assertNumQueries(1): page.set_attributes(participant, lazy=True) # query only when we need it with self.assertNumQueries(1): _ = page.player.id # cached in idmap with self.assertNumQueries(0): _ = page.player.id
def test_not_lazy(self): with use_cache(): page = Page() participant = Participant.objects.get() # 2 queries: for all objects, and for player_lookup with self.assertNumQueries(2): page.set_attributes(participant) with self.assertNumQueries(0): _ = page.player.id _ = page.group.id _ = page.session.id _ = page.subsession.id _ = page.participant.id
def get(self, request, participant_code): participant = get_object_or_404(Participant, code=participant_code) if participant._index_in_pages == 0: participant._index_in_pages = 1 participant.visited = True # participant.label might already have been set participant.label = participant.label or self.request.GET.get( otree.constants.participant_label) now = django.utils.timezone.now() participant.time_started = now participant._last_page_timestamp = time.time() participant.save() with idmap.use_cache(): player = participant._get_current_player() player.start() first_url = participant._url_i_should_be_on() return HttpResponseRedirect(first_url)
def test_strong_refs(self): def inside_function(): '''do these queries in a function so we can know use_strong_refs is working correctly ''' player = simple_models.Player.objects.get( session_id=self.session_id, id_in_group=1 ) player.nonexistent_attribute = 'temp_value' group = player.group subsession = simple_models.Subsession.objects.get( session_id=self.session_id) session = player.session # do a query rather than FK lookup player.participant # to avoid participant from being cached on player # we want to make sure it actually goes into the IDmap cache participant = session.participant_set.first() with use_cache(): inside_function() with self.assertNumQueries(0): # if you use get() by PK, it skips the query entirely player = simple_models.Player.objects.get(id=self.player_id) self.assertEqual(player.nonexistent_attribute, 'temp_value') group = player.group subsession = player.subsession session = player.session # this shouldn't do a query because we know the participant PK # (it's player.participant_id), and the participant # should have been stored in the IDmap cache participant = player.participant with self.assertNumQueries(1): # this does a query because we don't know the pk. # but after retrieving the object, it checks the cache # if that item already exists. player = simple_models.Player.objects.get( session_id=self.session_id, id_in_group=1 )
def test_strong_refs(self): def inside_function(): '''do these queries in a function so we can know use_strong_refs is working correctly ''' player = simple_models.Player.objects.get( session_id=self.session_id, id_in_group=1 ) player.nonexistent_attribute = 'temp_value' group = player.group subsession = simple_models.Subsession.objects.get( session_id=self.session_id) session = player.session # do a query rather than FK lookup player.participant # to avoid participant from being cached on player # we want to make sure it actually goes into the IDmap cache participant = session.participant_set.first() with use_cache(): inside_function() with self.assertNumQueries(0): # if you use get() by PK, it skips the query entirely player = simple_models.Player.objects.get(id=self.player_id) self.assertEqual(player.nonexistent_attribute, 'temp_value') group = player.group subsession = player.subsession session = player.session # this shouldn't do a query because we know the participant PK # (it's player.participant_id), and the participant # should have been stored in the IDmap cache participant = player.participant with self.assertNumQueries(1): # this does a query because we don't know the pk. # but after retrieving the object, it checks the cache # if that item already exists. player = simple_models.Player.objects.get( session_id=self.session_id, id_in_group=1 )
def test_with_app_models(self): call_command('create_session', 'simple', '2') from .simple.models import Player with use_cache(): players = Player.objects.all() self.assertEqual(len(players), 2) group = players[0].group group.save = Mock() group.round_number += 1 # Query session object to test that it's loaded.. group.session participants = group.session.participant_set.all() all_instances = { players[0], players[1], group, group.session, participants[0], participants[1] } self.assertEqual( set(_get_save_objects_model_instances()), all_instances) # No queries are executed. The group model shall be saved, but we # mocked out the save method. All other models should be left # untouched. with self.assertNumQueries(0): save_objects() self.assertTrue(group.save.called)
def query_and_call(): with idmap.use_cache(): new_model = cls.objects.get(id=self.id) callback.__func__.__get__(new_model, cls)(*args, **kwargs)
def live_payload_function(participant_code, page_name, payload): participant = Participant.objects.get(code=participant_code) lookup = get_page_lookup(participant._session_code, participant._index_in_pages) app_name = lookup.app_name models_module = otree.common.get_models_module(app_name) PageClass = lookup.page_class # this could be incorrect if the player advances right after liveSend is executed. # maybe just return if it doesn't match. (but leave it in for now and see how much that occurs, # don't want silent failures.) if page_name != PageClass.__name__: logger.warning( f'Ignoring liveSend message from {participant_code} because ' f'they are on page {PageClass.__name__}, not {page_name}.') return live_method_name = PageClass.live_method with idmap.use_cache(): player = models_module.Player.objects.get( round_number=lookup.round_number, participant=participant) # it makes sense to check the group first because # if the player forgot to define it on the Player, # we shouldn't fall back to checking the group. you could get an error like # 'Group' has no attribute 'live_auction' which would be confusing. # also, we need this 'group' object anyway. # and this is a good place to show the deprecation warning. group = player.group if hasattr(group, live_method_name): method = getattr(group, live_method_name) retval = method(player.id_in_group, payload) else: method = getattr(player, live_method_name) retval = method(payload) if not retval: return if not isinstance(retval, dict): msg = f'{live_method_name} must return a dict' raise LiveMethodBadReturnValue(msg) pcodes_dict = { d['id_in_group']: d['participant__code'] for d in models_module.Player.objects.filter( group=group).values('participant__code', 'id_in_group') } if 0 in retval: if len(retval) > 1: raise LiveMethodBadReturnValue( 'If dict returned by live_method has key 0, it must not contain any other keys' ) else: for pid in retval: if pid not in pcodes_dict: msg = f'live_method has invalid return value. No player with id_in_group={repr(pid)}' raise LiveMethodBadReturnValue(msg) pcode_retval = {} for pid, pcode in pcodes_dict.items(): payload = retval.get(pid, retval.get(0)) if payload is not None: pcode_retval[pcode] = payload _live_send_back(participant._session_code, participant._index_in_pages, pcode_retval)