Is it possible to provide a template name to a viewset in DRF, such that this new template extends api.html?
new_template.html
{% extends 'api.html' %}
{% block my-new-block %}
TEST
{% endblock %}
I can't see how to do it currently, and setting 'template_name' hasn't had any effect. I need to display content differently depending on the particular endpoint.
EDIT: Tried overwriting the retrieve method and adding 'rest_framework.renderers.TemplateHTMLRenderer', to the DEFAULT_RENDERER_CLASSES in settings.py.
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data, template_name='new_template.html')
However, this seems to have zero effect.
The way to do it is to subclass renderers.BaseRenderer and set your own template. In the view, you can set the renderer_classes attribute to your own custom renderer
Related
I'm working on a small e-commerce. For a model 'Product' I keep track of the stock using a Custom Manager and a method called Products.objects.with_stock() that uses annotations (I'm required to do so, there's no way I can add a stock attribute in Product)
In a ListView of all the products, it's pretty straightforward to add the stock, to access it from a template:
# app/views.py
class ProductListView(ListView):
...
def get_stock(self, obj):
return obj.stock
def get_queryset(self):
return super().get_queryset().with_stock()
And then in the template I just call it:
<!-- templates/home.html-->
{% for product in products %}
<p> Stock: {{product.stock}} <p>
{% endfor %}
How do I perform something similar for a DetailView?
Given that a DetailView gets a particular object instance, where or how do I run something similar to what I did in the ListView? Where do I run that query method that affects all objects as a whole, so that then I can access it in the same way from the template?
It couldn't be simpler: just do the same in the DetailView:
# app/views.py
class ProductDetailView(DetailView):
...
def get_stock(self, obj):
return obj.stock
def get_queryset(self):
return super().get_queryset().with_stock()
I want to display data from 2 tables (and more in the future), but something doesnt work in my code.
my views.py:
**imports**
def home(request):
context = {'users': Person.object.all(),
'emails': Email.object.all()
}
return render(request,'app/home.html',context)
class PersonListView(ListView):
model = Person
template_name = 'app/home.html'
context_object_name = 'users'
and in my home.html
{% extends "app/base.html" %}
{% block content %}
{% for user in users %}
Displaying user attributes works fine
{% endfor %}
Here should be emails
{% for email in emails %}
This displaying doesnt work
{% endfor %}
{% endbock content %}
So, displaying users works without any problem, but cant display anything form emails, but if I do it in shell, everything works well
A ListView [Django-doc] is designed to display only one queryset at a time. If you need to pass extra querysets, you can override the get_context_data(..) method [Django-doc]:
class PersonListView(ListView):
model = Person
template_name = 'app/home.html'
context_object_name = 'users'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(emails=Email.objects.all())
return context
Here we thus pass an extra variable emails to the template rendering engine. Note however that this queryset will not be paginated (or at least not without adding some pagination yourself).
my models.py:
Note that those are views, you need to write these in views.py.
I am using Django v2.2 admin to change the information on my database but after I change it and refresh the page, the new data is not there, only the old data.
A fix for this if I restart the server, the templates can now fetch the new data that I input.
views.py
# template with context
class Home(TemplateView):
template = 'home.html'
context = { 'bar': Baby.objects.all() }
def get(self, request):
return render(request, self.template, self.context)
home.html
{% for foo in bar %}
{{ foo.name }}
{{ foo.cost }}
{% endfor %}
How I can get the new data by refreshing the page and not restarting the server?
As others mentioned, use get_context_data() method is good idea, because ContextMixin is parent class (not base class, but part of TemplateView's __mro__ Method Resolution Order) of TemplateView which is responsible to pass data from view to template. But, if you want to render template manually using get() method, You should hit on database on every GET request (in your case).
class Home(TemplateView):
template = 'home.html'
def get(self, request):
self.context = {'bar': Baby.objects.all()}
return render(request, self.template, self.context)
Your code does not work, because static variables are initialized only once. In your case context was static variable.
Hope, it helps you.
Can you please try this?
class Home(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bar'] = Baby.objects.all()
return context
Question about pagination issue:
Why if I have for example following LISTVIEW:
class BoatListView(ListView):
model = BoatModel
template_name = "boats.html"
paginate_by = 5
def get_context_data(self, *, object_list=None, **kwargs):
context = ListView.get_context_data(self, object_list=None, **kwargs)
context["boats"] = BoatModel.objects.all()
context["images"] = BoatImage.objects.all()
return context
and I would use “boats” and “images” context in template , for example :
{% for boat in boats %}
some code here
{% endfor %}
...
…
….
{% bootstrap_pagination page_obj %}
paginator will not work at all in this case ( bootstrap one or original Django https://docs.djangoproject.com/en/2.2/topics/pagination/#using-paginator-in-a-view), no difference?
But as soon as I change “boats” and “images” to “object_list” - paginator would begin pagination.
Whats the problem and how in this case could I add extra context in view if I need to do so within ability to use paiginator indeed?
Thank you!
ListView declares an attribute object_list which takes the queryset from get_queryset(). When constructing the context, this attribute is used to define pagination. You can override the behaviour of the pagination in get_context_data itself by changing what is sent as a queryset in self.paginate_queryset(queryset, page_size)(I don't see a reason to do this though).
Take a look at how ListView works here.
I am trying to render a Django contact form on any arbitrary page. I am doing it with a request context processor and a template include. This allows me to display the form fine anywhere I want. Then I have a special URL that accepts POST requests (on GET, I just redirect them). If the form is valid, I send an email, and redirect to a success page. On form invalid, I know to pass the form bound with errors, but...I don't know which template to specify because the form is an include and the parent template could be anywhere.
The only way to get something in a Django view is from the request. I can get the path, and with more work, probably the original view from where the POST came from, but that doesn't get me the template.
# urls.py
url(r'^services/$', 'website.views.services', name='services'),
url(r'^services/contact/$', 'website.views.services_contact', name='services_contact'),
url(r'^services/contact/done/$', 'website.views.services_contact_done', name='services_contact_done')
# views.py
class ServicesView(TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
class ServicesContactView(View):
def get(self, request, *args, **kwargs):
return redirect('services')
def post(self, request, *args, **kwargs):
form = ContactForm(request.POST)
if form.is_valid():
form.send_email()
return redirect('services_contact_done')
else:
return render(request, ????, {'contact_form': form})
services_contact = ServicesContactView.as_view()
# contact.html
<h2>Contact me</h2>
<p>Enter your email to receive your questionnaire</p>
<form action="{% url 'services_contact' %}" method="post">
{% csrf_token %}
{% if contact_form.non_field_errors %}
{{ contact_form.non_field_errors }}
{% endif %}
{{ contact_form.as_p }}
<button type="submit" name="submit">Send questionnaire</button>
</form>
# home.html
{% extends "base.html" %}
{% block content %}
<h1>{{ site.name }}</h1>
{% include "services/contact.html" %}
{% endblock %}
The typical Django form view is somewhat silent on form invalid in that its scenario is mostly similar to an unbound form, so it's all just render in the end. My scenario is different due to the template include.
You could set up a session variable every time you render a template and use it afterwards when you need it :
request.session['template']="nameOfTemplate"
.
return render(request, request.session.get('template', 'default.html'), {'contact_form': form})
I know it requires to write a line of code every time you render a template, but that's the best solution I could think of.
If anybody needs this answer, I figured it out on my own. It's possible, but a different approach is required. First, a request context processor is not appropriate for this situation. They're fairly dumb because they just get something once and stick it in the context. Their only advantage is their global nature.
My context processor:
def contact_form(request):
"""
Gets the contact form and adds it to the request context.
You almost certainly don't want to do this.
"""
form = ContactForm()
return {'contact_form': form}
The nature of forms is that they act differently after being processed by Django's validation machinery, specifically ContactForm() is an unbound form and will always be. You don't want to do this (unless you want a form that simply displays but doesn't work). The TEMPLATE_CONTEXT_PROCESSORS should be edited to remove this processor.
Now the burden on displaying the form is back on the view, which also means just about any view must be able to handle POST requests as well. This means that editing each view that wants a contact form is required, but we can use the power of class-based views and mixins to handle most of the repetition.
ServicesView remains almost the same as a TemplateView, except with a mixin that will handle the form. This way, the template name always remains the same (my original problem), but with additional form power.
class ServicesView(ContactMixin, TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
ContactMixin uses FormMixin, to create and display a form, and ProcessFormView to handle the GET and POST requests for the form. And because the form's nature changes with different kinds of requests (unsubmitted, submitted and invalid, submitted and valid), get_context_data needs to be updated with the correct form class instance. Lastly, we probably want to prefix (namespace) our form because it can can be used anywhere, and we want to avoid conflicts when another possible form can POST to the same view. Thus, the mixin is:
class ContactMixin(FormMixin, ProcessFormView):
form_class = ContactForm
success_url = reverse_lazy('contact_done')
def get_form_kwargs(self):
kwargs = super(ContactMixin, self).get_form_kwargs()
kwargs['prefix'] = 'contact'
return kwargs
def get_context_data(self, **kwargs):
context = super(ContactMixin, self).get_context_data(**kwargs)
form_class = self.get_form_class()
context['contact_form'] = self.get_form(form_class)
return context
def form_valid(self, form):
form.send_email()
return super(ContactMixin, self).form_valid(form)
The subtleties of self.get_form_class() were almost lost on me if it were not for an example in the docs (of what not to do, heh) and another StackOverflow answer, where I would've usually just said self.form_class, which ignores the processing of the form.
Now I simply add ContactMixin to any view and {% include "includes/contact.html" %} to any template.