Django 2.2 'SafeText' object has no attribute 'get' - django

I am using Django 2.2 in a project. I am rolling an extra lightweight app that allows users to modify the raw HTML used in a template.
This is a cut down example of the function I have written to do that:
def show_page(request):
from django.template import Context, Template
template = Template("<h3>My name is {{ my_name }}.</h3>")
context = Context({"my_name": "Adrian"})
return template.render(context) # <- Django barfs at this line
I get the exception shown in the title:
AttributeError at /show-page 'SafeText' object has no attribute 'get'
How do I resolve this error, so I can display the page correctly?

From Django documentation
Each view you write is responsible for instantiating, populating, and returning an HttpResponse.
Currently you are returning a django.utils.safestring.SafeString which is just a subclassed string
A str subclass that has been specifically marked as "safe" for HTML
output
purposes.
return HttpResponse(template.render(context))

Assuming show_page is a Django view, you should be returning a Response object, not the template string.
Change your view by adding:
from django.http import HttpResponse
return HttpResponse(template.render(context))

Related

Correct way of implementing custom 404 page in Django 3 in production

I’ve tried importing and editing handler404 in urls.py, made sure pointed to right template etc but kept getting server 500 error responses. The only way I could get it to work was changing return render( to return HttpResponseNotFound( but in that case I only get the text representation of ‘mysite.views.404_error.html’ as need to return HTML directly with HttpResponseNotFound. Wondering what is the correct way to return a custom 404 error template. Thanks a lot.
We can render the template with render_to_string(…) and then wrap the result in a HttpResponseNotFound:
from django.http import HttpResponseNotFound
from django.template.loader import render_to_string
def my_404_handler(request, exception, template_name='404.html'):
context = {} # some context
content = render_to_string(
'some-template-name.html',
context,
request
)
return HttpResponseNotFound(content)
We here thus render a template some-template-name.html, and the content; the result of the function, is then returned with a HttpResponseNotFound.
For me the only solution that worked (Django 3.7) dev/prod is here
Please see #elano7 answer

Why does Django need a request object in rendering a template?

Why does Django need a request object in rendering a template?
return render(request, 'polls/index.html', context)
As per the docs about render:
Combines a given template with a given context dictionary and returns
an HttpResponse object with that rendered text.
Thus it's meant to be used in views, where you have a request object and need to return an HttpResponse. A typical use case is when you build the context from the request.
If you only need to render a template, you can use the shortcut function render_to_string:
from django.template.loader import render_to_string
render_to_string('your_template.html', {'some_key':'some_value'})
Or do it manually:
from django.template import Context, Template
Template('your_template.html').render(Context({'some_key':'some_value'})
The request argument is used if you want to use a RequestContext which is usually the case when you want to use template context processors. You can pass in None as the request argument if you want and you will get a regular Context object in your template.
I believe this is b/c the render() shortcut is using a RequestContext
You could also use get_template directly and call render with a normal Context

Can I have a CMSPluginBase class method directly return a custom plugin's html?

I would like to create a Django CMS plugin primarily used as a child of a TextPlugin (djangocms-text-ckeditor). It is meant to return a link to an application page.
To this end I subclass CMSPluginBase as described in the doc. It seems that the CMSPluginBase is relying on each plugin to have its own template.
Do I have to have a template.html file or can I write a method for the CMSPluginBase subclass that directly returns the rendered html (essentially something really simple like 'App link') and avoid the invocation of a template to be rendered?
Thanks very much for helping out!
Figured it out!
It seems that the render_template does not have to be a string. It can be a django.template.Template instance, too. So, here we go:
from django.template import Template
class MyLinkPlugin(CMSPluginBase):
render_template = Template('{{anchor}}')
def render(self, context, instance, placeholder):
context['link']='http://google.com'
context['anchor'] = 'Google me'
return(context)

Get Django views.py to return and execute javascript

So I'm working with django and file uploads and I need a javascript function to execute after the file has been uploaded.
I have a file upload handler in my views.py which looks like this:
def upload_file(request):
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
for f in request.FILES.getlist('fileAttachments'):
handle_uploaded_file(f)
return HttpJavascriptResponse('parent.Response_OK();')
else:
return HttpResponse("Failed to upload attachment.")
And I found a django snippet from http://djangosnippets.org/snippets/341/ and I put the HttpJavascriptResponse class in my views.py code. It looks as follows:
class HttpJavascriptResponse(HttpResponse):
def __init__(self,content):
HttpResponse.__init__(self,content,mimetype="text/javascript")
However, when I upload a file the browser simple renders "parent.Response_OK();" on the screen instead of actually executing the javascript. And Chrome gives me the warning: "Resource interpreted as Document but transferred with MIME type text/javascript"
Is there anyway to get views.py to execute the script?
A better way is to pass the mime to the HttpResponse Object.
See documentation: https://docs.djangoproject.com/en/3.2/ref/request-response/#django.http.HttpRequest.content_type.
return HttpResponse("parent.Response_OK()", content_type="application/x-javascript")
Note - Some previous versions of Django used mimetype instead of content_type:
return HttpResponse("parent.Response_OK()", mimetype="application/x-javascript")
I believe this will work.
return HttpResponse("<script>parent.Response_OK();</script>")
However, you might think about returning a success (200) status code in this case, and then having some javascript in parent attach to the load event of this child, and branch based on return status code. That way you have a separation of view rendering code and view behavior code.
Chase's solution worked for me, though I need to execute more javascript than I care to put in a python string:
from django.http import HttpResponse
from django.contrib.staticfiles.templatetags import staticfiles
...
return HttpResponse("<script src='{src}'></script>".format(
src = staticfiles.static('/path/to/something.js')))
I ended up here looking for a way to serve a dynamic js file using django.
Here is my solution :
return render(request, 'myscript.js', {'foo':'bar'},
content_type="application/x-javascript")

Django - Rendering Inclusion Tag from a View

so I was wondering if you could return an inclusion tag directly from a view.
The page is a normal page with a list of items. The list of items is rendered using an inclusion tag. If an ajax request is made to the view, I want to return only what the inclusion tag would return so I can append it onto the page via javascript. Is something like this possible? Or should I architect this better?
This sounds like a job for the render_to_string or render_to_response shortcuts:
https://docs.djangoproject.com/en/dev/ref/templates/api/#the-render-to-string-shortcut
https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#django.shortcuts.render_to_response
We can still use the inclusion tag to generate a template context dict from our args (as helpfully pointed out by #BobStein-VisiBone in comments)
from django.template.loader import render_to_string
from django.shortcuts import render_to_response
from .templatetags import my_inclusion_tag
rendered = render_to_string('my_include.html', my_inclusion_tag(foo='bar'))
#or
def my_view(request):
if request.is_ajax():
return render_to_response('my_include.html',
my_inclusion_tag(foo='bar'),
context_instance=RequestContext(request))
quick and dirty way could be to have a view, that renders a template, that only contains your templatetag.
I was trying to do the exact same thing today, and ended up writing a helper function to render template tags:
def render_templatetag(request, tag_string, tag_file, dictionary=None):
dictionary = dictionary or {}
context_instance = RequestContext(request)
context_instance.update(dictionary)
t = Template("{%% load %s %%}{%% %s %%}" % (tag_file, tag_string))
return t.render(context_instance)
tag_string is the call to the template tag as it'd appear in a normal template, and tag_file is the file you need to load for the template tag.
This is certainly possible. A view can render any template that an inclusion tag can. The advantage of this approach is that you can leverage the full strength of Django's templates.
On the other hand AJAX responses typically do not contain HTML so much as XML/JSON. If you are using Django's template features you are better off with returning HTML. If not, XML/JSON might make more sense. Just my two cents.
Using inclusion tag with configurable template decorator provided by Gonz instead of default Django's inclusion tags you can write a helper in your view:
from django.template.loader import render_to_string
from home.templatetags.objectwidgets import object_widget
def _object_widget(request, obj):
template, context = object_widget(RequestContext(request), obj)
return render_to_string(template, context)
It's DRY and works with Django 1.3 (I haven't tested it with Django 1.4).
I use this technique to return search results rendered in HTML via JSON/AJAX calls (nothing better which I came up with).
It's possible, but probably bit hacky, complex or complicated.
What I would suggest, is architect it so that you have the rendering part in utils.py function and use it in a simple_tag instead of an inclusion_tag. This way you can then use the same utils rendering function easily in views.
In my (very simplified) imaginary example I have listing of users and a "Load more" button that returns more users.
account/utils.py
from django.template.loader import render_to_string
def render_users(users):
return render_to_string("account/user_list_items.html", {"users": users})
account/templatetags/account_tags.py
from django import template
from ..utils import render_users
register = template.Library()
#register.simple_tag
def list_users(users):
return render_users(users)
account/views.py
from django.http import HttpResponse
from .models import User
from .utils import render_users
def load_more_users(request):
limit = request.GET["limit"]
offset = request.GET["offset"]
users = User.objects.all()[offset:offset + limit]
return HttpResponse(render_users(users))
Simple is better than complex.