Can we send the HttpResponse of django `render()` in a python dictionary? - python

What my use case is I need whole render() data into a dictionary which will be having other key values too and finally I can return it as a normal Response.
Let suppose my code is:
from django.shortcuts import render
def my_view(request):
# View code here...
return render(request, 'myapp/index.html', {
'foo': 'bar',
}, content_type='application/xhtml+xml')
Now what we are doing here is: render is basically returning a HttpResponse which we are returning.
What I need is:
Save the return response in a variable
x = render(request, 'myapp/index.html', {
'foo': 'bar',
}, content_type='application/xhtml+xml')
Then can we save it in a dictionary to return as a Response? Like this
y = {}
y = {name: 'testing', render_response: x}
return y

You cant return a plain dictionary from a view, it should return a HttpResponse object. You can return a JsonResponse from your view. Like #Daniel mentioned in comments, use render_to_string to get response in string format.
from django.template.loader import render_to_string
from django.http import JsonResponse
def my_view(request):
# View code here...
response = render_to_string('myapp/index.html', {'foo': 'bar'}, request=request)
context = {'name': 'testing', 'render_response': response}
return JsonResponse(context)

To answer your question: yes, you can. Your code, which I am rewriting here, is absolutely valid:
from django.shortcuts import render
def my_view(request):
x = render(
request,
'myapp/index.html',
{'foo': 'bar'},
content_type='application/xhtml+xml'
)
y = {name: 'testing', render_response: x}
return y
That said, you have to keep in mind that my_view is no longer a valid Django view. It is just a function that takes a request object and returns a dictionary (with an HttpResponse as one of its values).
For that reason, you will not be able to use this function in places where view functions are expected, such as in urlpatterns. A possible use of this function would be within a valid view function (which retrieves the returned HttpResponse object and returns it directly).

Related

Django 1.11.17 TypeError: 'context must be a dict rather than Context', except IT IS A DICT

I have recently switched from Django 1.9 to 1.11.17 and one thing is bothering me a lot. There is this error that says
TypeError at /somepath
context must be a dict rather than Context
The line that is throwing it is:
return render(request=request, template_name="mytemplate.html", context={"form": form, "update": updateType})
There are many answers on SO where people use RequestContext or Context instead of dict for context and switching to dict solves their problem. But not for me. Here I am pretty sure that my context is in fact a dict. What is interesting if I change it to:
return render(request=request, template_name="mytemplate.html", context={})
The error goes away, but obviously causes another error later on. Do you guys have any idead on what am I doing wrong here?
EDIT:
My imports:
from django.shortcuts import render, render_to_response
from django.template.context import RequestContext, Context
I have tried bot render and render_to_response with similar effect. Also using Context or RequestContext gave similar error.
EDIT2: More code for reference
from django.http import (
HttpResponseRedirect,
HttpResponseBadRequest,
)
from django.shortcuts import render, render_to_response
from django.template import RequestContext, Context
from django.utils.html import escape
# some more imports, but from local files, not django
def update_my_template(request):
user = request.user
# preform some checks for user
...
if request.method == "GET":
updateType = request.GET.get("id")
if updateType:
form = None
if updateType == "something":
form = SomeForm(user)
if updateType == "something else":
form = DifferentForm()
if form is None:
return HttpResponseRedirect("/somepage")
# This was the code that worked in 1.9
rctx = RequestContext(
request, {"form": form, "update": updateType}
)
return render_to_response("mytemplate.html", rctx)
# some different cases, but the error is thrown already
...
Neither of these work:
dictctx = {"form": form, "update": updateType}
return render(request=request, template_name="mytemplate.html", dictctx)
.
ctx = Context({"form": form, "update": updateType})
return render(request=request, template_name="mytemplate.html", ctx)
.
ctx = Context({"form": form, "update": updateType})
return render(request=request, template_name="mytemplate.html", ctx.flatten())
.
rctx = RequestContext(request, {"form": form, "update": updateType})
return render_to_response("mytemplate.html", rctx.flatten())
The render logic is different, depending on what you pass to render:
def render(self, context):
"Display stage -- can be called many times"
with context.render_context.push_state(self):
if context.template is None:
with context.bind_template(self):
context.template_name = self.name
return self._render(context)
else:
return self._render(context)
and it looks as though you may be able to change your parameter template_name to just be name but your object doesn't have a context.render_context value which is why it would be better to create and use an instance of a Context
https://docs.djangoproject.com/en/1.11/_modules/django/template/base/#Template.render
The docs show passing an actual instance of a Context so I recommend that you do that in your code instead of just passing a dict:
>>> from django.template import Context, Template
>>> template = Template("My name is {{ my_name }}.")
>>> context = Context({"my_name": "Adrian"})
>>> template.render(context)
"My name is Adrian."
>>> context = Context({"my_name": "Dolores"})
>>> template.render(context)
so the easiest way to fix your code would be something like this:
from django.template import Context
...
return render(request=request, template_name="mytemplate.html", context=Context({"form": form, "update": updateType}))
Ok, after some more digging (in "unresolved" questions) I found this gem. And yep, that was solution to my problem. Basically I had the line {{form|bootstrap}} in my mytemplate.html which was causing this.
Even better, updating the django-bootstrap-form to version 3.4 allowed me to keep the {{form|bootstrap}} and get rid of the error.
Always pass the variables/values in parameters. But You give both at a same time. Try this as,...
return render(request=request, template_name="mytemplate.html",
{"form": form, "update": updateType})
Or
context={"form": form, "update": updateType} return
render(request=request, template_name="mytemplate.html",context)

Django JsonRequest in generic view always returns a string

(using Django2.0)
This is my first time trying to collaborate with a frond-end developer and I am trying to serialize a Django model from a generic ListView. Even though I manage to send a JsonResponse with my objects as json, they are always a string:
"[{\"model\": \"questions.question\", \"pk\": 9535, \"fields\": {\"created\": \"2018-04-14T17:02:38.559Z\", \"modified\": \"2018-04-14T18:04:14.264Z\", \"question\": \"TEST\", \"category\": \"Rules\", \"event\": \"Beyonce\", \"answer\": \"aergaergaergaer\", \"verified\": true, \"verified_by\": [\"someotheruser\"], \"count\": 0, \"user_created\": [\"someuser\"]}}]"
the way the front-end developer solved this issue is by calling a JSON.parse(). (see: https://www.w3schools.com/js/js_json_parse.asp).
Is this the correct way to do it or should I return the objects without a string?
If I am wrong and there is a way to do this without the strings here is my view and url:
views.py:
from events.models import Event
from django.core import serializers
from django.http import JsonResponse
class EventView(LoginRequiredMixin, ListView):
login_url = '/accounts/login/'
model = Question
template_name = 'faq/faq.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
events_list = Event.objects.all()
context['events_list'] = events_list
return context
def get_queryset(self):
event = Event.objects.get(event=self.kwargs['event'])
queryset = Question.objects.filter(event=event)
return queryset
def get(self, request, *args, **kwargs):
queryset = self.get_queryset()
data = serializers.serialize("json", queryset, use_natural_foreign_keys=True)
return JsonResponse(data, status=200, safe=False)
urls.py
urlpatterns += [
path('<str:event>/', EventView.as_view(), name='event'),
]
What I also tried:
def EventRequest(request, **kwargs):
event = Event.objects.get(event=kwargs['event'])
queryset = Question.objects.filter(event=event)
data = serializers.serialize("json", queryset, use_natural_foreign_keys=True)
dump = json.dumps(data)
return HttpResponse(dump, content_type='application/json')
and:
def EventRequest(request, **kwargs):
event = Event.objects.get(event=kwargs['event'])
queryset = Question.objects.filter(event=event)
data = serializers.serialize("json", queryset, use_natural_foreign_keys=True)
return JsonResponse(data, status=200, safe=False)
Once again this could be absolutely correct and the front-end developer should just do a JSON.parse(). Please let me know, thanks!
This is normal. JSON is a string notation. It is just a format to represent JavaScript objects and arrays (Python dicts and lists) as a string.
In the frontend you'll have to use JSON.parse() to convert it into a JavaScript array (list) or object (dict).
This also holds true when you send JSON from frontend to backend. You use JSON.stringify() to covert the JS object to string. Then in the backend you convert that string to a Python object using json.loads().

Django custom context_processors in render_to_string method

I'm building a function to send email and I need to use a context_processor variable inside the HTML template of the email, but this don't work.
Example:
def send_email(plain_body_template_name, html_body_template_name):
plain_body = loader.render_to_string(plain_body_template_name, context)
html_body = loader.render_to_string(html_body_template_name, context)
email_msg = EmailMultiAlternatives(body=plain_body)
email_msg.attach_alternative(html_body, 'text/html')
email_message.send()
In my custom context_processor.py I just have a function that receive a HttpRequest and return a dict like {'foo': 'bar'}, and in the template I try to render using {{foo}}.
I added the context_processor in the TEMPLATE['OPTIONS']['context_processors'] too.
Assuming you're using the django backend in your TEMPLATE with
'BACKEND': 'django.template.backends.django.DjangoTemplates',
django is seeing that you haven't passed in a request and opting for a basic Context to wrap your dict instead of a RequestContext which will handle the context_processors you've defined.
You can probably get away with doing
html_body = loader.render_to_string(html_body_template_name, context, request=request)
but you'd need to pass in the request object.
This might not make sense though. Are you emailing the person making the request? Does the context make sense to include?
If your context processor doesn't need the request then I'd either make it a simple utility function (if it's only called here) or make the request parameter optional, import it into this module, and add it directly into the context
context = {"my_var": 1}
context.update(your_extra_context())
loader.render_to_string(...)
There are some complicated ways of updating a Context() in layers, but I don't think that's necessary here.
I had a similar problem - I needed to render template to string with context processor values, but at the same time request object was None (running command from console). Then I found this approach:
from django.template.loader import render_to_string
from django.template import RequestContext
from django.shortcuts import render
def index(request):
if not request:
context = {'param1':'value1'}
return render_to_string('myapp/index.html', RequestContext(None, context))
else:
#render as usual
render(request, 'myapp/index.html', context)
pass
when you pass RequestContext instead of dictionary, it populates values of all context processors into the context. But request must be optional in all your context processors, otherwise this won't work.

Displaying JSON data

In django my view.py is
import json
from django.http import HttpResponse
from django.template import Template, Context
from django.shortcuts import render_to_response
def ajax(request):
obj =[dict(a = 1,b = 2)]
jsons=json.dumps(obj)
print jsons
return render_to_response("2.html", {"obj_as_json": jsons})
I want to display value of a and b that are JSON in my template 2.html. Please help me to write the code.
I don't understand the usage of View.
Why do you want to pass JSON object as a context value while Template Rendering ?
The standard is When you do a Ajax request its response should be a JSON response i.e mimetype=application/json.
So, You should render the template normally and Convert the result into JSON and return.
e.g:
def ajax(request):
obj = {
'response': render_to_string("2.html", {"a": 1, "b": 2})
}
return HttpResponse(json.dumps(obj), mimetype='application/json')
OR
you can create a JSONResponse class Similar to HttpResponse to make it generic . e.g.
class JSONResponse(HttpResponse):
"""
JSON response
"""
def __init__(self, content, mimetype='application/json', status=None, content_type=None):
super(JSONResponse, self).__init__(
content=json.dumps(content),
mimetype=mimetype,
status=status,
content_type=content_type,
)
and use like : return JSONResponse(obj)
This has been added by default in django 1.7: https://docs.djangoproject.com/en/1.7/ref/request-response/#jsonresponse-objects

Query json http response from browser

I have a view written in Django which returns JSON response like:
[{"pk": 3222, "model": "test.test", "fields": {"f1:f1, f3:f2"}}......etc]
The URL that returns this view is: 127.0.0.1:8000/someview/modelname/all
However when I hit this on a browser 127.0.0.1:8000/someview/modelname/all?pk=3222
I do not get the filtered result. How do I achieve this? Or is my understanding of query in a URL wrong.
UPDATE:
In my view:
obj = SomeModel.objects.all()
return HttpResponse(serializers.serialize('json', [obj,]))
This is because you're not doing any filtering in your code at all. Your first line says "give me all the SomeModel objects" and your second line says "here's an HTTP response of all the SomeModel objects as JSON."
Assuming you're not using class-based views, you can do something like this:
from django.shortcuts import get_object_or_404
def SomeView(request):
obj = SomeModel.objects.all()
if request.GET.get('pk') is not None:
obj = get_object_or_404(SomeModel, pk=request.GET.get('pk'))
return HttpResponse(serializers.serialize('json', obj), content_type="application/json")
You only must GET the pk argument and with the pk you can GET the record:
if request.GET['pk']:
p_k = int(request.GET['pk'])
obj = Somemodel.objects.get(pk=p_k)
else:
obj = SomeModel.objects.all()

Categories

Resources