def get_formset(self, request, obj=None, **kwargs): # sidestep validation which wants to inherit from BaseModelFormSet self.formset = EditRegionInlineFormSet fset = super(EditRegionInline, self).get_formset(request, obj, **kwargs) modeladmin = get_modeladmin(EditRegionChunk, self.admin_site.name) if obj is not None and request.method == 'POST': # As I won't remember why we have to do this, later, this is the # traceback which not doing it caused: # https://gist.github.com/kezabelle/40653a0ad1ffd8fc77ba # Basically, the template gets changed half way through the request # because of the different points at which objects are saved. # By not relying on the new instance (with the changed template) # instead using the one in the DB (with the old template) we can # ensure the regions line up correctly. logger.info('Editing an %(obj)r; we may be changing the region ' 'group being used, so re-grabbing the DB version') obj = obj.__class__.objects.get(pk=obj.pk) fset.region_changelists = modeladmin.get_changelists_for_object( request=request, obj=obj, config=None) fset.editregion_config = get_configuration(obj=obj) # this bind is necessary for the template to emit dynamic # template changes. fset.editregions_template_fieldname = getattr( get_modeladmin(obj), 'editregions_template_field', None) return fset
def test_continue_editing_parent_object(self): """ if continue editing is hit, it should go back to the parent URL, I think? """ user = User(username='******', is_staff=True, is_superuser=True, is_active=True) user.set_password('test') user.full_clean() user.save() admin_instance = get_modeladmin(Iframe) self.assertIsInstance(admin_instance, RealishAdmin) request = RequestFactory().get('/', { '_continue': 1, }) request.user = user iframe_admin = reverse('admin:embeds_iframe_add') response_301 = HttpResponsePermanentRedirect(redirect_to=iframe_admin) ct = get_content_type(User) iframe = Iframe(position=2, region='test', content_type=ct, content_id=user.pk, url='https://news.bbc.co.uk/') iframe.full_clean() iframe.save() new_response = admin_instance.maybe_fix_redirection( request=request, response=response_301, obj=iframe) self.assertEqual(new_response['X-Chunkadmin-Response'], 'redirect-to-parent') self.assertEqual(301, new_response.status_code) self.assertEqual('/admin_mountpoint/auth/user/1/?_data_changed=1', new_response['Location'])
def test_render_into_region(self): theadmin = get_modeladmin(self.file) out = theadmin.render_into_region(obj=self.file, context=Context({ 'chunkloop': {'object': self.file} })) self.assertIn('a href="x/y/z.gif?t=', out) self.assertIn('>x</a>', out)
def test_render_into_region(self): theadmin = get_modeladmin(self.model) obj = self.model(local='x/y') context = Context() # nothing is output now, because we want to # render_into_mediagroup instead self.assertIsNone(theadmin.render_into_region(obj=obj, context=context))
def _test_view(self, func='add_view', generate_chunks=1): user = User(username='******', is_staff=True, is_superuser=True, is_active=True) user.set_password('test') user.full_clean() user.save() ct = get_content_type(User) request = RequestFactory().get('/', { REQUEST_VAR_CT: ct.pk, REQUEST_VAR_ID: user.pk, REQUEST_VAR_REGION: 'test' }) request.user = user admin_instance = get_modeladmin(Iframe) for x in range(0, generate_chunks): iframe = Iframe(position=2, region='test', content_type=ct, content_id=user.pk, url='https://news.bbc.co.uk/') iframe.full_clean() iframe.save() kwargs = {'request': request} if func != 'add_view': kwargs.update({'object_id': force_text(iframe.pk)}) view = getattr(admin_instance, func) view(**kwargs) # now do the view again without the fields required by the decorator request = RequestFactory().get('/') request.user = user kwargs.update({'request': request}) with self.assertRaises(SuspiciousOperation): view(**kwargs)
def test_render_into_summary(self): theadmin = get_modeladmin(self.model) obj = self.model() obj.url = """<rss version="2.0"> <channel> <title>Sample Feed</title> </channel> </rss>""" self.assertEqual(theadmin.render_into_summary(obj=obj, context={}), 'Sample Feed')
def test_leave_unchanged(self): request = RequestFactory().get('/') response_200 = HttpResponse(content='ok') admin_instance = get_modeladmin(Iframe) new_response = admin_instance.maybe_fix_redirection( request=request, response=response_200) # returned unchanged self.assertEqual(new_response['X-Chunkadmin-Response'], 'early') self.assertEqual(force_text('ok'), force_text(new_response.content)) self.assertEqual(200, new_response.status_code)
def configure(self, obj): self.obj = obj self.ct = get_content_type(obj) modeladmin = get_modeladmin(self.obj) if hasattr(modeladmin, 'get_editregions_template_choices'): self.valid_templates = modeladmin.get_editregions_template_choices( obj=self.obj) possible_templates = modeladmin.get_editregions_templates( obj=self.obj) self.set_template(possible_templates)
def get_changelists_for_object(self, request, obj, **kwargs): changelists = [] if obj is not None: logger.debug('Editing `{obj!r}`, so do ' '`get_changelists_for_object`'.format(obj=obj)) attach_configuration(obj, EditRegionConfiguration) config = get_configuration(obj) # Dynamic template changes ... obj_admin = get_modeladmin(admin_namespace=self.admin_site.name, obj=obj) if hasattr(obj_admin, 'editregions_template_field'): fieldname = obj_admin.editregions_template_field template_name = request.GET.get(fieldname, None) kv = TemplateRequestKeyValue(key=fieldname, value=template_name) if config.is_valid_template(template_name): logger.debug("{kv!r} was valid for this {obj!r} " "and {modeladmin!r}".format( kv=kv, obj=obj, modeladmin=obj_admin)) config.set_template(template_name) # store the old get here, because it gets changed inside the region # loops, which is a lossy process. old_get = request.GET # mutate the querystring and set some data onto it, which will # be passed to the get_changelist_filters method, as well as # being used to filter the ChangeList correctly. # new_get = request.GET.copy() new_get = QueryDict('', mutable=True) new_get[REQUEST_VAR_CT] = get_content_type(obj).pk new_get[REQUEST_VAR_ID] = obj.pk for region in config.config: new_get[REQUEST_VAR_REGION] = region request.GET = new_get our_list_display = self.list_display[:] our_list_links = self.get_list_display_links( request=request, list_display=our_list_display) ChangeList = self.get_changelist(request, **kwargs) cl = ChangeList(request=request, model=self.model, list_display=our_list_display, list_display_links=our_list_links, list_filter=self.list_filter, date_hierarchy=None, search_fields=None, list_select_related=None, list_per_page=100, list_max_show_all=100, list_editable=None, model_admin=self, parent_obj=obj, parent_conf=config) changelists.append(cl) # as the internal request.GET may be lossy, we restore the original # data here. request.GET = old_get return changelists
def get_response_add_context(self, request, obj): """ Override the default contexts generated by AdminlinksMixin to add our HTML. """ modeladmin = get_modeladmin(EditRegionChunk, self.admin_site.name) changelists = modeladmin.render_changelists_for_object( request=request, obj=obj.content_object) context = super(ChunkAdmin, self).get_response_add_context(request, obj) context.update(html=changelists) return context
def setUp(self): try: admin.site.unregister(EditRegionChunk) except NotRegistered: pass admin.site.register(EditRegionChunk, EditRegionAdmin) self.admin = get_modeladmin(EditRegionChunk) try: admin.site.unregister(Iframe) except NotRegistered: pass admin.site.register(Iframe, IframeAdmin)
def render_one_mediagroup(context, chunk, extra, renderer=None): # we could just let the errors bubble up, but instead we'll provide more # helpful error messages than one might otherwise get (AttributeError for # no render_into_region, TypeError for calling render_into_region because of # it being unbound method (got RequestContext instance instead)) if renderer is None: logger.debug('No renderer given as an argument, fetching the ' 'ModelAdmin instance for the first time') renderer = get_modeladmin(chunk) if hasattr(renderer, 'render_into_mediagroup'): return renderer.render_into_mediagroup(context=context, obj=chunk, extra=extra) return None
def test_render_into_region(self): theadmin = get_modeladmin(self.model) obj = self.model(content='var x;') # fake the iteration context context = Context({ 'chunkloop': { 'object': obj, } }) # nothing is output now, because we want to # render_into_mediagroup instead result = theadmin.render_into_region(obj=obj, context=context) self.assertIsNone(result)
def setUp(self): sample_user, created = User.objects.get_or_create(username='******') user_ct = get_content_type(sample_user) sr = SearchResults(position=1, content_type=user_ct, content_id=sample_user.pk, region='test', connection='default', query="goose fat") sr.full_clean() try: admin.site.unregister(SearchResults) except NotRegistered: pass admin.site.register(SearchResults, SearchResultsAdmin) self.modeladmin = get_modeladmin(SearchResults) self.obj = sr
def setUp(self): sample_user, created = User.objects.get_or_create(username='******') user_ct = get_content_type(sample_user) mlt = MoreLikeThis(position=1, content_type=user_ct, content_id=sample_user.pk, region='test', connection='default') mlt.full_clean() try: admin.site.unregister(MoreLikeThis) except NotRegistered: pass admin.site.register(MoreLikeThis, MoreLikeThisAdmin) self.modeladmin = get_modeladmin(MoreLikeThis) self.obj = mlt
def test_returned_data_changed(self): """ Just check that the `_data_changed` parameter is added the response. """ request = RequestFactory().get('/') admin_instance = get_modeladmin(Iframe) response_302 = HttpResponseRedirect(redirect_to='/admin_mountpoint/') new_response = admin_instance.maybe_fix_redirection( request=request, response=response_302) # returned early because it was a redirect, but we updated the # querystring anyway self.assertEqual(new_response['X-Chunkadmin-Response'], 'early') self.assertEqual(302, new_response.status_code) self.assertEqual('/admin_mountpoint/?_data_changed=1', new_response['Location'])
def render_one_chunk(context, chunk, extra, renderer=None): # we could just let the errors bubble up, but instead we'll provide more # helpful error messages than one might otherwise get (AttributeError for # no render_into_region, TypeError for calling render_into_region because of # it being unbound method (got RequestContext instance instead)) if renderer is None: logger.debug('No renderer given as an argument, fetching the ' 'ModelAdmin instance for the first time') renderer = get_modeladmin(chunk) if not hasattr(renderer, 'render_into_region'): msg = ('{0.__class__!r} does not have a `render_into_region` ' 'method'.format(renderer)) raise ImproperlyConfigured(msg) return renderer.render_into_region(context=context, obj=chunk, extra=extra)
def test_save_model(self): theadmin = get_modeladmin(self.model) obj = self.model(position=1) obj.url = """<rss version="2.0"> <channel> <title>Sample Feed</title> </channel> </rss>""" request = RequestFactory().get('/', data={ REQUEST_VAR_CT: 1, REQUEST_VAR_ID: 1, REQUEST_VAR_REGION: 'test' }) theadmin.save_model(request=request, obj=obj, form=Form(), change=False)
def get_object_tools(self, obj): """ Show the modifiers for this object. Currently just implements the drag handle as per `django-treeadmin`_. :return: the list of actions or tools available for this object :rtype: string """ modeladmin = get_modeladmin(obj) if hasattr(modeladmin, 'get_editregions_subclass_tools'): value = modeladmin.get_editregions_subclass_tools(obj=obj) else: value = '' return '<div class="chunk-object-tools">{value!s}</div>'.format( value=value)
def test_logging(self): user = User(username='******') user.set_password('test') user.full_clean() user.save() ct = get_content_type(User) iframe = Iframe(position=2, region='test', content_type=ct, content_id=user.pk, url='https://news.bbc.co.uk/') iframe.full_clean() iframe.save() request = RequestFactory().get('/') request.user = user admin_instance = get_modeladmin(Iframe) admin_instance.log_addition(request, iframe) admin_instance.log_change(request, iframe, "we changed a thing!") admin_instance.log_deletion(request, iframe, "we deleted a thing!") # find them on the user logs = LogEntry.objects.get(content_type=ct, object_id=user.pk, user=user, action_flag=ADDITION) self.assertEqual(force_text(logs), 'Added "test".') logs = LogEntry.objects.get(content_type=ct, object_id=user.pk, user=user, action_flag=CHANGE) self.assertEqual(force_text(logs), 'Changed "test" - we changed a ' 'thing!') # can't check for deletions properly, see # https://code.djangoproject.com/ticket/21771#ticket # logs = LogEntry.objects.get(content_type=ct, object_id=user.pk, # user=user, action_flag=DELETION) # self.assertEqual(force_text(logs), 'Changed "test" - we changed a ' # 'thing!') # find them on the iframe ct = get_content_type(Iframe) logs = LogEntry.objects.get(content_type=ct, object_id=iframe.pk, user=user, action_flag=ADDITION) self.assertEqual(force_text(logs), 'Added "https://news.bbc.co.uk/".') logs = LogEntry.objects.get(content_type=ct, object_id=iframe.pk, user=user, action_flag=CHANGE) self.assertEqual(force_text(logs), 'Changed "https://news.bbc.co.uk/" ' '- we changed a thing!') # can't check for deletions properly, see # https://code.djangoproject.com/ticket/21771#ticket logs = LogEntry.objects.filter(content_type=ct, object_id=user.pk, user=user, action_flag=DELETION) self.assertEqual(force_text(logs[0]), 'Deleted "we deleted a thing!."')
def test_render_into_region(self): theadmin = get_modeladmin(self.model) obj = self.model() # fake the url into a string parse. obj.url = """<rss version="2.0"> <channel> <title>Sample Feed</title> </channel> </rss>""" # fake the iteration context context = Context({ 'chunkloop': { 'object': obj, } }) self.assertIn('<span>Sample Feed</span>', theadmin.render_into_region(obj=obj, context=context))
def get_response_delete_context(self, request, obj_id, extra_context): """ Override the default contexts generated by AdminlinksMixin to add our HTML. """ modeladmin = get_modeladmin(EditRegionChunk, self.admin_site.name) context = super(ChunkAdmin, self).get_response_delete_context( request, obj_id, extra_context) try: changelists = modeladmin.render_changelists_for_object( request=request, obj=extra_context['gfk']['content_object']) context.update(html=changelists) except KeyError as e: # extra context didn't include gfk, or possibly content_object within # that gfk key, either way, we now can't render the HTML for the # client :( pass return context
def test_to_other_url(self): """ Going to a non-chunkadmin URL should be ok, and should also put the `_data_changed` parameter onto the URL. """ user = User(username='******', is_staff=True, is_superuser=True, is_active=True) user.set_password('test') user.full_clean() user.save() request = RequestFactory().get('/') response_302 = HttpResponseRedirect(redirect_to='/admin_mountpoint/') admin_instance = get_modeladmin(Iframe) new_response = admin_instance.maybe_fix_redirection( request=request, response=response_302, obj=user) self.assertEqual(new_response['X-Chunkadmin-Response'], 'not-chunkadmin') # noqa self.assertEqual(302, new_response.status_code) self.assertEqual('/admin_mountpoint/?_data_changed=1', new_response['Location'])
def get_subclass_type(self, obj): """ get the verbose name of the given object, which is likely a subclass .. note:: By using this callable, we avoid the problem of being able to sort by headers in the changelists (including on the change form) :return: the subclass object's verbose name :rtype: string """ modeladmin = get_modeladmin(obj) if hasattr(modeladmin, 'get_editregions_subclass_type'): value = modeladmin.get_editregions_subclass_type(obj=obj) else: value = obj._meta.verbose_name value = strip_tags(force_text(value)) return self.get_changelist_link_html(obj, data=value, caller='subclass')
def get_ancestors_instead(self, context, region_name, content_object): # make sure we have the damn method we need. try: parents = content_object.get_ancestors() except AttributeError as e: parents = None if parents is None: try: modeladmin = get_modeladmin(content_object) parents = modeladmin.get_ancestors(obj=content_object) except (NotRegistered, AttributeError) as e: # parents will remain None pass if parents is None: # doesn't have ancestors conforming to the mptt/treebeard # API, so it's probably a custom model that is BROKEN. error = ("{cls!r}, or the ModelAdmin for it, should implement " "`get_ancestors` to use the 'inherit' argument for " "this template tag".format( cls=content_object.__class__)) if settings.DEBUG: raise ImproperlyConfigured(error) logger.error(error, exc_info=1) return () # if there are parents, see if we can get values from them. for distance, parent in enumerate(reversed(parents), start=1): attach_configuration(parent, EditRegionConfiguration) parent_erc = get_configuration(parent) parent_results = self.fetch(parent_erc, region=region_name) chunks = tuple(self.do_render(context, parent_results)) chunk_count = len(chunks) if chunk_count > 0: logging.info("Found {1} chunks after {0} iterations over " "objects in `get_ancestors`".format( distance, chunk_count)) # stop processing further, we found some results! return chunks logging.debug("Inheriting from an ancestor yielded nothing") return ()
def test_save_model(self): user = User(username='******') user.set_password('test') user.full_clean() user.save() ct = get_content_type(User) iframe = Iframe(position=2, region='test', content_type=ct, content_id=user.pk, url='https://news.bbc.co.uk/') iframe.full_clean() iframe.save() request = RequestFactory().get('/', data={ 'region': 'test', 'content_type': ct.pk, 'content_id': user.pk, }) admin_instance = get_modeladmin(Iframe) expected_query_count = 2 if is_django_16plus() else 4 with self.assertNumQueries(expected_query_count): result = admin_instance.save_model(request=request, obj=iframe, form=ModelForm, change=True) self.assertIsNone(result)
def test_autoclose_chunkadmin(self): """ If `_autoclose` is in the URL, that + `_data_changed` should propagate to the next redirect URL for the purposes of our adminlinks JS. """ user = User(username='******', is_staff=True, is_superuser=True, is_active=True) user.set_password('test') user.full_clean() user.save() admin_instance = get_modeladmin(Iframe) self.assertIsInstance(admin_instance, RealishAdmin) request = RequestFactory().get('/', { '_autoclose': 1, }) request.user = user iframe_admin = reverse('admin:embeds_iframe_add') response_301 = HttpResponsePermanentRedirect(redirect_to=iframe_admin) ct = get_content_type(User) iframe = Iframe(position=2, region='test', content_type=ct, content_id=user.pk, url='https://news.bbc.co.uk/') iframe.full_clean() iframe.save() new_response = admin_instance.maybe_fix_redirection( request=request, response=response_301, obj=iframe) self.assertEqual(new_response['X-Chunkadmin-Response'], 'autoclose') self.assertEqual(301, new_response.status_code) location, querystring = new_response['Location'].split('?') self.assertEqual('/admin_mountpoint/embeds/iframe/add/', location) self.assertIn('region=test', querystring) self.assertIn('_data_changed=1', querystring) self.assertIn('_autoclose=1', querystring) self.assertIn('content_type={0}'.format(ct.pk), querystring) self.assertIn('content_id={0}'.format(iframe.pk), querystring)
def get_subclass_summary(self, obj): """ show a brief, HTML aware summary of the content. .. note:: By using this callable, we avoid the problem of being able to sort by headers in the changelists (including on the change form) :return: short representation of the data, HTML included. :rtype: string """ modeladmin = get_modeladmin(obj) if hasattr(modeladmin, 'get_editregions_subclass_summary'): value = modeladmin.get_editregions_subclass_summary(obj=obj) elif hasattr(modeladmin, 'render_into_summary'): context = chunk_iteration_context(index=0, value=obj, iterable=(obj,)) context.update({'admin_summary': True}) value = modeladmin.render_into_summary(obj=obj, context=context) else: value = '[missing]' value = strip_tags(force_text(value)) return self.get_changelist_link_html(obj, data=value, caller='summary')
def test_render_into_summary_no_data(self): self.file.data = None theadmin = get_modeladmin(self.file) out = theadmin.render_into_summary(obj=self.file, context=Context()) self.assertEqual(out, 'x')
def test_render_into_summary(self): theadmin = get_modeladmin(self.file) out = theadmin.render_into_summary(obj=self.file, context=Context()) self.assertEqual(out, 'x (z.gif)')