Can't use .update() function on Django context in views? - python

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.

Related

Control requests to view and template output in django

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()),
]

Overwrite django view with custom context (Django 1.11, Viewflow)

I have a Django 1.11 project using Viewflow - https://github.com/viewflow/viewflow - that I've incorporated. It's been very helpful, but a lot of stuff is kind of "magic", and being my first serious Django project, I'm running into an issue I'm not sure of how to solve, or the best way.
I have a generic template that expects a lot of context. I have a function that adds this context to all of my views:
def add_general_context(context, MOC, MOC_enabled_fields = (), MOC_status = None):
context['MOC'] = MOC
context['current_date'] = timezone.now().strftime("%D")
context['MOC_form'] = forms.MOCForm(prefix="MOC_form", MOC_enabled_fields=MOC_enabled_fields, instance=MOC)
context['MOCAttachments'] = models.MOCAttachment.objects.filter(MOC=MOC)
context['MOCAttachment_form'] = forms.MOCAttachmentForm(prefix="MOCAttachment_form")
context['MOCApprovals'] = models.MOCApproval.objects.filter(MOC=MOC)
context['MOCTasks'] = models.MOCTask.objects.filter(MOC=MOC)
context['MOC_status'] = MOC_status
context['MOCConversation'] = models.MOCConversation.objects.filter(MOC=MOC)
# Add comments to the conversation
for conversation in context['MOCConversation']:
conversation.comments = models.MOCComment.objects.filter(conversation=conversation)
context['MOCComment_form'] = forms.MOCCommentForm(MOC=MOC)
context['MOCCommentReply_form'] = forms.MOCCommentReplyForm()
I basically need to add this context to a view that is inside viewflow - namely, AssignTaskView - https://github.com/viewflow/viewflow/blob/f50accb3cde5d53f1d4db0debf5936867712c3bd/viewflow/flow/views/task.py#L109
I've tried a few things to overwrite/add to the context, but none seem to work.
Attempt 1: Overwrite the URL and use extra_context (SO suggested this)
- The issue is that the urls are "magic", my urlpatterns is very simply:
from material.frontend import urls as frontend_urls
urlpatterns = [
url(r'^MOC/', include('MOC.urls')),
url(r'', include(frontend_urls)),
]
Overwriting the urls themselves was WAY over my head, I dug into it for a while, but it uses really generic functions to pull in modules, etc. I didn't have a clue how to really even attempt it.
Attempt 2: Overwrite the view itself and the get_context_data function
I think this would be possible, but it just doesn't seem to work. My attempts looked similar to this (the lastest one):
from viewflow.flow.views.task import AssignTaskView as CoreAssignTaskView
class AssignTaskView(CoreAssignTaskView):
def get_context_data(self, **kwargs):
context = super(AssignTaskView, self).get_context_data(**kwargs)
print("Did it work?")
return context
This is in my views.py - however, it simply doesn't run. I'm probably missing something, but I can't figure out how to actually force it to use my view instead of the one built in to viewflow.
I've successfully overwritten Viewflow's templates without issue, but overwriting anything else is beyond me. Any suggestions?
Yes you can actually override a view url by putting it on top of url_patterns
urlpatterns = [
url(
r'^/workflow/appname/flowname/(?P<process_pk>\d+)/taskname/(?P<task_pk>\d+)/assign/$',
YouCustomView.as_view(),
{'flow_task': FlowClass.taskname},
name="{}__assign".format(self.name)))
),
url(r'', include(frontend_urls)),
]
But it's simpler just to create a custom flow.View subclass and set you own Assign View
https://github.com/viewflow/viewflow/blob/master/viewflow/flow/nodes.py#L306
from viewflow import flow
class MyView(flow.View):
assign_view_class = MyAssignTaskView
flows.py:
class MyFlow(Flow):
...
taskname = MyView(UpdateProcessView).next(this.end)
That's how you can override any of the built-in views.
Viewflow designed to provide all knobs within your codebase. You can customize any behavior by subclassing Flow or flow Node classes.
The custom node example could be helpful
https://github.com/viewflow/viewflow/blob/master/demo/customnode/nodes.py

Using a single URL for GET and POST with Django REST Framework list_route

The ModelViewSets in DRF have been really helpful, but I'm trying to extend one of them to be able to return a list of objects at a GET request, and process a list on a POST request. So far, it seems like I need to use the #list_route decorator to add this functionality.
I've used it just fine to add custom routes in other viewsets, however this is the first time I'm trying to add one that accepts more than one method. Here's what I have so far:
class PickViewset(viewsets.ModelViewSet):
queryset = Pick.objects.all()
serializer_class = PickSerializer
def get_queryset(self):
#gets the correct queryset
#list_route(methods=['get', 'post'])
def update_picks(self, request, league, week, format = None):
if request.method == 'POST':
#process/save objects here
else:
#otherwise return the requested list
I think this works and that my issue is in urls.py- here's the related stuff from there:
#bind the pick methods explicitly
update_picks = PickViewset.as_view({'get': 'update_picks'})
url(r'^api/picks/(?P<league>[\w ]+)/(?P<week>[0-9]+)/$', update_picks, name='update_picks')
This works fine for GET requests, and if i change the update_picks definition to
update_picks = PickViewset.as_view({'get': 'update_picks'})
then I can step into the POST code from the Viewset. What do I need to do to have both GET and POST requests routed to the update_picks action, where they can then be differentiated with the request method?
I tried adding a , {'post': 'update_picks'} to the as_view(), but that doesn't work.
I also tried adding
get_picks = PickViewset.as_view({'get': 'update_picks'})
with new URL pattern
url(r'^api/picks/(?P<league>[\w ]+)/(?P<week>[0-9]+)/$', get_picks, name='get_picks'),
but that didn't work either.
I looked into having separate list_routes with the same URL, but that doesn't seem to be supported, though I could have missed something in the docs.
Thanks for any help!
The actions argument to the ViewSet is a dict, all methods go in that dict:
get_picks = PickViewset.as_view({
'get': 'update_picks',
'post': 'update_picks',
})

Accessing to PK in Function Based View in a Template Context Processor

Im building a Template Context Processor to call the PK of each page that I have and that PK call it in the base.html, I have achieve to do a Template Context Processor with other query, but now I need to pass the PK. The context p works very well, but the context edit it does not, how can I call the PK from a Function Based View?
For example: localhost:8000/path/8 , I need to pass 8 in the context edit
def my_processor(request):
context = {'edit':InfoPredioGeneral.objects.filter(pk=self.kwargs['pk']),
'p':InfoPredioGeneral.objects.filter(user_id=request.user).latest('id')}
return context
I know that it does not works because of self and kwargs are not defined. But how can I do that?
You are using a FBV instead of a CBV, so the self you are using should be used with class methods, answering your question, you should pass the id parameter in the view, do something like this:
def my_processor(request, id):
context = {'edit':InfoPredioGeneral.objects.filter(pk=id),
'p':InfoPredioGeneral.objects.filter(user_id=request.user).latest('id')}
return context
in your urls.py you should put something like this:
url(r'^path/(?P<id>\d+)', my_processor, name='my_processor')

Hooking into django views

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')]

Categories

Resources