In Pyramid, templates are statically assigned to view with
#view_config(renderer='templates/foo.pt')
def my_view(request):
return {'foo':1, 'bar':2}
However, in some cases, we want to set different template for each user. For example, user1 use templates from templates/style1/xxx.pt and user2 use templates from templates/style2/xxx.pt. Thus, we need a way to dynamically pass templates to views, instead of configure them statically.
Is there anyway to do the task elegantly?
I just did this a couple of days ago.
Here is example code for an ajax call.
#view_config(name="my_view", renderer="")
def my_view(request):
renderer = choose_renderer() #this is where you would dynamically choose what renderer you want. ex "templates/foo.pt"
data = get_my_data_for_renderer
return render_to_response(renderer, data, request)
Here is an example for a normal route call
#view_config(route_name="my_view", renderer="")
def my_view(request):
renderer = choose_renderer() #this is where you would dynamically choose what renderer you want. ex "templates/foo.pt"
data = get_my_data_for_renderer
return render_to_response(renderer, data, request)
How about something like this. I have not tested it. I'm pulling from memory :)
Add this to your init.py in your config settings.
config.add_route('my_view', 'my_view/{renderer}')
Use this view:
#view_config(route_name='my_view', renderer='')
def my_view(request):
renderer = request.matchdict['renderer']
data = get_my_data_for_renderer()
return render_to_response(renderer, data, request)
Related
This is a view for get all the records in the EducationalRecord model:
def all_education_resume(request):
RESUME_INFO['view'] = 'education'
educations_resume = EducationalRecord.objects.all().order_by('-created_date')
template = 'resumes/all_resume.html'
context = {'educations_resume': educations_resume, 'resume_info': RESUME_INFO}
return render(request, template, context)
Now, if I want to write exactly this view for other models (like job resumes, research resumes , etc.),
I must another view one separately.
My question is:
How can I get a view for all these requests, so first check the URL of
the request and then do the relevant query? How can I control URL
requests in my views?
My other question is exactly the same as my first question,with this difference:
control view that must render in specific template.In other words,in
second question the ratio between the template and the view is instead
of the ratio of the view to the url or how to create a template for
multiple views (for example, for a variety of database resume
resumes, I have a template) and then, depending on which view render,
the template output is different.
I have implemented these two issues as follows:
I wrote a view for each of request!
In each view, I set the value of RESUME_INFO['view'], and then I've checked it in a template page and specified the corresponding template.
What is the best solution to these two questions?
How can I get a view for all these requests, so first check the URL of the request and then do the relevant query? How can I control URL requests in my views?
You can access request.path, or you can let the url(..)s pass a parameter with kwargs that holds a reference to the model for example, but this is usually bad design. Typically if you use different models, you will likely have to order these different as well, filter these differently, render these differently, etc. If not, then this typically indicates that something is wrong with the modeling.
You can however make use of class-based views [Django-doc], to remove as much boilerplate as posssible. Your view looks like a ListView [Django-doc], by using such view, and patching where necessary, we can omit most of the "boilerplate" code:
# app/views.py
from django.views.generic.list import ListView
class MyBaseListView(ListView):
resume_info = None
template = 'resumes/all_resume.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['resume_info'] = {'view': self.resume_info}
return context
In the individual listviews, you then only need to specify the resume_info and the model or queryset to render it with the 'all_resume.html' template, for example:
# app/views.py
# ...
class EducationalResumeView(MyBaseListView):
queryset = EducationalRecord.objects.order_by('-created_date')
resume_info = 'education'
class OtherModelView(MyBaseListView):
model = OtherModel
resume_info = 'other_info'
So we can here use inheritance to define common things only once, and use it in multiple views. In case we need to change something in a specific view, we can override it at that level.
In the urls.py, you define such view with the .as_view() method [Django-doc]. For example:
# app/urls.py
from django.urls import path
from app.views import EducationalResumeView, OtherModelView
urlpatterns = [
path('education/', EducationalResumeView.as_view()),
path('other/', OtherModelView.as_view()),
]
I have a function that gets some base information in my views.py file, and I'm trying to update the context of each page using it by having it return a dictionary. However, using .update() on the context dictionary in the render() function doesn't seem to work.
Here's what I'm doing:
def getBaseInfo():
allPages = list(Page.objects.all())
primaryPages = allPages[:5]
secondaryPages = allPages[5:]
return {'p':primaryPages, 'p2':secondaryPages}
def index(request):
return render(request, 'pages/index.html', {}.update(getBaseInfo()))
However, nothing is sent to my templates. Thanks in advance!
Edit: I'm using Python 2.7.11
Firstly, if you wanted to use a base dictionary and add objects to that you should do so explicitly:
def index(request):
context = getBaseInfo()
context.update({'otherkey': 'othervalue'})
# or
context['otherkey'] = 'othervalue'
return(...)
However, there is no need to do this at all. Django already provides you a way of automatically providing shared context, and that is a context processor.
In fact your getBaseInfo() function is already almost a context processor - it just needs to accept the request parameter - so you just need to add it to the context_processors list in your TEMPLATES setting. Then all your templates will automatically get the values from that function.
You should do something like this:
def index(request):
allPages = list(Page.objects.all())
primaryPages = allPages[:5]
secondaryPages = allPages[5:]
return render(request, 'pages/index.html', {'p':primaryPages, 'p2':secondaryPages})
Other option should be to make getBaseInfo a #property for reusability and DRY purposes, or make the view class based template view and define reusable code as mixin. I prefer the latter, but it's entirely matter of personal choice.
I have 3 different product page layouts that I would like to display dependent on the information available about the products. Using traversal I have a class called ProductFinder that grabs all the information. For example the user goes to domain/green/small and ProductFinder will list all products from my DB that are green and small. This list is self.products in the ProductFinder class. In my __init__.py I have added the line:
config.add_view('app.views.products', name='')
In products.py I have:
from pyramid.view import view_config
#view_config(context='app.models.ProductFinder', renderer='productpage.mako')
def products(context, request):
return dict(page=context)
Based on what's in context.products though I'd like to render a different mako. In Pylons I would have done something like:
def products(context, request):
if len(context.products) == 1:
return render("oneproduct.mako")
elif len(context.product) == 2:
return render("twoproducts.mako")
So how can I render a different template based on the contents of my context?
I will start off by saying this sort of seems like something you want to take care of in your template.
However, you can influence which renderer is used as part of the view lookup in just about any way you want to. As you might already know you can use the same view handler for multiple views, you simply need to help Pyramid figure out which one to use.
For example:
from pyramid.view import view_config
def ProductLengthPredicate(length):
def check_length(context, request):
return len(context.products) == length
return check_length
#view_config(context='app.models.ProductFinder', renderer='oneproduct.mako',
custom_predicates=(ProductLengthPredicate(1),))
#view_config(context='app.models.ProductFinder', renderer='twoproducts.mako',
custom_predicates=(ProductLengthPredicate(2),))
#view_config(context='app.models.ProductFinder', renderer='manyproducts.mako')
def products(context, request):
return dict(page=context)
NB. Some people might be more interested in the render_to_response approach here because then they will not be relying on custom_predicates. But it is of course up to you!
#view_config(context='app.models.ProductFinder', renderer='manyproducts.mako')
def products(context, request)
opts = dict(page=context)
if len(context.products) == 1:
return render_to_response('oneproduct.mako', opts, request)
if len(context.products) == 2:
return render_to_response('twoproducts.mako', opts, request)
return opts
This works because Pyramid will ignore the renderers if your view returns a Response() which is exactly what render_to_response does.
I'm not sure if it's the good way to go, but you could probably use request.override_renderer = 'oneproduct.mako'.
If it is just a different way of displaying your products depending on the quantity, you should do the decision in the template.
I am trying to write something elegant where I am not relying on Request object in my code. All the examples are using:
(r'^hello/(?P.*)$', 'foobar.views.hello')
but it doesn't seem like you can post to a URL like that very easily with a form. Is there a way to make that URL respond to ..../hello?name=smith
Absolutely. If your url is mapped to a function, in this case foobar.views.hello, then that function might look like this for a GET request:
def hello(request):
if request.method == "GET":
name_detail = request.GET.get("name", None)
if name_detail:
# got details
else:
# error handling if required.
Data in encoded forms, i.e. POST parameters, is available if you HTTP POST from request.POST.
You can also construct these yourself if you want, say, query parameters on a POST request. Just do this:
PARAMS = dict()
raw_qs = request.META.get('QUERY_STRING', '') # this will be the raw query string
if raw_qs:
for line in raw_qs.split("&"):
key,arg = line.split("=")
PARAMS[key] = arg
And likewise for form-encoded parameters in non POST requests, do this:
FORM_PARAMS = QueryDict(request.raw_post_data)
However, if you're trying to use forms with Django, you should definitely look at django.forms. The whole forms library will just generally make your life easier; I've never written a html form by hand using Django because this part of Django takes all the work out of it. As a quick summary, you do this:
forms.py:
class FooForm(forms.Form):
name = fields.CharField(max_length=200)
# other properties
or even this:
class FooForm(forms.ModelForm):
class Meta:
model = model_name
Then in your request, you can pass a form out to the template:
def pagewithforminit(request):
myform = FooForm()
return render_to_response('sometemplate.html', {'nameintemplate': myform},
context_instance=RequestContext(request))
And in the view that receives it:
def pagepostingto(request):
myform = FooForm(request.POST)
if myform.is_valid(): # check the fields for you:
# do something with results. if a model form, this:
myform.save()
# creates a model for you.
See also model forms. In short, I strongly recommend django.forms.
You can't catch GET parameters in a URL pattern. As you can see in django.core.handlers.base.BaseHandler.get_response, only the part of the URL that ends up in request.path_info is used to resolve an URL:
callback, callback_args, callback_kwargs = resolver.resolve(
request.path_info)
request.path_info does not contain the GET parameters. For handling those, see Ninefingers answer.
Simple question. I have bunch of django views. Is there a way to tell django that for each view, use foo(view) instead? Example:
Instead of writing
#foo
#bar
#baz
def view(request):
# do something
all the time, I'd like to have
def view(request):
markers = ['some', 'markers']
and hook this into django:
for view in all_the_views_in_my_app:
view = do_something_based_on_the_marker(view)
I'd like to have this done at server startup time. Any thoughts?
Depending on what you want to do (or achieve), you can write a custom middelware and implement the method process_view (and/or any other method that you need):
process_view() is called just before Django calls the view. It should return either None or an HttpResponse object. If it returns None, Django will continue processing this request, executing any other process_view() middleware and, then, the appropriate view. If it returns an HttpResponse object, Django won't bother calling ANY other request, view or exception middleware, or the appropriate view; it'll return that HttpResponse. Response middleware is always called on every response.
I don't know why you want to do this. I don't know either why you don't want to use decorators. But you could use this ugly (and likely error prone) hack as a start:
def view(request):
pass
view.markers = ['some', 'markers']
some other place:
from app import views
[x for x in views.__dict__.values() if hasattr(x,'markers')]