Django: Using render_to_string() on a class based view - django

I am using a complex subclass of DetailView to render my template.
class Test(DetailView):
template_name = 'my_template.html'
model = MyModel
def ..my_methods.. (self, ...):
...
return result
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
...
return context
How could I use this view in order to have the option of rendering the result into a string and save it in a variable?
I can think of render_to_string() but I am not sure how could I use it with a class based view.
EDIT
It is also a good approach to calibrate the dispatch() method in order to render_to_string() only when a given keyword is applied.

render_to_string expects these parameters template_name, dictionary, context_instance where dictionary and context_instance have default values of None. According to its definition
Loads the given template_name and renders it with the given dictionary
as context. The template_name may be a string to load a single
template using get_template, or it may be a tuple to use
select_template to find one of the templates in the list. Returns a
string.
just import the function:
from django.template.loader import render_to_string
and in the get function of your class inherited from DetailView just use it in the given definition format.
class Test(DetailView):
def get(self, request, *args, **kwargs):
return render_to_string(<template_name>, <dictionary>)
The dictionary is passed as context data to the template.

Related

GenericViews - and pushing model name in URL

using Django Generic CreateView - is it possible for me to pass a value into the CreateView from the URL call, that defines which model/table to base the view on?
I did try get_context_data but believe that is not the solution, as I think it only pushes it to the rendered template.
You will see from my scripts - I am pushing the value 'syslog_policy' - i would like the view to set the model variable to be, what-ever value I push from the URL.
The reason for this, I have some pages/models that are similar - so rather than create multiple html pages and views - I wouldn't need to if I could get this to work.
URL Call
<li>Update/Delete Policies</li>
urls.py
path('HardenTemplateCreate/<str:element>', HardenTemplateCreate.as_view(success_url="/security_tooling/add_success") ,name="HardenTemplateCreate")
views.py
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['element']= self.kwargs['element']
print(context['element'])
return context
model = !!<NEED VALUE SUPPLIED IN URL HERE>!!
fields = ['name','template']
template_name = 'security_app/add.html'```
This would assume that all the models we are working with here belong to the same app (main) - if not, you also need to pass that to form kwargs and handle accordingly. In forms.py:
from django.apps import apps
from django.forms.models import fields_for_model
class VariableModelForm(forms.Form):
def __init__(self, *args, **kwargs):
model_name = kwargs.pop('model_name', None)
model = apps.get_model(app_label='main', model_name=model_name)
model_fields = fields_for_model(model)
super(VariableForm, self).__init__(*args, **kwargs)
for field in model_fields:
self.fields[field] = model_fields[field]
In your CreateView:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form']= VariableModelForm(model_name=self.kwargs['modelname'])
return context
You grab the model name from the url kwargs, pass it to your form as an extra positional argument, and use it in your form's init method to set up the fields.

request v self.request in Django

I was just helped by the answer found here: global name 'request' is not defined: overriding form_valid but that left me asking another question:
When do you use just request, and when do you have to use self.request - and why? The example I see in the docs https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#dynamic-filtering don't use either request or self.request in the examples. Now maybe that's because it is a class based view, but I don't think I'm alone in getting the syntax and usage of cbv v generic v functional mixed up.
UPDATE
My code
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from essell.models import Code
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
context = super(CodeListView, self).get_context_data(**kwargs)
return context
from the docs
from django.views.generic.list import ListView
from django.utils import timezone
from articles.models import Article
class ArticleListView(ListView):
model = Article
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
MY CURRENT ERRORS
TypeError at /us/constitution
cannot convert dictionary update sequence element #0 to a sequence
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
#context = super(CodeListView, self).get_context_data(**kwargs)
context = {'object_list':object_list}
return context
NameError at /us/constitution
global name 'object_list' is not defined
from the docs: https://docs.djangoproject.com/es/1.9/topics/class-based-views/generic-display/
"This template will be rendered against a context containing a variable called object_list that contains all the publisher objects. A very simple template might look like the following:"
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
context = super(CodeListView, self).get_context_data(**kwargs)
context = {'object_list':object_list}
return context
global name 'object_list' is not defined
Like anything else in Python, any variable you reference must already exist in your current context.
Originally, Django views were simply functions that took as their first argument the request variable. Presumably, if you're writing old-style views, whatever you define as your first variable (e.g. def view_name(request, ...)) will be where the request is provided.
If you're using class-based views, then your views are member functions, so by convention their first argument will be self, e.g. def view_name(self, request, ...). In this case, refer to the Django documentation regarding which arguments are provided to which functions depending on which view you're subclassing.

How to use DetailView to override the view of an object that does not exists?

I want to override the admin default detail view for an object to use a different template when the object does not exist, i.e.
<mydomain>/admin/<myapp>/<mymodel>/<someidthatdoesexist>/
should render the default object detail view, and
<mydomain>/admin/<myapp>/<mymodel>/<someidthatdoesNOTexist>/
should render a custom template, instead of the default 404 error.
From what I've read I should use django.views.generic.detail.DetailView, but I'm not sure how I can achieve what I want.
I tried:
<b>in urls.py</b>
url(r'^admin/<myapp>/<mymodel>/(?P<pk>\d+)/$', views.MyModelDetailView.as_view(), name='mymodel_detail'),
url(r'^admin/', include(admin.site.urls)),
<b>in models.py</b>
class MyModelDetailView(DetailView):
model = MyModel
def get(self, request, *args, **kwargs):
try:
self.model.objects.get(pk=kwargs['pk'])
return super(MyModelDetailView, self).get(request, **kwargs)
except Http404:
# render custom template
but I get a TemplateDoesNotExist error:
<myapp>/mymodel_detail.html
What template should I set to render the default object detail view when the object exists?
EDIT
From the example given here, no template needs to be set...
The DetailView does not raise TemplateDoesNotExist anywhere in its source code. So the only probable place where the exception is being raised is in your redirect (return redirect(url)).
By the way a very useful place to browse class-based-views code is classy views at http://ccbv.co.uk/projects/Django/1.5/django.views.generic.detail/DetailView/
As for rendering a custom template if the object does not exist, you can easily modify your get function to make that work:
class MyModelDetailView(DetailView):
model = MyModel
template_name = 'object_is_found.html' # <= make sure to have this
def get(self, request, *args, **kwargs):
try:
self.object = self.get_object()
except Http404:
# return custom template
return render(request, 'no_object.html', status=404)
context = self.get_context_data(object=self.object)
return self.render_to_response(context)

django TemplateView and form

I have some problem to figure out how new django views (template view) and forms can works I also can't find good resources, official doc don't explain me how can get request ( I mean get and post) and forms in new django views class
Thanks
added for better explain
for example I have this form :
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
and this is the code for read and print the form (old fashion way):
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
well my question is how you can do the same thing with template view thanks
Use a FormView instead, i.e.
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
More on FormView in Django Docs
Technically TemplateView can also be used, just overwrite the post method, since by default template view does not allow you to post to it:
class ContactUsView(TemplateView):
template_name = 'contact_us/contact_us.html'
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid():
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
def get_context_data(self, **kwargs):
context = super(ContactUsView, self).get_context_data(**kwargs)
form = ContactUsEmailForm(self.request.POST or None) # instance= None
context["form"] = form
#context["latest_article"] = latest_article
return context
I think the FormView makes more sense though.
I would recommend just plodding through the official tutorial and I think realization will dawn and enlightenment will come automatically.
Basically:
When you issue a request: '''http://mydomain/myblog/foo/bar'''
Django will:
resolve myblog/foo/bar to a function/method call through the patterns defined in urls.py
call that function with the request as parameter, e.g. myblog.views.foo_bar_index(request).
and just send whatever string that function returns to the browser. Usually that's your generated html code.
The view function usually does the following:
Fill the context dict for the view
Renders the template using that context
returns the resulting string
The template generic view allows you to skip writing that function, and just pass in the context dictionary.
Quoting the django docs:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
All views.generic.*View classes have views.generic.View as their base. In the docs to that you find the information you require.
Basically:
# urls.py
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
)
MyView.as_view will generate a callable that calls views.generic.View.dispatch()
which in turn will call MyView.get(), MyView.post(), MyView.update() etc.
which you can override.
To quote the docs:
class View
dispatch(request, *args, **kwargs)
The view part of the view -- the method that accepts a request
argument plus arguments, and returns a HTTP response. The default
implementation will inspect the HTTP method and attempt to delegate to
a method that matches the HTTP method; a GET will be delegated to
get(), a POST to post(), and so on.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
The big plusses of the class based views (in my opinion):
Inheritance makes them dry.
More declarative form of programming

Redirect from Generic View DetailView in Django

I'm using Django's class based DetailView generic view to look up an object for display. Under certain circumstances, rather than displaying the object, I wish to back out and issue a HTTP rediect instead. I can't see how I go about doing this. It's for when a user hits an object in my app, but without using the canonical URL. So, for example, on StackOverflow URLs take the form:
http://stackoverflow.com/<content_type>/<pk>/<seo_friendly_slug>
eg:
http://stackoverflow.com/questions/5661806/django-debug-toolbar-with-django-cms-and-django-1-3
You can actually type anything as the seo_friendly_slug part and it will redirect you to the correct canonical URL for the object looked up via the PK.
I wish to do the same in my DetailView. Retrieve the object, check that it's the canonical URL, and if not redirect to the item's get_absolute_url URL.
I can't return an HttpResponseRedirect in get_object, as it's expecting the looked up object. I can't seem to return it from get_context_data, as it's just expecting context data.
Maybe I just need to write a manual view, but I wondered if anyone knew if it was possible?
Thanks!
Ludo.
This isn't a natural fit for DetailView. To do this you need to override the get method of BaseDetailView, which looks like:
class BaseDetailView(SingleObjectMixin, View):
def get(self, request, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
So in your class you'd need to provide a new get method which did the URL check between fetching the object and setting up the context. Something like:
def get(self, request, **kwargs):
self.object = self.get_object()
if self.request.path != self.object.get_absolute_url():
return HttpResponseRedirect(self.object.get_absolute_url())
else:
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
As you end up overriding so much of the functionality it becomes questionable whether it's worth actually using a generic view for this, but youknow.
Developing on Rolo's answer and comments, I came up with the following generic view to serve this purpose:
from django import http
from django.views import generic
class CanonicalDetailView(generic.DetailView):
"""
A DetailView which redirects to the absolute_url, if necessary.
"""
def get_object(self, *args, **kwargs):
# Return any previously-cached object
if getattr(self, 'object', None):
return self.object
return super(CanonicalDetailView, self).get_object(*args, **kwargs)
def get(self, *args, **kwargs):
# Make sure to use the canonical URL
self.object = self.get_object()
obj_url = self.object.get_absolute_url()
if self.request.path != obj_url:
return http.HttpResponsePermanentRedirect(obj_url)
return super(CanonicalDetailView, self).get(*args, **kwargs);
This is used in the same manner as the normal DetailView, and should work for any model which implements get_absolute_url correctly.