views:

554

answers:

2

Can the object value be looked up and passed to the generic view? Would the generic views need to be moved to views.py in order to support the object lookup?

urls.py

urlpatterns = patterns('myapp.views',
        url(r'^mymodel/create/$', view='mymodel_create',  name='mymodel_create'),
)

urlpatterns += patterns('django.views.generic',
        url(r'^(?P<model>\w+)/create/$','create_update.create_object', name='object_create'),
        url(r'^(?P<model>\w+)/(?P<id>\d+)/update/$', 'create_update.update_object', name='object_update' ),
        url(r'^(?P<model>\w+)/(?P<id>\d+)/delete/$', 'create_update.delete_object', name='object_delete'),        url(r'^(?P<model>\w+)/(?P<object_id>\d+)/$', 'list_detail.object_detail',  name='object_detail'),
        url(r'^(?P<model>\w+)/$','list_detail.object_list', name='object_list'),'
)

Example URL's:

http://localhost/myapp/thing/create
http://localhost/myapp/thing/1
http://localhost/myapp/thing/1/update
http://localhost/myapp/thing/1/delete

I'd like to use (?P\w+) to find the corresponding Model, 'Thing', but when this code is executed, the following error occurs: object_list() got an unexpected keyword argument 'model'

+1  A: 

I think you might be a bit confused about what the generic views are designed to do. If you go and read django.views.generic.list_detail, for instance, you will see that neither object_list nor object_detail accept an "object" argument, but both require a "queryset" argument. You don't need to "find the corresponding Model" - both generic views are simply common ways to render the provided queryset (in the case of object_detail, after being filtered by either object_id or slug and slug_field). I'm not entirely sure what you're trying to do but I don't think these generic views are what you're looking for.

ozan
How can I get the <object> into the queryset?obj_dict = { 'queryset':object.objects.all(),}
Ricklon
Ultimately I'm trying use generic views as CRUD and not need to write the same CRUD views for each object in my Model. I could do what I want by just writing the code necessary in the view, but I thought generic views would take care of the basics.
Ricklon
Yes, objects.all() will work for your queryset. Of course you don't need to write the same view for each object, but it's probably wise to write separate views for separate models (or use generic views for each one if appropriate). In other words, catching the model name in the url to use the same view across models is probably not a good idea, and in any case is not achievable using object_list and object_detail.
ozan
Can you say why you think it is "probably not a good idea" to have a generic URL that includes the model name? It's worked very well for me in several projects.
Carl Meyer
I agree with ozan that using the generic views in the urls.py file isn't going to work. The answer below proposed using the views.py and create views that do the model lookup, that looks like it will work. But why is this a bad idea. Why would I want to cut and paste my CRUD functions for every object in my model in the view?
Ricklon
Because when you do this across apps you remove an element of modularity. What happens when you want to overwrite one of the project-wide CRUD views in a particular app, for instance? Furthermore, explicit is better than implicit (the URLconf for a particular app should show me every pattern for the app's models). As the project matures, most of the generic views are going to be rewritten anyway, so the only time I can see project-wide CRUD url handling making sense is for the purpose of scaffolding... which is not so important in Django, thanks to the admin.
ozan
Let me clarify, this is urls.py at the app level. I can overide any of the object by placing the overide urls before these urls.
Ricklon
Okay, at the app level it's not a big deal, true.
ozan
Yes, I also agree that generic URL handling across different apps is probably not a good idea.
Carl Meyer
+1  A: 

Yes, you can do this, and I've done it. The Django generic views don't support it directly. You need to wrap the generic view with your own view that looks up the model and constructs the queryset. Here's an example (showing only the detail view, you can do the others similarly). I've changed your named patterns in the URLconf to use "model" instead of "object", which is clearer naming:

in urls.py:

url(r'^(?P<model>\w+)/(?P<object_id>\d+)/$', 'my_app.views.my_object_detail',  name='object_detail'),

in my_app/views.py

from django.views.generic.list_detail import object_detail
from django.db.models.loading import get_model
from django.http import Http404

def get_model_or_404(app, model):
    model = get_model(app, model)
    if model is None:
        raise Http404
    return model

def my_object_detail(request, model, object_id):
    model_class = get_model_or_404('my_app', model)
    queryset = model_class.objects.all()
    return object_detail(request, queryset, object_id=object_id)

If you're clever and want to keep things as DRY as possible, you could create a single wrapper that accepts the generic view function to call as one of its arguments, rather than having a wrapper for create, a wrapper for update, etc.

Carl Meyer
Thanks for making the naming clearer. I like this solution, but I'm not sure why this technique is not liked.
Ricklon
Just tested this answer, it works great.
Ricklon