2005-07-13 09:25:57 +08:00
# Generic admin views, with admin templates created dynamically at runtime.
2005-10-18 13:23:07 +08:00
from django . contrib . admin . views . decorators import staff_member_required
2005-10-18 13:18:50 +08:00
from django . core import formfields , meta
from django . core . template import loader
2005-07-13 09:25:57 +08:00
from django . core . exceptions import Http404 , ObjectDoesNotExist , PermissionDenied
2005-07-16 01:09:01 +08:00
from django . core . extensions import DjangoContext as Context
2005-09-24 05:52:01 +08:00
from django . core . extensions import get_object_or_404 , render_to_response
2005-10-19 09:09:05 +08:00
from django . models . admin import log
2005-07-13 09:25:57 +08:00
from django . utils . html import strip_tags
from django . utils . httpwrappers import HttpResponse , HttpResponseRedirect
2005-08-02 05:29:52 +08:00
from django . utils . text import capfirst , get_text_list
2005-11-13 06:08:54 +08:00
from django . conf . settings import ADMIN_MEDIA_PREFIX
from django . utils . translation import get_date_formats
2005-07-13 09:25:57 +08:00
import operator
# Text to display within changelist table cells if the value is blank.
EMPTY_CHANGELIST_VALUE = ' (None) '
def _get_mod_opts ( app_label , module_name ) :
" Helper function that returns a tuple of (module, opts), raising Http404 if necessary. "
try :
mod = meta . get_module ( app_label , module_name )
except ImportError :
raise Http404 # Invalid app or module name. Maybe it's not in INSTALLED_APPS.
opts = mod . Klass . _meta
if not opts . admin :
raise Http404 # This object is valid but has no admin interface.
return mod , opts
def get_query_string ( original_params , new_params = { } , remove = [ ] ) :
"""
>> > get_query_string ( { ' first_name ' : ' adrian ' , ' last_name ' : ' smith ' } )
' ?first_name=adrian&last_name=smith '
>> > get_query_string ( { ' first_name ' : ' adrian ' , ' last_name ' : ' smith ' } , { ' first_name ' : ' john ' } )
' ?first_name=john&last_name=smith '
>> > get_query_string ( { ' test ' : ' yes ' } , { ' blah ' : ' no ' } , [ ' te ' ] )
' ?blah=no '
"""
p = original_params . copy ( )
for r in remove :
for k in p . keys ( ) :
if k . startswith ( r ) :
del p [ k ]
for k , v in new_params . items ( ) :
if p . has_key ( k ) and v is None :
del p [ k ]
elif v is not None :
p [ k ] = v
return ' ? ' + ' & ' . join ( [ ' %s = %s ' % ( k , v ) for k , v in p . items ( ) ] ) . replace ( ' ' , ' % 20 ' )
def index ( request ) :
2005-10-19 09:09:05 +08:00
return render_to_response ( ' admin/index ' , { ' title ' : ' Site administration ' } , context_instance = Context ( request ) )
2005-10-18 13:23:07 +08:00
index = staff_member_required ( index )
2005-07-13 09:25:57 +08:00
def change_list ( request , app_label , module_name ) :
from django . core import paginator
from django . utils import dateformat
from django . utils . dates import MONTHS
from django . utils . html import escape
import datetime
# The system will display a "Show all" link only if the total result count
# is less than or equal to this setting.
MAX_SHOW_ALL_ALLOWED = 200
DEFAULT_RESULTS_PER_PAGE = 100
ALL_VAR = ' all '
ORDER_VAR = ' o '
ORDER_TYPE_VAR = ' ot '
PAGE_VAR = ' p '
SEARCH_VAR = ' q '
IS_POPUP_VAR = ' pop '
mod , opts = _get_mod_opts ( app_label , module_name )
if not request . user . has_perm ( app_label + ' . ' + opts . get_change_permission ( ) ) :
raise PermissionDenied
lookup_mod , lookup_opts = mod , opts
# Get search parameters from the query string.
try :
page_num = int ( request . GET . get ( PAGE_VAR , 0 ) )
except ValueError :
page_num = 0
show_all = request . GET . has_key ( ALL_VAR )
is_popup = request . GET . has_key ( IS_POPUP_VAR )
params = dict ( request . GET . copy ( ) )
if params . has_key ( PAGE_VAR ) :
del params [ PAGE_VAR ]
# For ordering, first check the "ordering" parameter in the admin options,
2005-07-17 07:58:49 +08:00
# then check the object's default ordering. If neither of those exist,
# order descending by ID by default. Finally, look for manually-specified
# ordering from the query string.
2005-07-23 01:58:56 +08:00
ordering = lookup_opts . admin . ordering or lookup_opts . ordering or [ ' - ' + lookup_opts . pk . name ]
2005-07-22 21:02:27 +08:00
# Normalize it to new-style ordering.
ordering = meta . handle_legacy_orderlist ( ordering )
if ordering [ 0 ] . startswith ( ' - ' ) :
2005-07-28 22:59:50 +08:00
order_field , order_type = ordering [ 0 ] [ 1 : ] , ' desc '
2005-07-17 07:58:49 +08:00
else :
2005-07-28 22:59:50 +08:00
order_field , order_type = ordering [ 0 ] , ' asc '
2005-07-13 09:25:57 +08:00
if params . has_key ( ORDER_VAR ) :
try :
try :
2005-07-22 12:40:37 +08:00
f = lookup_opts . get_field ( lookup_opts . admin . list_display [ int ( params [ ORDER_VAR ] ) ] )
2005-07-13 09:25:57 +08:00
except meta . FieldDoesNotExist :
pass
else :
if not isinstance ( f . rel , meta . ManyToOne ) or not f . null :
order_field = f . name
except ( IndexError , ValueError ) :
pass # Invalid ordering specified. Just use the default.
if params . has_key ( ORDER_TYPE_VAR ) and params [ ORDER_TYPE_VAR ] in ( ' asc ' , ' desc ' ) :
order_type = params [ ORDER_TYPE_VAR ]
query = request . GET . get ( SEARCH_VAR , ' ' )
# Prepare the lookup parameters for the API lookup.
lookup_params = params . copy ( )
for i in ( ALL_VAR , ORDER_VAR , ORDER_TYPE_VAR , SEARCH_VAR , IS_POPUP_VAR ) :
if lookup_params . has_key ( i ) :
del lookup_params [ i ]
# If the order-by field is a field with a relationship, order by the value
# in the related table.
lookup_order_field = order_field
if isinstance ( lookup_opts . get_field ( order_field ) . rel , meta . ManyToOne ) :
f = lookup_opts . get_field ( order_field )
2005-09-26 06:27:23 +08:00
rel_ordering = f . rel . to . ordering and f . rel . to . ordering [ 0 ] or f . rel . to . pk . column
lookup_order_field = ' %s . %s ' % ( f . rel . to . db_table , rel_ordering )
2005-11-06 02:14:17 +08:00
if lookup_opts . admin . list_select_related :
lookup_params [ ' select_related ' ] = True
else :
# Use select_related if one of the list_display options is a field with
# a relationship.
for field_name in lookup_opts . admin . list_display :
try :
f = lookup_opts . get_field ( field_name )
except meta . FieldDoesNotExist :
pass
else :
if isinstance ( f . rel , meta . ManyToOne ) :
lookup_params [ ' select_related ' ] = True
break
2005-07-28 22:59:50 +08:00
lookup_params [ ' order_by ' ] = ( ( order_type == ' desc ' and ' - ' or ' ' ) + lookup_order_field , )
2005-07-13 09:25:57 +08:00
if lookup_opts . admin . search_fields and query :
or_queries = [ ]
for bit in query . split ( ) :
or_query = [ ]
for field_name in lookup_opts . admin . search_fields :
or_query . append ( ( ' %s __icontains ' % field_name , bit ) )
or_queries . append ( or_query )
lookup_params [ ' _or ' ] = or_queries
if opts . one_to_one_field :
lookup_params . update ( opts . one_to_one_field . rel . limit_choices_to )
# Get the results.
try :
p = paginator . ObjectPaginator ( lookup_mod , lookup_params , DEFAULT_RESULTS_PER_PAGE )
# Naked except! Because we don't have any other way of validating "params".
# They might be invalid if the keyword arguments are incorrect, or if the
# values are not in the correct type (which would result in a database
# error).
except :
return HttpResponseRedirect ( request . path )
# Get the total number of objects, with no filters applied.
real_lookup_params = lookup_params . copy ( )
del real_lookup_params [ ' order_by ' ]
if real_lookup_params :
full_result_count = lookup_mod . get_count ( )
else :
full_result_count = p . hits
del real_lookup_params
result_count = p . hits
can_show_all = result_count < = MAX_SHOW_ALL_ALLOWED
multi_page = result_count > DEFAULT_RESULTS_PER_PAGE
# Get the list of objects to display on this page.
if ( show_all and can_show_all ) or not multi_page :
result_list = lookup_mod . get_list ( * * lookup_params )
else :
try :
result_list = p . get_page ( page_num )
except paginator . InvalidPage :
result_list = [ ]
# Calculate filters first, because a CSS class high in the document depends
# on whether they are available.
filter_template = [ ]
if lookup_opts . admin . list_filter and not opts . one_to_one_field :
filter_fields = [ lookup_opts . get_field ( field_name ) for field_name in lookup_opts . admin . list_filter ]
for f in filter_fields :
# Many-to-many or many-to-one filter.
if f . rel :
if isinstance ( f , meta . ManyToManyField ) :
lookup_title = f . rel . to . verbose_name
else :
lookup_title = f . verbose_name
2005-09-20 11:46:11 +08:00
lookup_kwarg = ' %s __ %s __exact ' % ( f . name , f . rel . to . pk . name )
2005-07-13 09:25:57 +08:00
lookup_val = request . GET . get ( lookup_kwarg , None )
lookup_choices = f . rel . to . get_model_module ( ) . get_list ( )
if len ( lookup_choices ) > 1 :
filter_template . append ( ' <h3>By %s :</h3> \n <ul> \n ' % lookup_title )
filter_template . append ( ' <li %s ><a href= " %s " >All</a></li> \n ' % \
( ( lookup_val is None and ' class= " selected " ' or ' ' ) ,
get_query_string ( params , { } , [ lookup_kwarg ] ) ) )
for val in lookup_choices :
2005-11-10 07:37:41 +08:00
pk_val = getattr ( val , f . rel . to . pk . attname )
2005-07-13 09:25:57 +08:00
filter_template . append ( ' <li %s ><a href= " %s " > %r </a></li> \n ' % \
2005-09-20 11:46:11 +08:00
( ( lookup_val == str ( pk_val ) and ' class= " selected " ' or ' ' ) ,
get_query_string ( params , { lookup_kwarg : pk_val } ) , val ) )
2005-07-13 09:25:57 +08:00
filter_template . append ( ' </ul> \n \n ' )
# Field with choices.
elif f . choices :
lookup_kwarg = ' %s __exact ' % f . name
lookup_val = request . GET . get ( lookup_kwarg , None )
filter_template . append ( ' <h3>By %s :</h3><ul> \n ' % f . verbose_name )
filter_template . append ( ' <li %s ><a href= " %s " >All</a></li> \n ' % \
( ( lookup_val is None and ' class= " selected " ' or ' ' ) ,
get_query_string ( params , { } , [ lookup_kwarg ] ) ) )
for k , v in f . choices :
filter_template . append ( ' <li %s ><a href= " %s " > %s </a></li> ' % \
( ( str ( k ) == lookup_val ) and ' class= " selected " ' or ' ' ,
get_query_string ( params , { lookup_kwarg : k } ) , v ) )
filter_template . append ( ' </ul> \n \n ' )
# Date filter.
elif isinstance ( f , meta . DateField ) :
today = datetime . date . today ( )
one_week_ago = today - datetime . timedelta ( days = 7 )
2005-08-07 04:39:43 +08:00
field_generic = ' %s __ ' % f . name
2005-07-13 09:25:57 +08:00
filter_template . append ( ' <h3>By %s :</h3><ul> \n ' % f . verbose_name )
date_params = dict ( [ ( k , v ) for k , v in params . items ( ) if k . startswith ( field_generic ) ] )
today_str = isinstance ( f , meta . DateTimeField ) and today . strftime ( ' % Y- % m- %d 23:59:59 ' ) or today . strftime ( ' % Y- % m- %d ' )
for title , param_dict in (
( ' Any date ' , { } ) ,
( ' Today ' , { ' %s __year ' % f . name : str ( today . year ) , ' %s __month ' % f . name : str ( today . month ) , ' %s __day ' % f . name : str ( today . day ) } ) ,
( ' Past 7 days ' , { ' %s __gte ' % f . name : one_week_ago . strftime ( ' % Y- % m- %d ' ) , ' %s __lte ' % f . name : today_str } ) ,
( ' This month ' , { ' %s __year ' % f . name : str ( today . year ) , ' %s __month ' % f . name : str ( today . month ) } ) ,
( ' This year ' , { ' %s __year ' % f . name : str ( today . year ) } )
) :
filter_template . append ( ' <li %s ><a href= " %s " > %s </a></li> \n ' % \
( ( date_params == param_dict ) and ' class= " selected " ' or ' ' ,
get_query_string ( params , param_dict , field_generic ) , title ) )
filter_template . append ( ' </ul> \n \n ' )
elif isinstance ( f , meta . BooleanField ) or isinstance ( f , meta . NullBooleanField ) :
lookup_kwarg = ' %s __exact ' % f . name
lookup_kwarg2 = ' %s __isnull ' % f . name
lookup_val = request . GET . get ( lookup_kwarg , None )
lookup_val2 = request . GET . get ( lookup_kwarg2 , None )
filter_template . append ( ' <h3>By %s :</h3><ul> \n ' % f . verbose_name )
2005-10-09 04:23:11 +08:00
for k , v in ( ( ' All ' , None ) , ( ' Yes ' , ' 1 ' ) , ( ' No ' , ' 0 ' ) ) :
2005-07-13 09:25:57 +08:00
filter_template . append ( ' <li %s ><a href= " %s " > %s </a></li> \n ' % \
( ( ( lookup_val == v and not lookup_val2 ) and ' class= " selected " ' or ' ' ) ,
get_query_string ( params , { lookup_kwarg : v } , [ lookup_kwarg2 ] ) , k ) )
if isinstance ( f , meta . NullBooleanField ) :
filter_template . append ( ' <li %s ><a href= " %s " > %s </a></li> \n ' % \
( ( ( lookup_val2 == ' True ' ) and ' class= " selected " ' or ' ' ) ,
get_query_string ( params , { lookup_kwarg2 : ' True ' } , [ lookup_kwarg ] ) , ' Unknown ' ) )
filter_template . append ( ' </ul> \n \n ' )
else :
pass # Invalid argument to "list_filter"
2005-10-19 09:09:05 +08:00
raw_template = [ ' { % e xtends " admin/base_site " % } \n ' ]
2005-07-13 09:25:57 +08:00
raw_template . append ( ' { % block bodyclass % }change-list { % e ndblock % } \n ' )
if not is_popup :
2005-08-02 05:29:52 +08:00
raw_template . append ( ' { %% block breadcrumbs %% }<div class= " breadcrumbs " ><a href= " ../../ " >Home</a> › %s </div> { %% endblock %% } \n ' % capfirst ( opts . verbose_name_plural ) )
2005-07-13 09:25:57 +08:00
raw_template . append ( ' { % block coltype % }flex { % e ndblock % } ' )
raw_template . append ( ' { % block content % } \n ' )
raw_template . append ( ' <div id= " content-main " > \n ' )
if request . user . has_perm ( app_label + ' . ' + lookup_opts . get_add_permission ( ) ) :
raw_template . append ( ' <ul class= " object-tools " ><li><a href= " add/ %s " class= " addlink " >Add %s </a></li></ul> \n ' % ( ( is_popup and ' ?_popup=1 ' or ' ' ) , opts . verbose_name ) )
raw_template . append ( ' <div class= " module %s " id= " changelist " > \n ' % ( filter_template and ' filtered ' or ' ' ) )
# Search form.
if lookup_opts . admin . search_fields :
raw_template . append ( ' <div id= " toolbar " > \n <form id= " changelist-search " action= " " method= " get " > \n ' )
2005-07-18 00:14:29 +08:00
raw_template . append ( ' <label><img src= " %s img/admin/icon_searchbox.png " /></label> ' % ADMIN_MEDIA_PREFIX )
2005-07-13 09:25:57 +08:00
raw_template . append ( ' <input type= " text " size= " 40 " name= " %s " value= " %s " id= " searchbar " /> ' % \
( SEARCH_VAR , escape ( query ) ) )
raw_template . append ( ' <input type= " submit " value= " Go " /> ' )
if result_count != full_result_count and not opts . one_to_one_field :
raw_template . append ( ' <span class= " small quiet " > %s result %s (<a href= " ? " > %s total</a>)</span> ' % \
( result_count , ( result_count != 1 and ' s ' or ' ' ) , full_result_count ) )
for k , v in params . items ( ) :
if k != SEARCH_VAR :
raw_template . append ( ' <input type= " hidden " name= " %s " value= " %s " /> ' % ( escape ( k ) , escape ( v ) ) )
raw_template . append ( ' </form></div> \n ' )
raw_template . append ( ' <script type= " text/javascript " >document.getElementById( " searchbar " ).focus();</script> ' )
# Date-based navigation.
if lookup_opts . admin . date_hierarchy :
field_name = lookup_opts . admin . date_hierarchy
year_field = ' %s __year ' % field_name
month_field = ' %s __month ' % field_name
day_field = ' %s __day ' % field_name
field_generic = ' %s __ ' % field_name
year_lookup = params . get ( year_field )
month_lookup = params . get ( month_field )
day_lookup = params . get ( day_field )
raw_template . append ( ' <div class= " xfull " > \n <ul class= " toplinks " > \n ' )
if year_lookup and month_lookup and day_lookup :
raw_template . append ( ' <li class= " date-back " ><a href= " %s " >‹ %s %s </a></li> ' % \
( get_query_string ( params , { year_field : year_lookup , month_field : month_lookup } , [ field_generic ] ) , MONTHS [ int ( month_lookup ) ] , year_lookup ) )
raw_template . append ( ' <li> %s %s </li> ' % ( MONTHS [ int ( month_lookup ) ] , day_lookup ) )
elif year_lookup and month_lookup :
raw_template . append ( ' <li class= " date-back " ><a href= " %s " >‹ %s </a></li> ' % \
( get_query_string ( params , { year_field : year_lookup } , [ field_generic ] ) , year_lookup ) )
date_lookup_params = lookup_params . copy ( )
date_lookup_params . update ( { year_field : year_lookup , month_field : month_lookup } )
for day in getattr ( lookup_mod , ' get_ %s _list ' % field_name ) ( ' day ' , * * date_lookup_params ) :
raw_template . append ( ' <li><a href= " %s " > %s </a></li> ' % \
( get_query_string ( params , { year_field : year_lookup , month_field : month_lookup , day_field : day . day } , [ field_generic ] ) , day . strftime ( ' % B %d ' ) ) )
elif year_lookup :
raw_template . append ( ' <li class= " date-back " ><a href= " %s " >‹ All dates</a></li> ' % \
get_query_string ( params , { } , [ year_field ] ) )
date_lookup_params = lookup_params . copy ( )
date_lookup_params . update ( { year_field : year_lookup } )
for month in getattr ( lookup_mod , ' get_ %s _list ' % field_name ) ( ' month ' , * * date_lookup_params ) :
raw_template . append ( ' <li><a href= " %s " > %s %s </a></li> ' % \
( get_query_string ( params , { year_field : year_lookup , month_field : month . month } , [ field_generic ] ) , month . strftime ( ' % B ' ) , month . year ) )
else :
for year in getattr ( lookup_mod , ' get_ %s _list ' % field_name ) ( ' year ' , * * lookup_params ) :
raw_template . append ( ' <li><a href= " %s " > %s </a></li> \n ' % \
( get_query_string ( params , { year_field : year . year } , [ field_generic ] ) , year . year ) )
raw_template . append ( ' </ul><br class= " clear " /> \n </div> \n ' )
# Filters.
if filter_template :
raw_template . append ( ' <div id= " changelist-filter " > \n <h2>Filter</h2> \n ' )
raw_template . extend ( filter_template )
raw_template . append ( ' </div> ' )
del filter_template
# Result table.
if result_list :
# Table headers.
raw_template . append ( ' <table cellspacing= " 0 " > \n <thead> \n <tr> \n ' )
for i , field_name in enumerate ( lookup_opts . admin . list_display ) :
try :
f = lookup_opts . get_field ( field_name )
except meta . FieldDoesNotExist :
# For non-field list_display values, check for the function
# attribute "short_description". If that doesn't exist, fall
# back to the method name. And __repr__ is a special-case.
if field_name == ' __repr__ ' :
header = lookup_opts . verbose_name
else :
func = getattr ( mod . Klass , field_name ) # Let AttributeErrors propogate.
try :
header = func . short_description
except AttributeError :
2005-11-17 23:26:50 +08:00
header = func . __name__ . replace ( ' _ ' , ' ' )
2005-07-13 09:25:57 +08:00
# Non-field list_display values don't get ordering capability.
2005-08-02 05:29:52 +08:00
raw_template . append ( ' <th> %s </th> ' % capfirst ( header ) )
2005-07-13 09:25:57 +08:00
else :
if isinstance ( f . rel , meta . ManyToOne ) and f . null :
2005-08-02 05:29:52 +08:00
raw_template . append ( ' <th> %s </th> ' % capfirst ( f . verbose_name ) )
2005-07-13 09:25:57 +08:00
else :
th_classes = [ ]
new_order_type = ' asc '
if field_name == order_field :
th_classes . append ( ' sorted %s ending ' % order_type . lower ( ) )
new_order_type = { ' asc ' : ' desc ' , ' desc ' : ' asc ' } [ order_type . lower ( ) ]
raw_template . append ( ' <th %s ><a href= " %s " > %s </a></th> ' % \
( ( th_classes and ' class= " %s " ' % ' ' . join ( th_classes ) or ' ' ) ,
get_query_string ( params , { ORDER_VAR : i , ORDER_TYPE_VAR : new_order_type } ) ,
2005-08-02 05:29:52 +08:00
capfirst ( f . verbose_name ) ) )
2005-07-13 09:25:57 +08:00
raw_template . append ( ' </tr> \n </thead> \n ' )
# Result rows.
2005-11-10 07:37:41 +08:00
pk = lookup_opts . pk . attname
2005-07-13 09:25:57 +08:00
for i , result in enumerate ( result_list ) :
raw_template . append ( ' <tr class= " row %s " > \n ' % ( i % 2 + 1 ) )
for j , field_name in enumerate ( lookup_opts . admin . list_display ) :
row_class = ' '
try :
f = lookup_opts . get_field ( field_name )
except meta . FieldDoesNotExist :
# For non-field list_display values, the value is a method
# name. Execute the method.
2005-11-12 01:15:24 +08:00
func = getattr ( result , field_name )
2005-07-13 09:25:57 +08:00
try :
2005-11-12 01:15:24 +08:00
result_repr = str ( func ( ) )
2005-07-13 09:25:57 +08:00
except ObjectDoesNotExist :
result_repr = EMPTY_CHANGELIST_VALUE
2005-11-12 01:15:24 +08:00
else :
# Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True.
if not getattr ( func , ' allow_tags ' , False ) :
result_repr = strip_tags ( result_repr )
2005-07-13 09:25:57 +08:00
else :
2005-11-10 07:37:41 +08:00
field_val = getattr ( result , f . attname )
2005-07-13 09:25:57 +08:00
# Foreign-key fields are special: Use the repr of the
# related object.
if isinstance ( f . rel , meta . ManyToOne ) :
if field_val is not None :
2005-08-26 06:51:30 +08:00
result_repr = getattr ( result , ' get_ %s ' % f . name ) ( )
2005-07-13 09:25:57 +08:00
else :
result_repr = EMPTY_CHANGELIST_VALUE
2005-11-07 07:49:03 +08:00
# Dates and times are special: They're formatted in a certain way.
elif isinstance ( f , meta . DateField ) or isinstance ( f , meta . TimeField ) :
2005-07-13 09:25:57 +08:00
if field_val :
2005-11-13 06:08:54 +08:00
( date_format , datetime_format , time_format ) = get_date_formats ( )
2005-07-13 09:25:57 +08:00
if isinstance ( f , meta . DateTimeField ) :
2005-11-13 06:08:54 +08:00
result_repr = capfirst ( dateformat . format ( field_val , datetime_format ) )
2005-11-07 07:49:03 +08:00
elif isinstance ( f , meta . TimeField ) :
2005-11-13 06:08:54 +08:00
result_repr = capfirst ( dateformat . time_format ( field_val , time_format ) )
2005-07-13 09:25:57 +08:00
else :
2005-11-13 06:08:54 +08:00
result_repr = capfirst ( dateformat . format ( field_val , date_format ) )
2005-07-13 09:25:57 +08:00
else :
result_repr = EMPTY_CHANGELIST_VALUE
row_class = ' class= " nowrap " '
# Booleans are special: We use images.
elif isinstance ( f , meta . BooleanField ) or isinstance ( f , meta . NullBooleanField ) :
BOOLEAN_MAPPING = { True : ' yes ' , False : ' no ' , None : ' unknown ' }
2005-07-17 00:38:28 +08:00
result_repr = ' <img src= " %s img/admin/icon- %s .gif " alt= " %s " /> ' % ( ADMIN_MEDIA_PREFIX , BOOLEAN_MAPPING [ field_val ] , field_val )
2005-07-13 09:25:57 +08:00
# ImageFields are special: Use a thumbnail.
elif isinstance ( f , meta . ImageField ) :
from django . parts . media . photos import get_thumbnail_url
result_repr = ' <img src= " %s " alt= " %s " title= " %s " /> ' % ( get_thumbnail_url ( getattr ( result , ' get_ %s _url ' % f . name ) ( ) , ' 120 ' ) , field_val , field_val )
# FloatFields are special: Zero-pad the decimals.
elif isinstance ( f , meta . FloatField ) :
if field_val is not None :
result_repr = ( ' %% . %s f ' % f . decimal_places ) % field_val
else :
result_repr = EMPTY_CHANGELIST_VALUE
# Fields with choices are special: Use the representation
# of the choice.
elif f . choices :
result_repr = dict ( f . choices ) . get ( field_val , EMPTY_CHANGELIST_VALUE )
else :
result_repr = strip_tags ( str ( field_val ) )
# Some browsers don't like empty "<td></td>"s.
if result_repr == ' ' :
result_repr = ' '
if j == 0 : # First column is a special case
result_id = getattr ( result , pk )
raw_template . append ( ' <th %s ><a href= " %s / " %s > %s </a></th> ' % \
2005-10-06 09:51:30 +08:00
( row_class , result_id , ( is_popup and ' onclick= " opener.dismissRelatedLookupPopup(window, %r ); return false; " ' % result_id or ' ' ) , result_repr ) )
2005-07-13 09:25:57 +08:00
else :
raw_template . append ( ' <td %s > %s </td> ' % ( row_class , result_repr ) )
raw_template . append ( ' </tr> \n ' )
del result_list # to free memory
raw_template . append ( ' </table> \n ' )
else :
raw_template . append ( ' <p>No %s matched your search criteria.</p> ' % opts . verbose_name_plural )
# Pagination.
raw_template . append ( ' <p class= " paginator " > ' )
if ( show_all and can_show_all ) or not multi_page :
pass
else :
raw_template . append ( ' Page › ' )
ON_EACH_SIDE = 3
ON_ENDS = 2
DOT = ' . '
# If there are 10 or fewer pages, display links to every page.
# Otherwise, do some fancy
if p . pages < = 10 :
page_range = range ( p . pages )
else :
# Insert "smart" pagination links, so that there are always ON_ENDS
# links at either end of the list of pages, and there are always
# ON_EACH_SIDE links at either end of the "current page" link.
page_range = [ ]
if page_num > ( ON_EACH_SIDE + ON_ENDS ) :
page_range . extend ( range ( 0 , ON_EACH_SIDE - 1 ) )
page_range . append ( DOT )
page_range . extend ( range ( page_num - ON_EACH_SIDE , page_num + 1 ) )
else :
page_range . extend ( range ( 0 , page_num + 1 ) )
if page_num < ( p . pages - ON_EACH_SIDE - ON_ENDS - 1 ) :
page_range . extend ( range ( page_num + 1 , page_num + ON_EACH_SIDE + 1 ) )
page_range . append ( DOT )
page_range . extend ( range ( p . pages - ON_ENDS , p . pages ) )
else :
page_range . extend ( range ( page_num + 1 , p . pages ) )
for i in page_range :
if i == DOT :
raw_template . append ( ' ... ' )
elif i == page_num :
raw_template . append ( ' <span class= " this-page " > %d </span> ' % ( i + 1 ) )
else :
raw_template . append ( ' <a href= " %s " %s > %d </a> ' % \
( get_query_string ( params , { PAGE_VAR : i } ) , ( i == p . pages - 1 and ' class= " end " ' or ' ' ) , i + 1 ) )
raw_template . append ( ' %s %s ' % ( result_count , result_count == 1 and opts . verbose_name or opts . verbose_name_plural ) )
if can_show_all and not show_all and multi_page :
raw_template . append ( ' <a href= " %s " class= " showall " >Show all</a> ' % \
get_query_string ( params , { ALL_VAR : ' ' } ) )
raw_template . append ( ' </p> ' )
raw_template . append ( ' </div> \n </div> ' )
raw_template . append ( ' { % e ndblock % } \n ' )
2005-10-18 13:18:50 +08:00
t = loader . get_template_from_string ( ' ' . join ( raw_template ) )
2005-07-13 09:25:57 +08:00
c = Context ( request , {
' title ' : ( is_popup and ' Select %s ' % opts . verbose_name or ' Select %s to change ' % opts . verbose_name ) ,
' is_popup ' : is_popup ,
} )
2005-09-23 09:50:01 +08:00
return HttpResponse ( t . render ( c ) )
2005-10-18 13:23:07 +08:00
change_list = staff_member_required ( change_list )
2005-07-13 09:25:57 +08:00
def _get_flattened_data ( field , val ) :
"""
Returns a dictionary mapping the field ' s manipulator field names to its
" flattened " string values for the admin view . " val " is an instance of the
field ' s value.
"""
if isinstance ( field , meta . DateTimeField ) :
date_field , time_field = field . get_manipulator_field_names ( ' ' )
return { date_field : ( val is not None and val . strftime ( " % Y- % m- %d " ) or ' ' ) ,
time_field : ( val is not None and val . strftime ( " % H: % M: % S " ) or ' ' ) }
elif isinstance ( field , meta . DateField ) :
return { field . name : ( val is not None and val . strftime ( " % Y- % m- %d " ) or ' ' ) }
elif isinstance ( field , meta . TimeField ) :
return { field . name : ( val is not None and val . strftime ( " % H: % M: % S " ) or ' ' ) }
else :
return { field . name : val }
2005-08-17 02:08:37 +08:00
use_raw_id_admin = lambda field : isinstance ( field . rel , ( meta . ManyToOne , meta . ManyToMany ) ) and field . rel . raw_id_admin
2005-07-13 09:25:57 +08:00
def _get_submit_row_template ( opts , app_label , add , change , show_delete , ordered_objects ) :
t = [ ' <div class= " submit-row " > ' ]
if change or show_delete :
t . append ( ' { %% if perms. %s . %s %% } { %% if not is_popup %% }<p class= " float-left " ><a href= " delete/ " class= " deletelink " >Delete</a></p> { %% endif %% } { %% endif %% } ' % \
( app_label , opts . get_delete_permission ( ) ) )
if change and opts . admin . save_as :
t . append ( ' { %% if not is_popup %% }<input type= " submit " value= " Save as new " name= " _saveasnew " %s /> { %% endif %% } ' % \
( ordered_objects and change and ' onclick= " submitOrderForm(); " ' or ' ' ) )
if not opts . admin . save_as or add :
t . append ( ' { %% if not is_popup %% }<input type= " submit " value= " Save and add another " name= " _addanother " %s /> { %% endif %% } ' % \
( ordered_objects and change and ' onclick= " submitOrderForm(); " ' or ' ' ) )
2005-09-30 06:34:17 +08:00
t . append ( ' { %% if not is_popup %% }<input type= " submit " value= " Save and continue editing " name= " _continue " %s /> { %% endif %% } ' % \
2005-07-13 09:25:57 +08:00
( ordered_objects and change and ' onclick= " submitOrderForm(); " ' or ' ' ) )
t . append ( ' <input type= " submit " value= " Save " class= " default " %s /> ' % \
( ordered_objects and change and ' onclick= " submitOrderForm(); " ' or ' ' ) )
t . append ( ' </div> \n ' )
return t
def _get_template ( opts , app_label , add = False , change = False , show_delete = False , form_url = ' ' ) :
2005-07-21 11:19:30 +08:00
admin_field_objs = opts . admin . get_field_objs ( opts )
2005-07-13 09:25:57 +08:00
ordered_objects = opts . get_ordered_objects ( ) [ : ]
auto_populated_fields = [ f for f in opts . fields if f . prepopulate_from ]
2005-10-19 09:09:05 +08:00
t = [ ' { % e xtends " admin/base_site " % } \n ' ]
2005-07-13 09:25:57 +08:00
t . append ( ' { % block extrahead % } ' )
# Put in any necessary JavaScript imports.
2005-07-17 00:38:28 +08:00
javascript_imports = [ ' %s js/core.js ' % ADMIN_MEDIA_PREFIX , ' %s js/admin/RelatedObjectLookups.js ' % ADMIN_MEDIA_PREFIX ]
2005-07-13 09:25:57 +08:00
if auto_populated_fields :
2005-07-17 00:38:28 +08:00
javascript_imports . append ( ' %s js/urlify.js ' % ADMIN_MEDIA_PREFIX )
2005-07-13 09:25:57 +08:00
if opts . has_field_type ( meta . DateTimeField ) or opts . has_field_type ( meta . TimeField ) or opts . has_field_type ( meta . DateField ) :
2005-07-17 00:38:28 +08:00
javascript_imports . extend ( [ ' %s js/calendar.js ' % ADMIN_MEDIA_PREFIX , ' %s js/admin/DateTimeShortcuts.js ' % ADMIN_MEDIA_PREFIX ] )
2005-07-13 09:25:57 +08:00
if ordered_objects :
2005-07-17 00:38:28 +08:00
javascript_imports . extend ( [ ' %s js/getElementsBySelector.js ' % ADMIN_MEDIA_PREFIX , ' %s js/dom-drag.js ' % ADMIN_MEDIA_PREFIX , ' %s js/admin/ordering.js ' % ADMIN_MEDIA_PREFIX ] )
2005-07-13 09:25:57 +08:00
if opts . admin . js :
javascript_imports . extend ( opts . admin . js )
2005-07-21 11:19:30 +08:00
seen_collapse = False
for _ , options in admin_field_objs :
if not seen_collapse and ' collapse ' in options . get ( ' classes ' , ' ' ) :
seen_collapse = True
javascript_imports . append ( ' %s js/admin/CollapsedFieldsets.js ' % ADMIN_MEDIA_PREFIX )
2005-10-25 09:47:34 +08:00
for field_list in options [ ' fields ' ] :
try :
2005-07-13 09:25:57 +08:00
for f in field_list :
if f . rel and isinstance ( f , meta . ManyToManyField ) and f . rel . filter_interface :
2005-07-17 00:38:28 +08:00
javascript_imports . extend ( [ ' %s js/SelectBox.js ' % ADMIN_MEDIA_PREFIX , ' %s js/SelectFilter2.js ' % ADMIN_MEDIA_PREFIX ] )
2005-07-13 09:25:57 +08:00
raise StopIteration
2005-10-25 09:47:34 +08:00
except StopIteration :
break
2005-07-13 09:25:57 +08:00
for j in javascript_imports :
t . append ( ' <script type= " text/javascript " src= " %s " ></script> ' % j )
t . append ( ' { % e ndblock % } \n ' )
if ordered_objects :
coltype = ' colMS '
else :
coltype = ' colM '
t . append ( ' { %% block coltype %% } %s { %% endblock %% } \n ' % coltype )
t . append ( ' { %% block bodyclass %% } %s - %s change-form { %% endblock %% } \n ' % ( app_label , opts . object_name . lower ( ) ) )
breadcrumb_title = add and " Add %s " % opts . verbose_name or ' {{ original|striptags|truncatewords: " 18 " }} '
t . append ( ' { %% block breadcrumbs %% } { %% if not is_popup %% }<div class= " breadcrumbs " ><a href= " ../../../ " >Home</a> › <a href= " ../ " > %s </a> › %s </div> { %% endif %% } { %% endblock %% } \n ' % \
2005-08-02 05:29:52 +08:00
( capfirst ( opts . verbose_name_plural ) , breadcrumb_title ) )
2005-07-13 09:25:57 +08:00
t . append ( ' { % block content % }<div id= " content-main " > \n ' )
if change :
t . append ( ' { % i f not is_popup % } ' )
t . append ( ' <ul class= " object-tools " ><li><a href= " history/ " class= " historylink " >History</a></li> ' )
if hasattr ( opts . get_model_module ( ) . Klass , ' get_absolute_url ' ) :
t . append ( ' <li><a href= " /r/ %s / {{ object_id }}/ " class= " viewsitelink " >View on site</a></li> ' % opts . get_content_type_id ( ) )
t . append ( ' </ul> \n ' )
t . append ( ' { % e ndif % } ' )
t . append ( ' <form ' )
if opts . has_field_type ( meta . FileField ) :
t . append ( ' enctype= " multipart/form-data " ' )
t . append ( ' action= " %s " method= " post " > \n ' % form_url )
t . append ( ' { % i f is_popup % }<input type= " hidden " name= " _popup " value= " 1 " > { % e ndif % } ' )
if opts . admin . save_on_top :
t . extend ( _get_submit_row_template ( opts , app_label , add , change , show_delete , ordered_objects ) )
t . append ( ' { % i f form.error_dict % }<p class= " errornote " >Please correct the error {{ form.error_dict.items|pluralize }} below.</p> { % e ndif % } \n ' )
2005-07-21 11:19:30 +08:00
for fieldset_name , options in admin_field_objs :
2005-07-13 09:25:57 +08:00
t . append ( ' <fieldset class= " module aligned %s " > \n \n ' % options . get ( ' classes ' , ' ' ) )
if fieldset_name :
t . append ( ' <h2> %s </h2> \n ' % fieldset_name )
for field_list in options [ ' fields ' ] :
t . append ( _get_admin_field ( field_list , ' form. ' , False , add , change ) )
for f in field_list :
if f . rel and isinstance ( f , meta . ManyToManyField ) and f . rel . filter_interface :
2005-08-10 09:01:54 +08:00
t . append ( ' <script type= " text/javascript " >addEvent(window, " load " , function(e) { SelectFilter.init( " id_ %s " , " %s " , %s , %r ); });</script> \n ' % ( f . name , f . verbose_name , f . rel . filter_interface - 1 , ADMIN_MEDIA_PREFIX ) )
2005-07-13 09:25:57 +08:00
t . append ( ' </fieldset> \n ' )
if ordered_objects and change :
t . append ( ' <fieldset class= " module " ><h2>Ordering</h2> ' )
t . append ( ' <div class= " form-row { % i f form.order_.errors % } error { % e ndif % } " > \n ' )
t . append ( ' { % i f form.order_.errors % } {{ form.order_.html_error_list }} { % e ndif % } ' )
t . append ( ' <p><label for= " id_order_ " >Order:</label> {{ form.order_ }}</p> \n ' )
t . append ( ' </div></fieldset> \n ' )
for rel_obj , rel_field in opts . get_inline_related_objects ( ) :
var_name = rel_obj . object_name . lower ( )
field_list = [ f for f in rel_obj . fields + rel_obj . many_to_many if f . editable and f != rel_field ]
2005-08-10 05:08:00 +08:00
t . append ( ' <fieldset class= " module %s " > \n ' % ( ( rel_field . rel . edit_inline != meta . TABULAR ) and ' aligned ' or ' ' ) )
2005-07-13 09:25:57 +08:00
view_on_site = ' '
if change and hasattr ( rel_obj , ' get_absolute_url ' ) :
view_on_site = ' { %% if %s .original %% }<a href= " /r/ {{ %s .content_type_id }}/ {{ %s .original.id }}/ " >View on site</a> { %% endif %% } ' % ( var_name , var_name , var_name )
2005-08-10 05:08:00 +08:00
if rel_field . rel . edit_inline == meta . TABULAR :
2005-08-02 05:29:52 +08:00
t . append ( ' <h2> %s </h2> \n <table> \n ' % capfirst ( rel_obj . verbose_name_plural ) )
2005-07-13 09:25:57 +08:00
t . append ( ' <thead><tr> ' )
for f in field_list :
if isinstance ( f , meta . AutoField ) :
continue
2005-08-02 05:29:52 +08:00
t . append ( ' <th %s > %s </th> ' % ( f . blank and ' class= " optional " ' or ' ' , capfirst ( f . verbose_name ) ) )
2005-07-13 09:25:57 +08:00
t . append ( ' </tr></thead> \n ' )
t . append ( ' { %% for %s in form. %s %% } \n ' % ( var_name , rel_obj . module_name ) )
if change :
for f in field_list :
if use_raw_id_admin ( f ) :
t . append ( ' { %% if %s .original %% } ' % var_name )
t . append ( ' <tr class= " row-label { % c ycle row1,row2 % } " > ' )
t . append ( ' <td colspan= " %s " ><strong> {{ %s .original }}</strong></td> ' % ( 30 , var_name ) )
t . append ( ' </tr> { % e ndif % } \n ' )
break
t . append ( ' { %% if %s %% } \n ' % ' or ' . join ( [ ' %s . %s .errors ' % ( var_name , f . name ) for f in field_list ] ) )
t . append ( ' <tr class= " errorlist " ><td colspan= " %s " > %s </td></tr> \n { %% endif %% } \n ' % \
( len ( field_list ) , ' ' . join ( [ ' {{ %s . %s .html_error_list }} ' % ( var_name , f . name ) for f in field_list ] ) ) )
t . append ( ' <tr class= " { % c ycle row1,row2 % } " > \n ' )
hidden_fields = [ ]
for f in field_list :
form_widget = _get_admin_field_form_widget ( f , var_name + ' . ' , True , add , change )
# Don't put AutoFields within a <td>, because they're hidden.
if not isinstance ( f , meta . AutoField ) :
# Fields with raw_id_admin=True get class="nowrap".
if use_raw_id_admin ( f ) :
t . append ( ' <td class= " nowrap { %% if %s . %s .errors %% }error " { %% endif %% } " > %s </td> \n ' % ( var_name , f . name , form_widget ) )
else :
t . append ( ' <td { %% if %s . %s .errors %% } class= " error " { %% endif %% }> %s </td> \n ' % ( var_name , f . name , form_widget ) )
else :
hidden_fields . append ( form_widget )
if hasattr ( rel_obj , ' get_absolute_url ' ) :
t . append ( ' <td> %s </td> \n ' % view_on_site )
t . append ( ' </tr> \n ' )
t . append ( ' { % e ndfor % } \n </table> \n ' )
# Write out the hidden fields. We didn't write them out earlier
# because it would've been invalid HTML.
t . append ( ' { %% for %s in form. %s %% } \n ' % ( var_name , rel_obj . module_name ) )
t . extend ( hidden_fields )
t . append ( ' { % e ndfor % } \n ' )
2005-08-10 05:08:00 +08:00
else : # edit_inline == STACKED
2005-07-13 09:25:57 +08:00
t . append ( ' { %% for %s in form. %s %% } ' % ( var_name , rel_obj . module_name ) )
2005-08-02 05:29:52 +08:00
t . append ( ' <h2> %s # {{ forloop.counter }}</h2> ' % capfirst ( rel_obj . verbose_name ) )
2005-07-13 09:25:57 +08:00
if view_on_site :
t . append ( ' <p> %s </p> ' % view_on_site )
for f in field_list :
# Don't put AutoFields within the widget -- just use the field.
if isinstance ( f , meta . AutoField ) :
t . append ( _get_admin_field_form_widget ( f , var_name + ' . ' , True , add , change ) )
else :
t . append ( _get_admin_field ( [ f ] , var_name + ' . ' , True , add , change ) )
t . append ( ' { % e ndfor % } \n ' )
t . append ( ' </fieldset> \n ' )
t . extend ( _get_submit_row_template ( opts , app_label , add , change , show_delete , ordered_objects ) )
if add :
# Add focus to the first field on the form, if this is an "add" form.
t . append ( ' <script type= " text/javascript " >document.getElementById( " id_ %s " ).focus();</script> ' % \
2005-07-21 11:19:30 +08:00
admin_field_objs [ 0 ] [ 1 ] [ ' fields ' ] [ 0 ] [ 0 ] . get_manipulator_field_names ( ' ' ) [ 0 ] )
2005-07-13 09:25:57 +08:00
if auto_populated_fields :
t . append ( ' <script type= " text/javascript " > ' )
for field in auto_populated_fields :
if change :
t . append ( ' document.getElementById( " id_ %s " )._changed = true; ' % field . name )
else :
t . append ( ' document.getElementById( " id_ %s " ).onchange = function() { this._changed = true; }; ' % field . name )
for f in field . prepopulate_from :
t . append ( ' document.getElementById( " id_ %s " ).onkeyup = function() { var e = document.getElementById( " id_ %s " ); if (!e._changed) { e.value = URLify( %s , %s );}}; ' % \
( f , field . name , ' + " " + ' . join ( [ ' document.getElementById( " id_ %s " ).value ' % g for g in field . prepopulate_from ] ) , field . maxlength ) )
t . append ( ' </script> \n ' )
if change and ordered_objects :
t . append ( ' { % i f form.order_objects % }<ul id= " orderthese " > { % f or object in form.order_objects % } ' )
t . append ( ' <li id= " p { %% firstof %(x)s %% } " ><span id= " handlep { %% firstof %(x)s %% } " > {{ object|truncatewords: " 5 " }}</span></li> ' % \
{ ' x ' : ' ' . join ( [ ' object. %s ' % o . pk . name for o in ordered_objects ] ) } )
t . append ( ' { % e ndfor % }</ul> { % e ndif % } \n ' )
t . append ( ' </form> \n </div> \n { % e ndblock % } ' )
return ' ' . join ( t )
def _get_admin_field ( field_list , name_prefix , rel , add , change ) :
" Returns the template code for editing the given list of fields in the admin template. "
field_names = [ ]
for f in field_list :
field_names . extend ( f . get_manipulator_field_names ( name_prefix ) )
div_class_names = [ ' form-row ' , ' { %% if %s %% } error { %% endif %% } ' % ' or ' . join ( [ ' %s .errors ' % n for n in field_names ] ) ]
# Assumes BooleanFields won't be stacked next to each other!
if isinstance ( field_list [ 0 ] , meta . BooleanField ) :
div_class_names . append ( ' checkbox-row ' )
t = [ ]
t . append ( ' <div class= " %s " > \n ' % ' ' . join ( div_class_names ) )
for n in field_names :
t . append ( ' { %% if %s .errors %% } {{ %s .html_error_list }} { %% endif %% } \n ' % ( n , n ) )
for i , field in enumerate ( field_list ) :
label_name = ' id_ %s %s ' % ( ( rel and " %s {{ forloop.counter0 }}. " % name_prefix or " " ) , field . get_manipulator_field_names ( ' ' ) [ 0 ] )
# BooleanFields are a special case, because the checkbox widget appears to
# the *left* of the label.
if isinstance ( field , meta . BooleanField ) :
t . append ( _get_admin_field_form_widget ( field , name_prefix , rel , add , change ) )
2005-08-02 05:29:52 +08:00
t . append ( ' <label for= " %s " class= " vCheckboxLabel " > %s </label> ' % ( label_name , capfirst ( field . verbose_name ) ) )
2005-07-13 09:25:57 +08:00
else :
class_names = [ ]
if not field . blank :
class_names . append ( ' required ' )
if i > 0 :
class_names . append ( ' inline ' )
2005-08-02 05:29:52 +08:00
t . append ( ' <label for= " %s " %s > %s :</label> ' % ( label_name , class_names and ' class= " %s " ' % ' ' . join ( class_names ) or ' ' , capfirst ( field . verbose_name ) ) )
2005-07-13 09:25:57 +08:00
t . append ( _get_admin_field_form_widget ( field , name_prefix , rel , add , change ) )
2005-08-11 03:32:38 +08:00
if change and field . primary_key :
t . append ( ' {{ %s original. %s }} ' % ( ( rel and name_prefix or ' ' ) , field . name ) )
2005-07-13 09:25:57 +08:00
if change and use_raw_id_admin ( field ) :
2005-08-17 02:08:37 +08:00
if isinstance ( field . rel , meta . ManyToOne ) :
2005-08-26 06:51:30 +08:00
if_bit = ' %s original.get_ %s ' % ( rel and name_prefix or ' ' , field . name )
2005-08-17 02:08:37 +08:00
obj_repr = if_bit + ' |truncatewords: " 14 " '
elif isinstance ( field . rel , meta . ManyToMany ) :
2005-08-26 06:51:30 +08:00
if_bit = ' %s original.get_ %s _list ' % ( rel and name_prefix or ' ' , field . name )
2005-08-17 02:08:37 +08:00
obj_repr = if_bit + ' |join: " , " |truncatewords: " 14 " '
t . append ( ' { %% if %s %% } <strong> {{ %s }}</strong> { %% endif %% } ' % ( if_bit , obj_repr ) )
2005-07-13 09:25:57 +08:00
if field . help_text :
t . append ( ' <p class= " help " > %s </p> \n ' % field . help_text )
t . append ( ' </div> \n \n ' )
return ' ' . join ( t )
def _get_admin_field_form_widget ( field , name_prefix , rel , add , change ) :
" Returns JUST the formfield widget for the field ' s admin interface. "
field_names = field . get_manipulator_field_names ( name_prefix )
if isinstance ( field , meta . DateTimeField ) :
return ' <p class= " datetime " >Date: {{ %s }}<br />Time: {{ %s }}</p> ' % tuple ( field_names )
t = [ ' {{ %s }} ' % n for n in field_names ]
if change and isinstance ( field , meta . FileField ) :
return ' { %% if %s original. %s %% }Currently: <a href= " {{ %s original.get_ %s _url }} " > {{ %s original. %s }}</a><br />Change: %s { %% else %% } %s { %% endif %% } ' % \
( name_prefix , field . name , name_prefix , field . name , name_prefix , field . name , ' ' . join ( t ) , ' ' . join ( t ) )
field_id = ' id_ %s %s ' % ( ( rel and " %s {{ forloop.counter0 }}. " % name_prefix or " " ) , field . get_manipulator_field_names ( ' ' ) [ 0 ] )
# raw_id_admin fields get the little lookup link next to them
if use_raw_id_admin ( field ) :
t . append ( ' <a href= " ../../../ %s / %s / " class= " related-lookup " id= " lookup_ %s " onclick= " return showRelatedObjectLookupPopup(this); " > ' % \
( field . rel . to . app_label , field . rel . to . module_name , field_id ) )
2005-07-17 00:38:28 +08:00
t . append ( ' <img src= " %s img/admin/selector-search.gif " width= " 16 " height= " 16 " alt= " Lookup " /></a> ' % ADMIN_MEDIA_PREFIX )
2005-07-13 09:25:57 +08:00
# fields with relationships to editable objects get an "add another" link,
# but only if the field doesn't have raw_admin ('cause in that case they get
# the "add" button in the popup)
2005-09-29 11:19:14 +08:00
elif field . rel and ( isinstance ( field . rel , meta . ManyToOne ) or isinstance ( field . rel , meta . ManyToMany ) ) and field . rel . to . admin :
2005-07-13 09:25:57 +08:00
t . append ( ' { %% if perms. %s . %s %% } ' % ( field . rel . to . app_label , field . rel . to . get_add_permission ( ) ) )
t . append ( ' <a href= " ../../../ %s / %s /add/ " class= " add-another " id= " add_ %s " onclick= " return showAddAnotherPopup(this); " > ' % \
( field . rel . to . app_label , field . rel . to . module_name , field_id ) )
2005-07-17 00:38:28 +08:00
t . append ( ' <img src= " %s img/admin/icon_addlink.gif " width= " 10 " height= " 10 " alt= " Add Another " /></a> ' % ADMIN_MEDIA_PREFIX )
2005-07-13 09:25:57 +08:00
t . append ( ' { % e ndif % } ' )
return ' ' . join ( t )
def add_stage ( request , app_label , module_name , show_delete = False , form_url = ' ' , post_url = ' ../ ' , post_url_continue = ' ../ %s / ' , object_id_override = None ) :
mod , opts = _get_mod_opts ( app_label , module_name )
if not request . user . has_perm ( app_label + ' . ' + opts . get_add_permission ( ) ) :
raise PermissionDenied
manipulator = mod . AddManipulator ( )
if request . POST :
new_data = request . POST . copy ( )
if opts . has_field_type ( meta . FileField ) :
new_data . update ( request . FILES )
errors = manipulator . get_validation_errors ( new_data )
if not errors and not request . POST . has_key ( " _preview " ) :
2005-08-17 02:08:37 +08:00
for f in opts . many_to_many :
if f . rel . raw_id_admin :
new_data . setlist ( f . name , new_data [ f . name ] . split ( " , " ) )
2005-07-13 09:25:57 +08:00
manipulator . do_html2python ( new_data )
new_object = manipulator . save ( new_data )
2005-11-10 07:37:41 +08:00
pk_value = getattr ( new_object , opts . pk . attname )
2005-08-04 22:48:48 +08:00
log . log_action ( request . user . id , opts . get_content_type_id ( ) , pk_value , repr ( new_object ) , log . ADDITION )
2005-07-13 09:25:57 +08:00
msg = ' The %s " %s " was added successfully. ' % ( opts . verbose_name , new_object )
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if request . POST . has_key ( " _continue " ) :
request . user . add_message ( " %s You may edit it again below. " % msg )
if request . POST . has_key ( " _popup " ) :
post_url_continue + = " ?_popup=1 "
2005-08-04 22:48:48 +08:00
return HttpResponseRedirect ( post_url_continue % pk_value )
2005-07-13 09:25:57 +08:00
if request . POST . has_key ( " _popup " ) :
return HttpResponse ( ' <script type= " text/javascript " >opener.dismissAddAnotherPopup(window, %s , " %s " );</script> ' % \
2005-09-23 09:50:01 +08:00
( pk_value , repr ( new_object ) . replace ( ' " ' , ' \\ " ' ) ) )
2005-07-13 09:25:57 +08:00
elif request . POST . has_key ( " _addanother " ) :
request . user . add_message ( " %s You may add another %s below. " % ( msg , opts . verbose_name ) )
return HttpResponseRedirect ( request . path )
else :
request . user . add_message ( msg )
return HttpResponseRedirect ( post_url )
if request . POST . has_key ( " _preview " ) :
manipulator . do_html2python ( new_data )
else :
new_data = { }
# Add default data.
for f in opts . fields :
if f . has_default ( ) :
new_data . update ( _get_flattened_data ( f , f . get_default ( ) ) )
# In required many-to-one fields with only one available choice,
# select that one available choice. Note: We have to check that
# the length of choices is *2*, not 1, because SelectFields always
# have an initial "blank" value.
elif not f . blank and ( ( isinstance ( f . rel , meta . ManyToOne ) and not f . rel . raw_id_admin ) or f . choices ) and len ( manipulator [ f . name ] . choices ) == 2 :
new_data [ f . name ] = manipulator [ f . name ] . choices [ 1 ] [ 0 ]
# In required many-to-many fields with only one available choice,
# select that one available choice.
for f in opts . many_to_many :
2005-08-17 02:08:37 +08:00
if not f . blank and not f . rel . edit_inline and not f . rel . raw_id_admin and len ( manipulator [ f . name ] . choices ) == 1 :
2005-07-13 09:25:57 +08:00
new_data [ f . name ] = [ manipulator [ f . name ] . choices [ 0 ] [ 0 ] ]
# Add default data for related objects.
for rel_opts , rel_field in opts . get_inline_related_objects ( ) :
var_name = rel_opts . object_name . lower ( )
for i in range ( rel_field . rel . num_in_admin ) :
for f in rel_opts . fields + rel_opts . many_to_many :
if f . has_default ( ) :
for field_name in f . get_manipulator_field_names ( ' ' ) :
new_data [ ' %s . %d . %s ' % ( var_name , i , field_name ) ] = f . get_default ( )
# Override the defaults with request.GET, if it exists.
new_data . update ( request . GET )
errors = { }
# Populate the FormWrapper.
form = formfields . FormWrapper ( manipulator , new_data , errors )
for rel_opts , rel_field in opts . get_inline_related_objects ( ) :
var_name = rel_opts . object_name . lower ( )
wrapper = [ ]
for i in range ( rel_field . rel . num_in_admin ) :
collection = { }
for f in rel_opts . fields + rel_opts . many_to_many :
if f . editable and f != rel_field and not isinstance ( f , meta . AutoField ) :
for field_name in f . get_manipulator_field_names ( ' ' ) :
full_field_name = ' %s . %d . %s ' % ( var_name , i , field_name )
collection [ field_name ] = formfields . FormFieldWrapper ( manipulator [ full_field_name ] , new_data . get ( full_field_name , ' ' ) , errors . get ( full_field_name , [ ] ) )
wrapper . append ( formfields . FormFieldCollection ( collection ) )
setattr ( form , rel_opts . module_name , wrapper )
c = Context ( request , {
' title ' : ' Add %s ' % opts . verbose_name ,
" form " : form ,
" is_popup " : request . REQUEST . has_key ( " _popup " ) ,
} )
if object_id_override is not None :
c [ ' object_id ' ] = object_id_override
raw_template = _get_template ( opts , app_label , add = True , show_delete = show_delete , form_url = form_url )
# return HttpResponse(raw_template, mimetype='text/plain')
2005-10-18 13:18:50 +08:00
t = loader . get_template_from_string ( raw_template )
2005-09-23 09:50:01 +08:00
return HttpResponse ( t . render ( c ) )
2005-10-18 13:23:07 +08:00
add_stage = staff_member_required ( add_stage )
2005-07-13 09:25:57 +08:00
def change_stage ( request , app_label , module_name , object_id ) :
mod , opts = _get_mod_opts ( app_label , module_name )
if not request . user . has_perm ( app_label + ' . ' + opts . get_change_permission ( ) ) :
raise PermissionDenied
if request . POST and request . POST . has_key ( " _saveasnew " ) :
return add_stage ( request , app_label , module_name , form_url = ' ../add/ ' )
try :
manipulator = mod . ChangeManipulator ( object_id )
except ObjectDoesNotExist :
raise Http404
2005-08-17 02:08:37 +08:00
2005-07-13 09:25:57 +08:00
inline_related_objects = opts . get_inline_related_objects ( )
if request . POST :
new_data = request . POST . copy ( )
if opts . has_field_type ( meta . FileField ) :
new_data . update ( request . FILES )
2005-08-17 02:08:37 +08:00
2005-07-13 09:25:57 +08:00
errors = manipulator . get_validation_errors ( new_data )
if not errors and not request . POST . has_key ( " _preview " ) :
2005-08-17 02:08:37 +08:00
for f in opts . many_to_many :
if f . rel . raw_id_admin :
new_data . setlist ( f . name , new_data [ f . name ] . split ( " , " ) )
2005-07-13 09:25:57 +08:00
manipulator . do_html2python ( new_data )
new_object = manipulator . save ( new_data )
2005-11-10 07:37:41 +08:00
pk_value = getattr ( new_object , opts . pk . attname )
2005-07-13 09:25:57 +08:00
# Construct the change message.
change_message = [ ]
if manipulator . fields_added :
change_message . append ( ' Added %s . ' % get_text_list ( manipulator . fields_added , ' and ' ) )
if manipulator . fields_changed :
change_message . append ( ' Changed %s . ' % get_text_list ( manipulator . fields_changed , ' and ' ) )
if manipulator . fields_deleted :
change_message . append ( ' Deleted %s . ' % get_text_list ( manipulator . fields_deleted , ' and ' ) )
change_message = ' ' . join ( change_message )
if not change_message :
change_message = ' No fields changed. '
2005-08-04 22:48:48 +08:00
log . log_action ( request . user . id , opts . get_content_type_id ( ) , pk_value , repr ( new_object ) , log . CHANGE , change_message )
2005-07-13 09:25:57 +08:00
msg = ' The %s " %s " was changed successfully. ' % ( opts . verbose_name , new_object )
if request . POST . has_key ( " _continue " ) :
request . user . add_message ( " %s You may edit it again below. " % msg )
if request . REQUEST . has_key ( ' _popup ' ) :
return HttpResponseRedirect ( request . path + " ?_popup=1 " )
else :
return HttpResponseRedirect ( request . path )
elif request . POST . has_key ( " _saveasnew " ) :
request . user . add_message ( ' The %s " %s " was added successfully. You may edit it again below. ' % ( opts . verbose_name , new_object ) )
2005-08-04 22:48:48 +08:00
return HttpResponseRedirect ( " ../ %s / " % pk_value )
2005-07-13 09:25:57 +08:00
elif request . POST . has_key ( " _addanother " ) :
request . user . add_message ( " %s You may add another %s below. " % ( msg , opts . verbose_name ) )
return HttpResponseRedirect ( " ../add/ " )
else :
request . user . add_message ( msg )
return HttpResponseRedirect ( " ../ " )
if request . POST . has_key ( " _preview " ) :
manipulator . do_html2python ( new_data )
else :
# Populate new_data with a "flattened" version of the current data.
new_data = { }
obj = manipulator . original_object
for f in opts . fields :
2005-11-10 07:37:41 +08:00
new_data . update ( _get_flattened_data ( f , getattr ( obj , f . attname ) ) )
2005-07-13 09:25:57 +08:00
for f in opts . many_to_many :
2005-09-20 11:46:11 +08:00
get_list_func = getattr ( obj , ' get_ %s _list ' % f . rel . singular )
2005-08-17 02:08:37 +08:00
if f . rel . raw_id_admin :
2005-11-10 07:37:41 +08:00
new_data [ f . name ] = " , " . join ( [ str ( getattr ( i , f . rel . to . pk . attname ) ) for i in get_list_func ( ) ] )
2005-08-17 02:08:37 +08:00
elif not f . rel . edit_inline :
2005-11-10 07:37:41 +08:00
new_data [ f . name ] = [ getattr ( i , f . rel . to . pk . attname ) for i in get_list_func ( ) ]
2005-07-13 09:25:57 +08:00
for rel_obj , rel_field in inline_related_objects :
var_name = rel_obj . object_name . lower ( )
for i , rel_instance in enumerate ( getattr ( obj , ' get_ %s _list ' % opts . get_rel_object_method_name ( rel_obj , rel_field ) ) ( ) ) :
for f in rel_obj . fields :
if f . editable and f != rel_field :
2005-11-10 07:37:41 +08:00
for k , v in _get_flattened_data ( f , getattr ( rel_instance , f . attname ) ) . items ( ) :
2005-07-13 09:25:57 +08:00
new_data [ ' %s . %d . %s ' % ( var_name , i , k ) ] = v
for f in rel_obj . many_to_many :
2005-08-26 06:51:30 +08:00
new_data [ ' %s . %d . %s ' % ( var_name , i , f . column ) ] = [ j . id for j in getattr ( rel_instance , ' get_ %s _list ' % f . rel . singular ) ( ) ]
2005-07-13 09:25:57 +08:00
# If the object has ordered objects on its admin page, get the existing
# order and flatten it into a comma-separated list of IDs.
id_order_list = [ ]
for rel_obj in opts . get_ordered_objects ( ) :
id_order_list . extend ( getattr ( obj , ' get_ %s _order ' % rel_obj . object_name . lower ( ) ) ( ) )
if id_order_list :
new_data [ ' order_ ' ] = ' , ' . join ( map ( str , id_order_list ) )
errors = { }
# Populate the FormWrapper.
form = formfields . FormWrapper ( manipulator , new_data , errors )
form . original = manipulator . original_object
form . order_objects = [ ]
for rel_opts , rel_field in inline_related_objects :
var_name = rel_opts . object_name . lower ( )
wrapper = [ ]
orig_list = getattr ( manipulator . original_object , ' get_ %s _list ' % opts . get_rel_object_method_name ( rel_opts , rel_field ) ) ( )
count = len ( orig_list ) + rel_field . rel . num_extra_on_change
if rel_field . rel . min_num_in_admin :
count = max ( count , rel_field . rel . min_num_in_admin )
if rel_field . rel . max_num_in_admin :
count = min ( count , rel_field . rel . max_num_in_admin )
for i in range ( count ) :
collection = { ' original ' : ( i < len ( orig_list ) and orig_list [ i ] or None ) }
for f in rel_opts . fields + rel_opts . many_to_many :
if f . editable and f != rel_field :
for field_name in f . get_manipulator_field_names ( ' ' ) :
full_field_name = ' %s . %d . %s ' % ( var_name , i , field_name )
2005-08-07 04:51:06 +08:00
collection [ field_name ] = formfields . FormFieldWrapper ( manipulator [ full_field_name ] , new_data . get ( full_field_name , f . get_default ( ) ) , errors . get ( full_field_name , [ ] ) )
2005-07-13 09:25:57 +08:00
wrapper . append ( formfields . FormFieldCollection ( collection ) )
setattr ( form , rel_opts . module_name , wrapper )
if rel_opts . order_with_respect_to and rel_opts . order_with_respect_to . rel and rel_opts . order_with_respect_to . rel . to == opts :
form . order_objects . extend ( orig_list )
c = Context ( request , {
' title ' : ' Change %s ' % opts . verbose_name ,
" form " : form ,
' object_id ' : object_id ,
' original ' : manipulator . original_object ,
' is_popup ' : request . REQUEST . has_key ( ' _popup ' ) ,
} )
raw_template = _get_template ( opts , app_label , change = True )
# return HttpResponse(raw_template, mimetype='text/plain')
2005-10-18 13:18:50 +08:00
t = loader . get_template_from_string ( raw_template )
2005-09-23 09:50:01 +08:00
return HttpResponse ( t . render ( c ) )
2005-10-18 13:23:07 +08:00
change_stage = staff_member_required ( change_stage )
2005-07-13 09:25:57 +08:00
def _nest_help ( obj , depth , val ) :
current = obj
for i in range ( depth ) :
current = current [ - 1 ]
current . append ( val )
def _get_deleted_objects ( deleted_objects , perms_needed , user , obj , opts , current_depth ) :
" Helper function that recursively populates deleted_objects. "
nh = _nest_help # Bind to local variable for performance
if current_depth > 16 :
return # Avoid recursing too deep.
objects_seen = [ ]
for rel_opts , rel_field in opts . get_all_related_objects ( ) :
if rel_opts in objects_seen :
continue
objects_seen . append ( rel_opts )
rel_opts_name = opts . get_rel_object_method_name ( rel_opts , rel_field )
if isinstance ( rel_field . rel , meta . OneToOne ) :
try :
sub_obj = getattr ( obj , ' get_ %s ' % rel_opts_name ) ( )
except ObjectDoesNotExist :
pass
else :
if rel_opts . admin :
p = ' %s . %s ' % ( rel_opts . app_label , rel_opts . get_delete_permission ( ) )
if not user . has_perm ( p ) :
perms_needed . add ( rel_opts . verbose_name )
# We don't care about populating deleted_objects now.
continue
if rel_field . rel . edit_inline or not rel_opts . admin :
# Don't display link to edit, because it either has no
# admin or is edited inline.
2005-08-02 05:29:52 +08:00
nh ( deleted_objects , current_depth , [ ' %s : %r ' % ( capfirst ( rel_opts . verbose_name ) , sub_obj ) , [ ] ] )
2005-07-13 09:25:57 +08:00
else :
# Display a link to the admin page.
nh ( deleted_objects , current_depth , [ ' %s : <a href= " ../../../../ %s / %s / %s / " > %r </a> ' % \
2005-08-02 05:29:52 +08:00
( capfirst ( rel_opts . verbose_name ) , rel_opts . app_label , rel_opts . module_name ,
2005-11-10 07:37:41 +08:00
getattr ( sub_obj , rel_opts . pk . attname ) , sub_obj ) , [ ] ] )
2005-07-13 09:25:57 +08:00
_get_deleted_objects ( deleted_objects , perms_needed , user , sub_obj , rel_opts , current_depth + 2 )
else :
has_related_objs = False
for sub_obj in getattr ( obj , ' get_ %s _list ' % rel_opts_name ) ( ) :
has_related_objs = True
if rel_field . rel . edit_inline or not rel_opts . admin :
# Don't display link to edit, because it either has no
# admin or is edited inline.
2005-08-02 05:29:52 +08:00
nh ( deleted_objects , current_depth , [ ' %s : %s ' % ( capfirst ( rel_opts . verbose_name ) , strip_tags ( repr ( sub_obj ) ) ) , [ ] ] )
2005-07-13 09:25:57 +08:00
else :
# Display a link to the admin page.
nh ( deleted_objects , current_depth , [ ' %s : <a href= " ../../../../ %s / %s / %s / " > %s </a> ' % \
2005-08-02 05:29:52 +08:00
( capfirst ( rel_opts . verbose_name ) , rel_opts . app_label , rel_opts . module_name , sub_obj . id , strip_tags ( repr ( sub_obj ) ) ) , [ ] ] )
2005-07-13 09:25:57 +08:00
_get_deleted_objects ( deleted_objects , perms_needed , user , sub_obj , rel_opts , current_depth + 2 )
# If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed.
if rel_opts . admin and has_related_objs :
p = ' %s . %s ' % ( rel_opts . app_label , rel_opts . get_delete_permission ( ) )
if not user . has_perm ( p ) :
perms_needed . add ( rel_opts . verbose_name )
for rel_opts , rel_field in opts . get_all_related_many_to_many_objects ( ) :
if rel_opts in objects_seen :
continue
objects_seen . append ( rel_opts )
rel_opts_name = opts . get_rel_object_method_name ( rel_opts , rel_field )
has_related_objs = False
for sub_obj in getattr ( obj , ' get_ %s _list ' % rel_opts_name ) ( ) :
has_related_objs = True
if rel_field . rel . edit_inline or not rel_opts . admin :
# Don't display link to edit, because it either has no
# admin or is edited inline.
nh ( deleted_objects , current_depth , [ ' One or more %s in %s : %s ' % \
( rel_field . name , rel_opts . verbose_name , strip_tags ( repr ( sub_obj ) ) ) , [ ] ] )
else :
# Display a link to the admin page.
nh ( deleted_objects , current_depth , [ ' One or more %s in %s : <a href= " ../../../../ %s / %s / %s / " > %s </a> ' % \
( rel_field . name , rel_opts . verbose_name , rel_opts . app_label , rel_opts . module_name , sub_obj . id , strip_tags ( repr ( sub_obj ) ) ) , [ ] ] )
# If there were related objects, and the user doesn't have
# permission to change them, add the missing perm to perms_needed.
if rel_opts . admin and has_related_objs :
p = ' %s . %s ' % ( rel_opts . app_label , rel_opts . get_change_permission ( ) )
if not user . has_perm ( p ) :
perms_needed . add ( rel_opts . verbose_name )
def delete_stage ( request , app_label , module_name , object_id ) :
import sets
mod , opts = _get_mod_opts ( app_label , module_name )
if not request . user . has_perm ( app_label + ' . ' + opts . get_delete_permission ( ) ) :
raise PermissionDenied
2005-09-24 05:52:01 +08:00
obj = get_object_or_404 ( mod , pk = object_id )
2005-07-13 09:25:57 +08:00
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
2005-08-02 05:29:52 +08:00
deleted_objects = [ ' %s : <a href= " ../../ %s / " > %s </a> ' % ( capfirst ( opts . verbose_name ) , object_id , strip_tags ( repr ( obj ) ) ) , [ ] ]
2005-07-13 09:25:57 +08:00
perms_needed = sets . Set ( )
_get_deleted_objects ( deleted_objects , perms_needed , request . user , obj , opts , 1 )
if request . POST : # The user has already confirmed the deletion.
if perms_needed :
raise PermissionDenied
obj_repr = repr ( obj )
2005-07-27 06:43:27 +08:00
obj . delete ( )
2005-07-13 09:25:57 +08:00
log . log_action ( request . user . id , opts . get_content_type_id ( ) , object_id , obj_repr , log . DELETION )
request . user . add_message ( ' The %s " %s " was deleted successfully. ' % ( opts . verbose_name , obj_repr ) )
return HttpResponseRedirect ( " ../../ " )
2005-10-19 09:09:05 +08:00
return render_to_response ( ' admin/delete_confirmation ' , {
2005-07-13 09:25:57 +08:00
" title " : " Are you sure? " ,
" object_name " : opts . verbose_name ,
" object " : obj ,
" deleted_objects " : deleted_objects ,
" perms_lacking " : perms_needed ,
2005-09-24 05:52:01 +08:00
} , context_instance = Context ( request ) )
2005-10-18 13:23:07 +08:00
delete_stage = staff_member_required ( delete_stage )
2005-07-13 09:25:57 +08:00
def history ( request , app_label , module_name , object_id ) :
mod , opts = _get_mod_opts ( app_label , module_name )
2005-08-26 06:51:30 +08:00
action_list = log . get_list ( object_id__exact = object_id , content_type__id__exact = opts . get_content_type_id ( ) ,
2005-07-22 21:02:27 +08:00
order_by = ( " action_time " , ) , select_related = True )
2005-07-13 09:25:57 +08:00
# If no history was found, see whether this object even exists.
2005-09-24 05:52:01 +08:00
obj = get_object_or_404 ( mod , pk = object_id )
2005-10-19 09:09:05 +08:00
return render_to_response ( ' admin/object_history ' , {
2005-07-13 09:25:57 +08:00
' title ' : ' Change history: %r ' % obj ,
' action_list ' : action_list ,
2005-08-02 05:29:52 +08:00
' module_name ' : capfirst ( opts . verbose_name_plural ) ,
2005-07-13 09:25:57 +08:00
' object ' : obj ,
2005-09-24 05:52:01 +08:00
} , context_instance = Context ( request ) )
2005-10-18 13:23:07 +08:00
history = staff_member_required ( history )