Filter all queries based on url parameters - django

I have in a Django project all my urls based on the following syntax:
/ID_PROGRAM/ID_PROJECT/blablabla
I would like by default that all my queries have the following filters:
.filter(program=ID_PROGRAM).filter(project=ID_PROJECT)
How can I apply these filters automatically to all my queries? My idea was to define a new manager. But is the manager able to access to the url parameters? I this the best way to do?
To complet the question, I want to enrich all my queries without having to pass explicitly the view parameters to the manager.

You could have just tried it to see if it works.
Yes, managers do accept parameters
class MyModelManager(models.Manager):
def my_filters(self, id_prog, id_proj):
return super(MyModelManager, self).get_query_set().filter(program=id_prog, project=id_proj)
and in the views:
MyModelManager.objects.my_filters(id_prog, id_proj)
Documentation on custom managers

Python promotes "Explicit is better than implicit"
karthikr is almost right, but you can also use:
1 - decorator above your function. Decorator will get args from url and put objects to any variable
2 - write mixin and aply it to view. Mixin will get args from url at overriden dispatch and save filter result to self.custom_context. Override get_context_data to merge contexts.

Related

Force django querystring in url

Say you have a url like this:
/cats/?filter=kittens
Is it possible to build a django url pattern that forces the use of the querystring?
Currently I have:
url(r'^/cats/$', views.CatsListView.as_view(), name='cats')
Now I want to add the querystring and get a different view, something like this:
url(r'^/cats/?filter=(?P<filter>.+?)$', views.CatsFilteredListView.as_view(), name='cats-filtered')
Is it possible to do something like this and still keep the querystring in the GET parameter of the request?
Remember that this is just a testcase, I, and you should too, know that filtering like probably this isn't the way to go..
Short answer: no, it's not possible. Django url patterns match only on the "path" componant of the url, period.
No, it's not possible to do this. If you really need two separate views, you can write a view that dispatches the correct view.
def cat_list_view(request, *args, **kwargs):
if 'filter' in request.GET:
return cat_list_filter_view(request, *args, **kwargs)
else:
return cat_list_unfiltered_view(request, *args, **kwargs)
However, for your example of CatsListView and CatsFilteredListView there is probably a better way to combine the views. For example you might be able to do the filtering in the get_queryset method.
While it is true that you cannot manipulate the path component like that, you can pass a dictionary in. It's a 3rd unnamed argument.
This approach can be useful if you want to use the same view for multiple resources, and pass data to configure its behaviour in each case (below we supply a different template in each case).
path('url/', views.my_reused_view, {'my_template_name': 'some_path'}, name='aurl'),
path('anotherurl/', views.my_reused_view, {'my_template_name': 'another_path'}, name='anotherurl'),
Note: Both extra options and named captured patterns are passed to the view as named arguments. If you use the same name for both a captured pattern and an extra option then only the captured pattern value will be sent to the view (the value specified in the additional option will be dropped).
Courtesy of https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views

Django context processors and URL arguments

I have some code that is repeated at the start of my Django views. It basically just adds some variables to the context, but based on the URL argument, e.g.
def someView(request, id):
target = Target.objects.get(id=id)
# name will be added to ctx
name = target.name
(there are more attributes added and other attributes from related models, but this gives the general idea --- There are quite a few lines of repeat code at the start of each view)
I thought I could make my code more DRY by taking advantage of Django's context processors, but it would seem these don't access to the URL arguments?
Is there another way to avoid these repeat lines? Maybe middleware or something else?
You can access the URL parameters via request through the resolver_match attribute. So for instance you can do request.resolver_match.kwargs['id'] to get the ID kwarg.

Django: How to access the model id's within an AJAX script?

I was wondering what is the correct approach,
Do I create HiddenInput fields in my ModelForm and from the
View I pass in the primaryKey for the models I am about to edit into
the hiddenInput fields and then grab those hiddenInput fields from
the AJAX script to use it like this?
item.load(
"/bookmark/save/" + hidden_input_field_1,
null,
function () {
$("#save-form").submit(bookmark_save);
}
);
Or is there is some more clever way of doing it and I have no idea?
Thanks
It depends upon how you want to implement.
The basic idea is to edit 1. you need to get the existing instance, 2. Save provided information into this object.
For #1 you can do it multiple ways, like passing ID or any other primary key like attribute in url like http://myserver/edit_object/1 , Or pass ID as hidden input then you have to do it through templates.
For #2, I think you would already know this. Do something like
inst = MyModel.objects.get(id=input_id) # input_id taken as per #1
myform = MyForm(request.POST, instance=inst)
if myform.is_valid():
saved_inst = myform.save()
I just asked in the django IRC room and it says:
since js isn't processed by the django template engine, this is not
possible.
Hence the id or the object passed in from django view can't be accessed within AJAX script.

Django get() query not working

this_category = Category.objects.get(name=cat_name)
gives error: get() takes exactly 2 non-keyword arguments (1 given)
I am using the appengine helper, so maybe that is causing problems. Category is my model. Category.objects.all() works fine. Filter is also similarily not working.
Thanks,
Do you have any functions named name or cat_name? If so, try changing them or the variable names you are using and trying again.
The helper maps the Django model manager (Category.objects in this case) back to the class instance of the model via the appengine_django.models.ModelManager. Through the inheritance chain you eventually come to appengine.ext.db.Model.get(cls, keys, **kwargs) so that is why you are seeing this error. The helper does not support the same interface for get that Django does. If you do not want to get by primary key, you must use a filter
To do your query, you need to use the GAE filter function like this:
this_category = Category.objects.all().filter('name =', cat_name).get()

Adding more CoC to Django

I come from a Cake background, and I'm just starting to learn Django now. I'm liking it quite a bit, but I kinda wish it used convention over configuration like cake does. So,
How can I get Cake-style URLs automatically? For example, if I went to mysite.com/posts/view/5 it would load up mysite.posts.views.view and pass an argument 5 to it? I was thinking I could add something like (r'^(.*)/(.*)', 'mysite.$1.$2'), to urls.py, but of course, that won't work.
How can I automatically load up a template? Each view function should automatically load a template like templates/posts/view.html.
Is this even possible, or do I have to hack the core of Django?
Here's my solution, based on what Carl suggested:
urlpatterns = patterns('',
# url pats here
url(r'^(?P<app>\w+)/(?P<view>\w+)/(?P<args>.*)$', 'urls.dispatch')
)
def dispatch(req, app, view, args): # FIXME: ignores decorators on view func!
func = get_callable(app+'.views.'+view)
if args:
ret = func(req, *args.split('/'))
else:
ret = func(req)
if type(ret) is dict:
return render_to_response(app+'/'+view+'.html', ret)
else:
return ret
Seems to be working pretty well with initial tests. Solves both problems with a single function. Probably won't support GET-style arguments tho.
Those points are both implementable without hacking Django core, but either one will require a non-trivial level of familiarity with advanced Python techniques.
You can do the generic URL pattern with a pattern like this:
url(r'^(?P<appname>\w+)/(?P<viewfunc>\w+)/(?P<args>.*)$', 'myresolverfunc')
Then define a 'myresolverfunc' "view" function that takes "appname", "viewfunc", and "args" parameters, and implement whatever logic you want, splitting args on "/" and dynamically importing and dispatching to whatever view function is referenced. The trickiest part is the dynamic import, you can search Django's source for "importlib" to see how dynamic imports are done internally various places.
The automatic template loader can be implemented as a view function decorator similar to the various "render_to" decorators out there, except you'll generate the template name rather than passing it in to the decorator. You'll have to introspect the function object to get its name. Getting the app name will be trickier; you'll probably just want to hardcode it as a module-level global in each views.py file, or else work in conjunction with the above URL dispatcher, and have it annotate the request object with the app name or some such.
I don't you'll need to hack the core of Django for this. It sounds like you might be in need of generic views. Also check out the Generic Views topic guide.
The first example given in the generic views documentation sounds like your first bullet point:
Example:
Given the following URL patterns:
urlpatterns = patterns('django.views.generic.simple',
(r'^foo/$', 'direct_to_template', {'template':'foo_index.html'}),
(r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template':'foo_detail.html'}),
)
... a request to /foo/ would render the template foo_index.html, and a request to /foo/15/ would render the foo_detail.html with a context variable {{ params.id }} that is set to 15.