I have a ListView with some good functionality that i want to use from another app. The way i did it was using get_template_names.
def get_template_names(self):
referer = self.request.META['HTTP_REFERER']
if "/mwo/order/" in referer:
return ['invent/use_part.html']
return ['invent/part_list.html']
Which i access from two different apps:
path('inventory/', PartListView.as_view(), name='partlist'),
...
path('mwo/order/<int:pk>/add_part/', PartListView.as_view(),
name='add_part'),
But it causes a bug if i use a direct link to 1st url from navbar and not from another app. Now i'm new to django and i'm pretty sure there should be a better way for this. What can i use instead of request referrer to render different template for ListView when i access it from another view.
You can specify the template_name in the .as_view:
path('inventory/', PartListView.as_view(template_name='invent/part_list.html'), name='partlist'),
# …
path('mwo/order/<int:pk>/add_part/', PartListView.as_view(template_name='invent/use_part.html'), name='add_part'),
Then of course you should remove the get_template_names from the method, since otherwise you will override that behaviour.
Related
I want to write custom template loader for my Django app which looks for a specific folder based on a key that is part of the request.
Let me get into more details to be clear. Assume that I will be getting a key on every request(which I populate using a middleware).
Example: request.key could be 'india' or 'usa' or 'uk'.
I want my template loader to look for the template "templates/<key>/<template.html>". So when I say {% include "home.html" %}, I want the template loader to load "templates/india/home.html" or "templates/usa/home.html" or "templates/uk/home.html" based on the request.
Is there a way to pass the request object to a custom template loader?
I've been searching for the same solution and, after a couple days of searching, decided to use threading.local(). Simply make the request object global for the duration of the HTTP request processing! Commence rotten tomato throwing from the gallery.
Let me explain:
As of Django 1.8 (according to the development version docs) the "dirs" argument for all template finding functions will be deprecated. (ref)
This means that there are no arguments passed into a custom template loader other than the template name being requested and the list of template directories. If you want to access paramters in the request URL (or even the session information) you'll have to "reach out" into some other storage mechanism.
import threading
_local = threading.local()
class CustomMiddleware:
def process_request(self, request):
_local.request = request
def load_template_source(template_name, template_dirs=None):
if _local.request:
# Get the request URL and work your magic here!
pass
In my case it wasn't the request object (directly) I was after but rather what site (I'm developing a SaaS solution) the template should be rendered for.
To find the template to render Django uses the get_template method which only gets the template_name and optional dirs argument. So you cannot really pass the request there.
However, if you customize your render_to_response function to pass along a dirs argument you should be able to do it.
For example (assuming you are using a RequestContext as most people would):
from django import shortcuts
from django.conf import settings
def render_to_response(template_name, dictionary=None, context_instance=None, content_type=None, dirs):
assert context_instance, 'This method requires a `RequestContext` instance to function'
if not dirs:
dirs = []
dirs.append(os.path.join(settings.BASE_TEMPLATE_DIR, context_instance['request'].key)
return shortcuts.render_to_response(template_name, dictionary, context_instance, content_type, dirs)
I'm structuring a Django API with rest framework, I read the docs and DRF only makes a crud (get, post, patch, delete) from a model. Now the deal is how I can make custom actions with DRF.
Example:
api/v1/model/custom_action
Code:
class DistrictViewSet(viewsets.ModelViewSet):
queryset = District.objects.all()
serializer_class = DistrictSerializer
def custom_action(request, param):
# do many actions and return as Json Object
urls.py
url(r'api/v1/', include(router.urls))
Where router
router.register(r'model',api.ModelViewSet)
I'm correct with this or I need to create another modelview, customize the code and add it to router list?
You can add custom actions as you have done but you may need the #action decorator to configure the url to apply to a single object, or many.
#action(detail=True) adds pk to the url, as it applies to one object.
The url is generated from the action name, so for example
#action(detail=True)
def custom_action(self):
pass
Would yield the url ^<app_name>/{pk}/custom_action/$
You may find this useful:
https://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
I have 2 templates to render one listview and I am choosing the template according to the request url given by the user. I know that, I can add 2 classes for 2 templates on 2 seperate urls respectively. For example
class MyListView1(generic.ListView):
template_name = 'myapp/list_one.html'
.....
.....
class MyListView2(generic.ListView):
template_name = 'myapp/list_two.html'
.....
.....
But is there a way if I could check the url request inside one class and render the template according to it inside one listview class ? something like
class MyListView(generic.ListView):
if request.path == '/list1'
template_name = 'myapp/list_one.html'
if request.path == '/list2'
template_name = 'myapp/list_two.html'
I know this is not a valid code but just to visualise
Whenever you want to do something dynamic in a generic view, it needs to be in a method. This page shows the methods available for ListViews, and you can see that it includes get_template_names() which should do exactly what you want.
An alternative though would be to have two separate view classes, each defining their own template name, that inherit from a common base class which defines the rest of the shared functionality.
Just pass template from urls.py like
path("/list1",views.MyListView.as_view(template_name="myapp/list_one.html"),name="list1")
path("/list2",views.MyListView.as_view(template_name="myapp/list_two.html"),name="list2")
I have a viewsdirectory containing say a hundred view files. How can i make my urls.py transfer control to these view files without putting in an "intermediate handler" as described here (answered 3 years back).
Basically- is there a cleaner way for redirecting control to views in django?
The view function returns an HTML page that includes the current date and time. To display this view at a particular URL, you’ll need to create a URLconf; see URL dispatcher for instructions.
https://docs.djangoproject.com/en/1.7/topics/http/urls/
For redirecting you should use the Django redirect shortcut function
from django.shortcuts import redirect
def my_view(request):
...
return redirect('some-view-name', foo='bar')
https://docs.djangoproject.com/en/1.7/topics/http/shortcuts/#redirect
Using Marius Gedminas's excellent blog post, I have created a custom traverser for a folder in my site.
This allows me to show: http://foo.com/folder/random_id
Instead of: http://foo.com/folder/object.html?id=random_id
The configuration side works great, I can catch the random_ids and search through my messages for the correct one, ready to display.
My problem is that I'm unsure how to then display the data via my usual page templates - at the TODO point in his original code ;)
if name == 'mycalendar':
mycalendar = ... # TODO: do something to get the appropriate object
return mycalendar
Usually I'd use something similar to:
class Test(BrowserPage):
template = ViewPageTemplateFile('atest.pt')
def __call__(self):
return self.template()
But I can't work out how to do this correctly in the context of the custom traversal.
UPDATE: To be clear I want to avoid adding anything else to the url (No: http://foo.com/folder/random_id/read).
I don't need the view to be available via any other address (No: http://foo.com/folder/read)
The ZCML for the view I'd like to use is:
<browser:page
for="foo.interfaces.IFooFolderContainer"
name="read"
template="read.pt"
permission="zope.ManageContent"
/>
I'm guessing (on further advice), something along the lines of:
return getMultiAdapter((mycalendar, self.request), IPageTemplate, name=u'read')
Or even a default view for the object type (a dict in this case) that's being returned:
<browser:page
for="dict"
name="read"
template="read.pt"
permission="zope.ManageContent"
/>
It would be easier to answer your question if you showed what your custom traverser is doing.
Essentially, you want something like this:
def publishTraverse(self, request, name):
if name in self.context:
return MyMessageView(self.context[name], request)
# fall back to views such as index.html
view = queryMultiAdapter((self.context, request), name=name)
if view is not None:
return view
# give up and return a 404 Not Found error page
raise NotFound(self.context, name, request)
where MyMessageView can be something as simple as
class MyMessageView(BrowserPage):
__call__ = ViewPageTemplateFile('read.pt')
Disclaimer: I'm not sure if the view you instantiate directly will be protected by a security wrapper; make sure your functional tests ensure anonymous users can't view messages if that's what you want.
If you end up at a proper object with your custom traverser, you can just tack on the template name and user "context" in that template. So http://foo.com/folder/random_id/my_template and in the template do the normal <h1 tal:content="context/title" /> stuff.
IIUC, what you want is to render the 'read' view when somebody requests /folder/random_id. If that's the case, all you need to do is make your traversal return an object (IFolderContent, maybe) representing a random_id and specify the 'view' page as the defaultView for IFolderContent.
The defaultView is needed because there's no view specified for the random_id object in your URL.