Re: making some generic views more generic

2009-01-07 Thread Gabriel Getzie

Hi Malcolm,

Thanks for your input. I can certainly understand your perspective of
wanting to keep the maintenance burden for Django to a minimum.

Sorry about the formatting. I didn't realize it wouldn't translate so
well into the mailing list post. I'm new to the open source world
(that's probably obvious). Thanks for pointing me towards dpaste, I'll
be sure to use that in the future.

On Jan 6, 6:46 pm, Malcolm Tredinnick 
wrote:
> On Tue, 2009-01-06 at 11:01 -0800, Gabe wrote:
> > Hello,
>
> > I really like the generic view feature of Django. It's been a handy
> > way to save a lot of work. Lately though, I've been finding myself
> > often wanting to display a list of objects filtered by some field. The
> > documentation suggests handling this by writing a small view that
> > filters the objects and then calling the generic view from within
> > that. This works fine of course, but it seems that in a lot of cases
> > one can end up writing a large number of small views that all simply
> > display a filtered list of objects. I figured it might be helpful to
> > abstract this behavior into a couple of additional generic
> > views. These will automatically filter based on a keyword captured in
> > the URL and a field specified in the urls.py file. I've called these
> > new views object_list_field and object_list_foreign_field.
> >From the description you've provided here, this sounds like a few lines
>
> to filter the queryset appropriately and then call an existing generic
> view. That's the normal approach when you need more filtering: filter
> the queryset and then pass of the existing view, perhaps after also
> updating extra_context. Your code seems quite long and has wrapped
> fairly horribly in the email, so my eyes were watering a bit too much to
> read it all (if you really, really have to provide code of that length,
> either an attachment or a link to something like dpaste would be great).
>
> I don't think this should be added to Django. If it scratches your itch,
> then that's ideal. I think you could look at the wrapper approach for
> simpler code, but that's entirely up to you for your purposes. However,
> since the wrapper approach in conjunction with existing views does solve
> this, it's a strong argument against needing to include it in core.
> There are a million and one different use-cases that could be abstracted
> in various forms. Django shouldn't include functions for all of them,
> even if there's more than one user for it, since it would make the
> codebase huge and adds stuff we have to maintain forever. We're working
> with something that's closer to a library than a framework in many ways
> (code is built on top of it, rather than within it) and so people should
> be encouraged to build their enhancements externally.
>
> Please, please don't take this the wrong way. It always looks a bit
> horrible when somebody posts a bunch of code and a problem description
> (you get bonus points for at least mostly describing the problem you're
> trying to solve here -- something that is often missing in proposals
> that leap straight to solutions) and then some bozo like me comes along
> and says they're not enthused. Remember that (a) it's only my opinion,
> although that does carry some weight with some people and (b) I'm
> answering from the perspective of "this is django-dev and you're asking
> about including it in core", not evaluating whether the idea has any
> validity at all in the wild.
>
> Best wishes,
> Malcolm
--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~--~~~~--~~--~--~---



Re: making some generic views more generic

2009-01-06 Thread Malcolm Tredinnick

On Tue, 2009-01-06 at 11:01 -0800, Gabe wrote:
> Hello,
> 
> I really like the generic view feature of Django. It's been a handy
> way to save a lot of work. Lately though, I've been finding myself
> often wanting to display a list of objects filtered by some field. The
> documentation suggests handling this by writing a small view that
> filters the objects and then calling the generic view from within
> that. This works fine of course, but it seems that in a lot of cases
> one can end up writing a large number of small views that all simply
> display a filtered list of objects. I figured it might be helpful to
> abstract this behavior into a couple of additional generic
> views. These will automatically filter based on a keyword captured in
> the URL and a field specified in the urls.py file. I've called these
> new views object_list_field and object_list_foreign_field. 

>From the description you've provided here, this sounds like a few lines
to filter the queryset appropriately and then call an existing generic
view. That's the normal approach when you need more filtering: filter
the queryset and then pass of the existing view, perhaps after also
updating extra_context. Your code seems quite long and has wrapped
fairly horribly in the email, so my eyes were watering a bit too much to
read it all (if you really, really have to provide code of that length,
either an attachment or a link to something like dpaste would be great).

I don't think this should be added to Django. If it scratches your itch,
then that's ideal. I think you could look at the wrapper approach for
simpler code, but that's entirely up to you for your purposes. However,
since the wrapper approach in conjunction with existing views does solve
this, it's a strong argument against needing to include it in core.
There are a million and one different use-cases that could be abstracted
in various forms. Django shouldn't include functions for all of them,
even if there's more than one user for it, since it would make the
codebase huge and adds stuff we have to maintain forever. We're working
with something that's closer to a library than a framework in many ways
(code is built on top of it, rather than within it) and so people should
be encouraged to build their enhancements externally.

Please, please don't take this the wrong way. It always looks a bit
horrible when somebody posts a bunch of code and a problem description
(you get bonus points for at least mostly describing the problem you're
trying to solve here -- something that is often missing in proposals
that leap straight to solutions) and then some bozo like me comes along
and says they're not enthused. Remember that (a) it's only my opinion,
although that does carry some weight with some people and (b) I'm
answering from the perspective of "this is django-dev and you're asking
about including it in core", not evaluating whether the idea has any
validity at all in the wild.

Best wishes,
Malcolm



--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~--~~~~--~~--~--~---



Re: making some generic views more generic

2009-01-06 Thread cipher

Sorry, here is an example of item_list

urlpatterns = patterns('',
(r'^products/$',
item_list,{
'template_object_name' : 'product',
'queryset' : Product.objects.all()
}),
(r'^products/(?P\d+)/$',
item_list,{
'template_object_name' : 'product',
'queryset' : Product.objects.all()
}),
(r'^products/(?P[\w-]+)/$',
views.item_list,{
'template_object_name' : 'product',
'queryset' : Product.objects.all()
}),
)

On Jan 6, 2:43 pm, cipher  wrote:
> I also would like to request another view.
>
> def item_list(request,queryset,extra_context={},
>                           item_template=None,template_name=None,
>                           
> template_object_name='object',template_content_name='items',
>                           page=None,paginate_by=None,
>                           mimetype=None,allow_empty=True,
>                           template_loader=loader,context_processors=None,
>                           **filters):
>         queryset = queryset._clone()
>         if filters:
>                 for k,v in filters.items():
>                         if k.startswith('_'):
>                                 del filters[k]
>                                 filters[k[1:]]=v
>                 queryset=queryset.filter(**filters)
>
>         if paginate_by:
>                 paginator = Paginator(queryset, paginate_by,
> allow_empty_first_page=allow_empty)
>                 if not page:
>                         page = request.GET.get('page', 1)
>                 try:
>                         page_number = int(page)
>                 except ValueError:
>                         if page == 'last':
>                                 page_number = paginator.num_pages
>                         else:
>                                 # Page is not 'last', nor can it be converted 
> to an int.
>                                 raise Http404
>                 try:
>                         page_obj = paginator.page(page_number)
>                 except InvalidPage:
>                         raise Http404
>
>                 c = RequestContext(request, {
>                         '{0}_list'.format(template_object_name): 
> page_obj.object_list,
>                         'paginator': paginator,
>                         'page': page_obj,
>                         'results_per_page': paginator.per_page,
>                         'pages': paginator.num_pages,
>                         'hits': paginator.count,
>                         'page_range': paginator.page_range,
>                         }, context_processors)
>                 queryset=page_obj.object_list
>         else:
>                 c = RequestContext(request, {
>                         '%s_list' % template_object_name: queryset,
>                         'paginator': None,
>                         'page_obj': None,
>                         'is_paginated': False,
>                         }, context_processors)
>                 if not allow_empty and len(queryset) == 0:
>                         raise Http404
>         for key, value in extra_context.items():
>                 if callable(value):
>                         c[key] = value()
>                 else:
>                         c[key] = value
>
>         modelMeta=queryset.model()._meta
>         directory,name=modelMeta.app_label,modelMeta.object_name.lower()
>
>         if item_template is None:
>                 item_template='{0}/{1}_list_item.html'.format(directory,name)
>         if template_name is None:
>                 template_name='{0}/{1}_list.html'.format(directory,name)
>         item_template = template_loader.get_template(item_template)
>         responses=[]
>         c.push()
>         for item in queryset:
>                 c[template_object_name]=item
>                 item_result=item_template.render(c)
>                 responses.append(item_result)
>         c.pop()
>         template=template_loader.get_template(template_name)
>         c[template_content_name]=mark_safe(''.join(responses))
>         return HttpResponse(template.render(c), mimetype=mimetype)
>
> basically that will take keyword args and filter based on them like
> Gabes methods,
> but it also abstracts the item template in to another file.
>
> On Jan 6, 11:01 am, Gabe  wrote:
>
> > Hello,
>
> > I really like the generic view feature of Django. It's been a handy
> > way to save a lot of work. Lately though, I've been finding myself
> > often wanting to display a list of objects filtered by some field. The
> > documentation suggests handling this by writing a small view that
> > filters the objects and then calling the generic view from within
> > that. This works fine of course, but it seems that in a lot of cases
> > one can end up writing a large number of small views that 

Re: making some generic views more generic

2009-01-06 Thread cipher

I also would like to request another view.

def item_list(request,queryset,extra_context={},
  item_template=None,template_name=None,
  
template_object_name='object',template_content_name='items',
  page=None,paginate_by=None,
  mimetype=None,allow_empty=True,
  template_loader=loader,context_processors=None,
  **filters):
queryset = queryset._clone()
if filters:
for k,v in filters.items():
if k.startswith('_'):
del filters[k]
filters[k[1:]]=v
queryset=queryset.filter(**filters)

if paginate_by:
paginator = Paginator(queryset, paginate_by,
allow_empty_first_page=allow_empty)
if not page:
page = request.GET.get('page', 1)
try:
page_number = int(page)
except ValueError:
if page == 'last':
page_number = paginator.num_pages
else:
# Page is not 'last', nor can it be converted 
to an int.
raise Http404
try:
page_obj = paginator.page(page_number)
except InvalidPage:
raise Http404

c = RequestContext(request, {
'{0}_list'.format(template_object_name): 
page_obj.object_list,
'paginator': paginator,
'page': page_obj,
'results_per_page': paginator.per_page,
'pages': paginator.num_pages,
'hits': paginator.count,
'page_range': paginator.page_range,
}, context_processors)
queryset=page_obj.object_list
else:
c = RequestContext(request, {
'%s_list' % template_object_name: queryset,
'paginator': None,
'page_obj': None,
'is_paginated': False,
}, context_processors)
if not allow_empty and len(queryset) == 0:
raise Http404
for key, value in extra_context.items():
if callable(value):
c[key] = value()
else:
c[key] = value

modelMeta=queryset.model()._meta
directory,name=modelMeta.app_label,modelMeta.object_name.lower()

if item_template is None:
item_template='{0}/{1}_list_item.html'.format(directory,name)
if template_name is None:
template_name='{0}/{1}_list.html'.format(directory,name)
item_template = template_loader.get_template(item_template)
responses=[]
c.push()
for item in queryset:
c[template_object_name]=item
item_result=item_template.render(c)
responses.append(item_result)
c.pop()
template=template_loader.get_template(template_name)
c[template_content_name]=mark_safe(''.join(responses))
return HttpResponse(template.render(c), mimetype=mimetype)

basically that will take keyword args and filter based on them like
Gabes methods,
but it also abstracts the item template in to another file.

On Jan 6, 11:01 am, Gabe  wrote:
> Hello,
>
> I really like the generic view feature of Django. It's been a handy
> way to save a lot of work. Lately though, I've been finding myself
> often wanting to display a list of objects filtered by some field. The
> documentation suggests handling this by writing a small view that
> filters the objects and then calling the generic view from within
> that. This works fine of course, but it seems that in a lot of cases
> one can end up writing a large number of small views that all simply
> display a filtered list of objects. I figured it might be helpful to
> abstract this behavior into a couple of additional generic
> views. These will automatically filter based on a keyword captured in
> the URL and a field specified in the urls.py file. I've called these
> new views object_list_field and object_list_foreign_field. They are
> included below:
>
> def object_list_field(request, model, field, value, paginate_by=None,
> page=None,
>                       fv_dict=None, allow_empty=True,
> template_name=None,
>                       template_loader=loader, extra_context=None,
> context_processors=None,
>                       template_object_name='object', mimetype=None):
>     """Extends generic view object_list to display a list of objects
> filtered
>     by an arbitrary field.
>     Works only for 

making some generic views more generic

2009-01-06 Thread Gabe

Hello,

I really like the generic view feature of Django. It's been a handy
way to save a lot of work. Lately though, I've been finding myself
often wanting to display a list of objects filtered by some field. The
documentation suggests handling this by writing a small view that
filters the objects and then calling the generic view from within
that. This works fine of course, but it seems that in a lot of cases
one can end up writing a large number of small views that all simply
display a filtered list of objects. I figured it might be helpful to
abstract this behavior into a couple of additional generic
views. These will automatically filter based on a keyword captured in
the URL and a field specified in the urls.py file. I've called these
new views object_list_field and object_list_foreign_field. They are
included below:

def object_list_field(request, model, field, value, paginate_by=None,
page=None,
  fv_dict=None, allow_empty=True,
template_name=None,
  template_loader=loader, extra_context=None,
context_processors=None,
  template_object_name='object', mimetype=None):
"""Extends generic view object_list to display a list of objects
filtered
by an arbitrary field.
Works only for fields that are not ForeignKey or ManyToMany.
See object_list_foreign_field for ForeignKey fields"""

if not fv_dict:
fv_dict = {}
fv_dict[field] = value
obj_list = model.objects.filter(**fv_dict)

# calculate the number of the first object on this page
# in case the objects are paginated and want to be displayed as
# a numbered list
extra_context = {'start': calc_start(page, paginate_by,
obj_list.count())}

return list_detail.object_list(request=request,
queryset=obj_list,
   paginate_by=paginate_by,
page=page,
   allow_empty=allow_empty,
template_name=template_name,
   template_loader=template_loader,
extra_context=extra_context,
 
context_processors=context_processors,
 
template_object_name=template_object_name,
   mimetype=mimetype)

def object_list_foreign_field(request, model, field, value,
foreign_model,
  foreign_field, fv_dict=None,
  paginate_by=None, page=None,
allow_empty=True,
  template_name=None,
template_loader=loader,
  extra_context=None,
context_processors=None,
  template_object_name='object',
mimetype=None):
"""Generic view to display a list of objects filtered by an
arbitary foreign key field"""

if not fv_dict:
fv_dict = {}
foreign_obj = get_object_or_404(foreign_model, **{foreign_field:
value})
fv_dict[field] = foreign_obj.id
obj_list = model.objects.filter(**fv_dict)

# calculate the number of the first object on this page
# in case the objects are paginated and want to be displayed as
# a numbered list
extra_context = {'start': calc_start(page, paginate_by,
obj_list.count())}

return list_detail.object_list(request=request,
queryset=obj_list,
   extra_context={foreign_field:
foreign_obj},
   paginate_by=paginate_by,
page=page,
   allow_empty=allow_empty,
template_name=template_name,
   template_loader=template_loader,
 
context_processors=context_processors,
 
template_object_name=template_object_name,
   mimetype=mimetype)

Both views expect to capture a variable called "value" from the
URL. They also both accept all arguments that object_list
accepts. These can be specified within urls.py and are passed on to
object_list when it is called by object_list_field or
object_list_foreign_field.

To use the canonical book website as an example, suppose we wanted to
use these views to make a couple of different lists. For instance, we
might want to have an alpabetical listing of authors, with one page
for each letter that their last name could begin with. With
object_list_field, we wouldn't have to write a new view for this, just
add the following to urlpatterns

urlpatterns = patterns('',
...other url patterns...,
(r'^authors/(?P[A-Z]+)/$', object_list_field', 
{'model':
Author,
   
'field': 'lastname__startswith'}),
...other url patterns...,
  )

Of course, this assumes that the template_name and
template_object_name are set to the defaults expected by
list_detail.object_list. If not, these values can also be added to the
dictionary above. As another example, suppose we wanted a view that
showed all books by a particular publisher. Assuming publisher has