2006-05-02 09:31:56 +08:00
from django import forms , template
from django . conf import settings
2005-11-26 05:20:09 +08:00
from django . contrib . admin . filterspecs import FilterSpec
2006-05-02 09:31:56 +08:00
from django . contrib . admin . views . decorators import staff_member_required
from django . views . decorators . cache import never_cache
from django . contrib . contenttypes . models import ContentType
from django . core . exceptions import ImproperlyConfigured , ObjectDoesNotExist , PermissionDenied
2005-11-26 05:20:09 +08:00
from django . core . paginator import ObjectPaginator , InvalidPage
2006-05-02 09:31:56 +08:00
from django . shortcuts import get_object_or_404 , render_to_response
from django . db import models
from django . db . models . query import handle_legacy_orderlist , QuerySet
from django . http import Http404 , HttpResponse , HttpResponseRedirect
2005-11-26 05:20:09 +08:00
from django . utils . html import escape
2006-05-02 09:31:56 +08:00
from django . utils . text import capfirst , get_text_list
2005-07-13 09:25:57 +08:00
import operator
2006-05-02 09:31:56 +08:00
from django . contrib . admin . models import LogEntry , ADDITION , CHANGE , DELETION
if not LogEntry . _meta . installed :
raise ImproperlyConfigured , " You ' ll need to put ' django.contrib.admin ' in your INSTALLED_APPS setting before you can use the admin application. "
2005-11-26 05:20:09 +08:00
2006-05-02 09:31:56 +08:00
if ' django.core.context_processors.auth ' not in settings . TEMPLATE_CONTEXT_PROCESSORS :
raise ImproperlyConfigured , " You ' ll need to put ' django.core.context_processors.auth ' in your TEMPLATE_CONTEXT_PROCESSORS setting before you can use the admin application. "
# The system will display a "Show all" link on the change list only if the
# total result count is less than or equal to this setting.
MAX_SHOW_ALL_ALLOWED = 200
2005-11-26 05:20:09 +08:00
2006-05-02 09:31:56 +08:00
# Changelist settings
2005-11-26 05:20:09 +08:00
ALL_VAR = ' all '
ORDER_VAR = ' o '
ORDER_TYPE_VAR = ' ot '
PAGE_VAR = ' p '
SEARCH_VAR = ' q '
IS_POPUP_VAR = ' pop '
2006-05-31 23:25:23 +08:00
ERROR_FLAG = ' e '
2005-11-26 05:20:09 +08:00
2006-05-02 09:31:56 +08:00
# Text to display within change-list table cells if the value is blank.
2005-07-13 09:25:57 +08:00
EMPTY_CHANGELIST_VALUE = ' (None) '
2006-05-02 09:31:56 +08:00
use_raw_id_admin = lambda field : isinstance ( field . rel , ( models . ManyToOneRel , models . ManyToManyRel ) ) and field . rel . raw_id_admin
2005-07-13 09:25:57 +08:00
2005-11-26 05:20:09 +08:00
class IncorrectLookupParameters ( Exception ) :
pass
2006-05-02 09:31:56 +08:00
def quote ( s ) :
"""
Ensure that primary key values do not confuse the admin URLs by escaping
any ' / ' , ' _ ' and ' : ' characters . Similar to urllib . quote , except that the
quoting is slightly different so that it doesn ' t get autoamtically
unquoted by the web browser .
"""
if type ( s ) != type ( ' ' ) :
return s
res = list ( s )
for i in range ( len ( res ) ) :
c = res [ i ]
if c in ' :/_ ' :
res [ i ] = ' _ %02X ' % ord ( c )
return ' ' . join ( res )
def unquote ( s ) :
"""
Undo the effects of quote ( ) . Based heavily on urllib . unquote ( ) .
"""
mychr = chr
myatoi = int
list = s . split ( ' _ ' )
res = [ list [ 0 ] ]
myappend = res . append
del list [ 0 ]
for item in list :
if item [ 1 : 2 ] :
2005-11-26 05:20:09 +08:00
try :
2006-06-01 11:35:23 +08:00
myappend ( mychr ( myatoi ( item [ : 2 ] , 16 ) ) + item [ 2 : ] )
2006-05-02 09:31:56 +08:00
except ValueError :
myappend ( ' _ ' + item )
2005-11-26 05:20:09 +08:00
else :
2006-05-02 09:31:56 +08:00
myappend ( ' _ ' + item )
return " " . join ( res )
2005-07-13 09:25:57 +08:00
2006-05-02 09:31:56 +08:00
def get_javascript_imports ( opts , auto_populated_fields , field_sets ) :
2005-11-26 05:20:09 +08:00
# Put in any necessary JavaScript imports.
js = [ ' js/core.js ' , ' js/admin/RelatedObjectLookups.js ' ]
2005-07-13 09:25:57 +08:00
if auto_populated_fields :
2005-11-26 05:20:09 +08:00
js . append ( ' js/urlify.js ' )
2006-05-02 09:31:56 +08:00
if opts . has_field_type ( models . DateTimeField ) or opts . has_field_type ( models . TimeField ) or opts . has_field_type ( models . DateField ) :
2005-11-26 05:20:09 +08:00
js . extend ( [ ' js/calendar.js ' , ' js/admin/DateTimeShortcuts.js ' ] )
2006-05-02 09:31:56 +08:00
if opts . get_ordered_objects ( ) :
2005-11-26 05:20:09 +08:00
js . extend ( [ ' js/getElementsBySelector.js ' , ' js/dom-drag.js ' , ' js/admin/ordering.js ' ] )
2005-07-13 09:25:57 +08:00
if opts . admin . js :
2005-11-26 05:20:09 +08:00
js . extend ( opts . admin . js )
2005-07-21 11:19:30 +08:00
seen_collapse = False
2005-11-26 05:20:09 +08:00
for field_set in field_sets :
if not seen_collapse and ' collapse ' in field_set . classes :
2005-07-21 11:19:30 +08:00
seen_collapse = True
2006-05-02 09:31:56 +08:00
js . append ( ' js/admin/CollapsedFieldsets.js ' )
2005-11-26 05:20:09 +08:00
for field_line in field_set :
2005-10-25 09:47:34 +08:00
try :
2005-11-26 05:20:09 +08:00
for f in field_line :
2006-05-02 09:31:56 +08:00
if f . rel and isinstance ( f , models . ManyToManyField ) and f . rel . filter_interface :
2005-11-26 05:20:09 +08:00
js . extend ( [ ' js/SelectBox.js ' , ' js/SelectFilter2.js ' ] )
2005-07-13 09:25:57 +08:00
raise StopIteration
2005-10-25 09:47:34 +08:00
except StopIteration :
break
2005-11-26 05:20:09 +08:00
return js
2006-05-02 09:31:56 +08:00
class AdminBoundField ( object ) :
2005-11-26 05:20:09 +08:00
def __init__ ( self , field , field_mapping , original ) :
2006-05-02 09:31:56 +08:00
self . field = field
self . original = original
self . form_fields = [ field_mapping [ name ] for name in self . field . get_manipulator_field_names ( ' ' ) ]
2005-11-26 05:20:09 +08:00
self . element_id = self . form_fields [ 0 ] . get_id ( )
2006-05-02 09:31:56 +08:00
self . has_label_first = not isinstance ( self . field , models . BooleanField )
2005-11-26 05:20:09 +08:00
self . raw_id_admin = use_raw_id_admin ( field )
2006-05-02 09:31:56 +08:00
self . is_date_time = isinstance ( field , models . DateTimeField )
self . is_file_field = isinstance ( field , models . FileField )
2006-05-27 01:54:19 +08:00
self . needs_add_label = field . rel and ( isinstance ( field . rel , models . ManyToOneRel ) or isinstance ( field . rel , models . ManyToManyRel ) ) and field . rel . to . _meta . admin
2006-05-02 09:31:56 +08:00
self . hidden = isinstance ( self . field , models . AutoField )
2005-11-26 05:20:09 +08:00
self . first = False
classes = [ ]
2005-11-29 13:54:56 +08:00
if self . raw_id_admin :
2005-11-26 05:20:09 +08:00
classes . append ( ' nowrap ' )
if max ( [ bool ( f . errors ( ) ) for f in self . form_fields ] ) :
classes . append ( ' error ' )
if classes :
self . cell_class_attribute = ' class= " %s " ' % ' ' . join ( classes )
self . _repr_filled = False
2006-05-02 09:31:56 +08:00
if field . rel :
self . related_url = ' ../../../ %s / %s / ' % ( field . rel . to . _meta . app_label , field . rel . to . _meta . object_name . lower ( ) )
def original_value ( self ) :
if self . original :
return self . original . __dict__ [ self . field . column ]
2005-11-26 05:20:09 +08:00
def existing_display ( self ) :
2006-05-02 09:31:56 +08:00
try :
return self . _display
except AttributeError :
if isinstance ( self . field . rel , models . ManyToOneRel ) :
2006-05-31 22:37:57 +08:00
self . _display = getattr ( self . original , self . field . name )
2006-05-02 09:31:56 +08:00
elif isinstance ( self . field . rel , models . ManyToManyRel ) :
2006-05-31 22:37:57 +08:00
self . _display = " , " . join ( [ str ( obj ) for obj in getattr ( self . original , self . field . name ) . all ( ) ] )
2006-05-02 09:31:56 +08:00
return self . _display
2005-11-26 05:20:09 +08:00
def __repr__ ( self ) :
return repr ( self . __dict__ )
def html_error_list ( self ) :
return " " . join ( [ form_field . html_error_list ( ) for form_field in self . form_fields if form_field . errors ] )
2006-05-02 09:31:56 +08:00
def original_url ( self ) :
if self . is_file_field and self . original and self . field . attname :
url_method = getattr ( self . original , ' get_ %s _url ' % self . field . attname )
if callable ( url_method ) :
return url_method ( )
return ' '
class AdminBoundFieldLine ( object ) :
2005-11-26 05:20:09 +08:00
def __init__ ( self , field_line , field_mapping , original ) :
2006-05-02 09:31:56 +08:00
self . bound_fields = [ field . bind ( field_mapping , original , AdminBoundField ) for field in field_line ]
2005-11-26 05:20:09 +08:00
for bound_field in self :
bound_field . first = True
break
2006-05-02 09:31:56 +08:00
def __iter__ ( self ) :
for bound_field in self . bound_fields :
yield bound_field
def __len__ ( self ) :
return len ( self . bound_fields )
class AdminBoundFieldSet ( object ) :
2005-11-26 05:20:09 +08:00
def __init__ ( self , field_set , field_mapping , original ) :
2006-05-02 09:31:56 +08:00
self . name = field_set . name
self . classes = field_set . classes
self . description = field_set . description
self . bound_field_lines = [ field_line . bind ( field_mapping , original , AdminBoundFieldLine ) for field_line in field_set ]
def __iter__ ( self ) :
for bound_field_line in self . bound_field_lines :
yield bound_field_line
def __len__ ( self ) :
return len ( self . bound_field_lines )
def render_change_form ( model , manipulator , context , add = False , change = False , form_url = ' ' ) :
opts = model . _meta
app_label = opts . app_label
auto_populated_fields = [ f for f in opts . fields if f . prepopulate_from ]
field_sets = opts . admin . get_field_sets ( opts )
original = getattr ( manipulator , ' original_object ' , None )
bound_field_sets = [ field_set . bind ( context [ ' form ' ] , original , AdminBoundFieldSet ) for field_set in field_sets ]
first_form_field_id = bound_field_sets [ 0 ] . bound_field_lines [ 0 ] . bound_fields [ 0 ] . form_fields [ 0 ] . get_id ( ) ;
ordered_objects = opts . get_ordered_objects ( )
inline_related_objects = opts . get_followed_related_objects ( manipulator . follow )
2005-11-26 05:20:09 +08:00
extra_context = {
' add ' : add ,
' change ' : change ,
2005-12-09 10:16:17 +08:00
' has_delete_permission ' : context [ ' perms ' ] [ app_label ] [ opts . get_delete_permission ( ) ] ,
2006-05-02 09:31:56 +08:00
' has_change_permission ' : context [ ' perms ' ] [ app_label ] [ opts . get_change_permission ( ) ] ,
' has_file_field ' : opts . has_field_type ( models . FileField ) ,
' has_absolute_url ' : hasattr ( model , ' get_absolute_url ' ) ,
' auto_populated_fields ' : auto_populated_fields ,
' bound_field_sets ' : bound_field_sets ,
' first_form_field_id ' : first_form_field_id ,
' javascript_imports ' : get_javascript_imports ( opts , auto_populated_fields , field_sets ) ,
' ordered_objects ' : ordered_objects ,
' inline_related_objects ' : inline_related_objects ,
2005-12-09 10:16:17 +08:00
' form_url ' : form_url ,
2006-05-02 09:31:56 +08:00
' opts ' : opts ,
' content_type_id ' : ContentType . objects . get_for_model ( model ) . id ,
2005-11-26 05:20:09 +08:00
}
context . update ( extra_context )
2006-05-02 09:31:56 +08:00
return render_to_response ( [
" admin/ %s / %s /change_form.html " % ( app_label , opts . object_name . lower ( ) ) ,
" admin/ %s /change_form.html " % app_label ,
" admin/change_form.html " ] , context_instance = context )
2005-11-26 05:20:09 +08:00
2006-05-02 09:31:56 +08:00
def index ( request ) :
return render_to_response ( ' admin/index.html ' , { ' title ' : _ ( ' Site administration ' ) } , context_instance = template . RequestContext ( request ) )
index = staff_member_required ( never_cache ( index ) )
def add_stage ( request , app_label , model_name , show_delete = False , form_url = ' ' , post_url = None , post_url_continue = ' ../ %s / ' , object_id_override = None ) :
model = models . get_model ( app_label , model_name )
if model is None :
raise Http404 , " App %r , model %r , not found " % ( app_label , model_name )
opts = model . _meta
2005-07-13 09:25:57 +08:00
if not request . user . has_perm ( app_label + ' . ' + opts . get_add_permission ( ) ) :
raise PermissionDenied
2006-05-02 09:31:56 +08:00
if post_url is None :
if request . user . has_perm ( app_label + ' . ' + opts . get_change_permission ( ) ) :
# redirect to list view
post_url = ' ../ '
else :
# Object list will give 'Permission Denied', so go back to admin home
post_url = ' ../../../ '
manipulator = model . AddManipulator ( )
2005-07-13 09:25:57 +08:00
if request . POST :
new_data = request . POST . copy ( )
2006-05-02 09:31:56 +08:00
if opts . has_field_type ( models . FileField ) :
2005-07-13 09:25:57 +08:00
new_data . update ( request . FILES )
2006-05-02 09:31:56 +08:00
2005-07-13 09:25:57 +08:00
errors = manipulator . get_validation_errors ( new_data )
2005-11-26 05:20:09 +08:00
manipulator . do_html2python ( new_data )
2006-05-02 09:31:56 +08:00
if not errors :
2005-07-13 09:25:57 +08:00
new_object = manipulator . save ( new_data )
2006-05-02 09:31:56 +08:00
pk_value = new_object . _get_pk_val ( )
LogEntry . objects . log_action ( request . user . id , ContentType . objects . get_for_model ( model ) . id , pk_value , str ( new_object ) , ADDITION )
msg = _ ( ' The %(name)s " %(obj)s " was added successfully. ' ) % { ' name ' : opts . verbose_name , ' obj ' : new_object }
2005-07-13 09:25:57 +08:00
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if request . POST . has_key ( " _continue " ) :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = msg + ' ' + _ ( " You may edit it again below. " ) )
2005-07-13 09:25:57 +08:00
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 " ) :
2006-08-19 05:39:29 +08:00
if type ( pk_value ) is str : # Quote if string, so JavaScript doesn't think it's a variable.
pk_value = ' " %s " ' % pk_value . replace ( ' " ' , ' \\ " ' )
return HttpResponse ( ' <script type= " text/javascript " >opener.dismissAddAnotherPopup(window, %s , " %s " );</script> ' % \
2006-02-28 03:13:41 +08:00
( pk_value , str ( new_object ) . replace ( ' " ' , ' \\ " ' ) ) )
2005-07-13 09:25:57 +08:00
elif request . POST . has_key ( " _addanother " ) :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = msg + ' ' + ( _ ( " You may add another %s below. " ) % opts . verbose_name ) )
2005-07-13 09:25:57 +08:00
return HttpResponseRedirect ( request . path )
else :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = msg )
2005-07-13 09:25:57 +08:00
return HttpResponseRedirect ( post_url )
else :
# Add default data.
2005-11-26 05:20:09 +08:00
new_data = manipulator . flatten_data ( )
2005-12-21 11:37:31 +08:00
# Override the defaults with GET params, if they exist.
new_data . update ( dict ( request . GET . items ( ) ) )
2005-07-13 09:25:57 +08:00
errors = { }
# Populate the FormWrapper.
2006-05-02 09:31:56 +08:00
form = forms . FormWrapper ( manipulator , new_data , errors )
2005-07-13 09:25:57 +08:00
2006-05-02 09:31:56 +08:00
c = template . RequestContext ( request , {
2005-11-26 05:20:09 +08:00
' title ' : _ ( ' Add %s ' ) % opts . verbose_name ,
' form ' : form ,
' is_popup ' : request . REQUEST . has_key ( ' _popup ' ) ,
' show_delete ' : show_delete ,
2005-07-13 09:25:57 +08:00
} )
2006-05-02 09:31:56 +08:00
2005-07-13 09:25:57 +08:00
if object_id_override is not None :
c [ ' object_id ' ] = object_id_override
2005-11-26 05:20:09 +08:00
2006-05-02 09:31:56 +08:00
return render_change_form ( model , manipulator , c , add = True )
add_stage = staff_member_required ( never_cache ( add_stage ) )
def change_stage ( request , app_label , model_name , object_id ) :
model = models . get_model ( app_label , model_name )
object_id = unquote ( object_id )
if model is None :
raise Http404 , " App %r , model %r , not found " % ( app_label , model_name )
opts = model . _meta
2005-07-13 09:25:57 +08:00
if not request . user . has_perm ( app_label + ' . ' + opts . get_change_permission ( ) ) :
raise PermissionDenied
2006-05-02 09:31:56 +08:00
2005-07-13 09:25:57 +08:00
if request . POST and request . POST . has_key ( " _saveasnew " ) :
2006-05-02 09:31:56 +08:00
return add_stage ( request , app_label , model_name , form_url = ' ../../add/ ' )
2005-07-13 09:25:57 +08:00
try :
2006-05-02 09:31:56 +08:00
manipulator = model . ChangeManipulator ( object_id )
2005-07-13 09:25:57 +08:00
except ObjectDoesNotExist :
raise Http404
2005-08-17 02:08:37 +08:00
2005-07-13 09:25:57 +08:00
if request . POST :
new_data = request . POST . copy ( )
2006-05-02 09:31:56 +08:00
if opts . has_field_type ( models . FileField ) :
2005-07-13 09:25:57 +08:00
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 )
2005-11-26 05:20:09 +08:00
manipulator . do_html2python ( new_data )
2006-05-02 09:31:56 +08:00
if not errors :
2005-07-13 09:25:57 +08:00
new_object = manipulator . save ( new_data )
2006-05-02 09:31:56 +08:00
pk_value = new_object . _get_pk_val ( )
# 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. ' )
LogEntry . objects . log_action ( request . user . id , ContentType . objects . get_for_model ( model ) . id , pk_value , str ( new_object ) , CHANGE , change_message )
msg = _ ( ' The %(name)s " %(obj)s " was changed successfully. ' ) % { ' name ' : opts . verbose_name , ' obj ' : new_object }
2005-07-13 09:25:57 +08:00
if request . POST . has_key ( " _continue " ) :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = msg + ' ' + _ ( " You may edit it again below. " ) )
2005-07-13 09:25:57 +08:00
if request . REQUEST . has_key ( ' _popup ' ) :
return HttpResponseRedirect ( request . path + " ?_popup=1 " )
else :
return HttpResponseRedirect ( request . path )
elif request . POST . has_key ( " _saveasnew " ) :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = _ ( ' The %(name)s " %(obj)s " was added successfully. You may edit it again below. ' ) % { ' name ' : opts . verbose_name , ' obj ' : 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 " ) :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = msg + ' ' + ( _ ( " You may add another %s below. " ) % opts . verbose_name ) )
2005-07-13 09:25:57 +08:00
return HttpResponseRedirect ( " ../add/ " )
else :
2006-05-02 09:31:56 +08:00
request . user . message_set . create ( message = msg )
2005-07-13 09:25:57 +08:00
return HttpResponseRedirect ( " ../ " )
else :
# Populate new_data with a "flattened" version of the current data.
2005-11-26 05:20:09 +08:00
new_data = manipulator . flatten_data ( )
2005-07-13 09:25:57 +08:00
2005-11-26 05:20:09 +08:00
# TODO: do this in flatten_data...
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.
2005-11-26 05:20:09 +08:00
2005-07-13 09:25:57 +08:00
id_order_list = [ ]
for rel_obj in opts . get_ordered_objects ( ) :
2005-11-26 05:20:09 +08:00
id_order_list . extend ( getattr ( manipulator . original_object , ' get_ %s _order ' % rel_obj . object_name . lower ( ) ) ( ) )
2005-07-13 09:25:57 +08:00
if id_order_list :
new_data [ ' order_ ' ] = ' , ' . join ( map ( str , id_order_list ) )
errors = { }
# Populate the FormWrapper.
2006-05-02 09:31:56 +08:00
form = forms . FormWrapper ( manipulator , new_data , errors )
2005-07-13 09:25:57 +08:00
form . original = manipulator . original_object
form . order_objects = [ ]
2005-11-26 05:20:09 +08:00
#TODO Should be done in flatten_data / FormWrapper construction
for related in opts . get_followed_related_objects ( ) :
wrt = related . opts . order_with_respect_to
if wrt and wrt . rel and wrt . rel . to == opts :
func = getattr ( manipulator . original_object , ' get_ %s _list ' %
2006-05-02 09:31:56 +08:00
related . get_accessor_name ( ) )
2005-11-26 05:20:09 +08:00
orig_list = func ( )
2005-07-13 09:25:57 +08:00
form . order_objects . extend ( orig_list )
2006-05-02 09:31:56 +08:00
c = template . RequestContext ( request , {
2005-11-26 05:20:09 +08:00
' title ' : _ ( ' Change %s ' ) % opts . verbose_name ,
' form ' : form ,
2005-07-13 09:25:57 +08:00
' object_id ' : object_id ,
' original ' : manipulator . original_object ,
2006-05-02 09:31:56 +08:00
' is_popup ' : request . REQUEST . has_key ( ' _popup ' ) ,
2005-07-13 09:25:57 +08:00
} )
2006-05-02 09:31:56 +08:00
return render_change_form ( model , manipulator , c , change = True )
change_stage = staff_member_required ( never_cache ( 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.
2005-12-10 13:32:05 +08:00
opts_seen = [ ]
2005-11-26 05:20:09 +08:00
for related in opts . get_all_related_objects ( ) :
2005-12-10 13:32:05 +08:00
if related . opts in opts_seen :
2005-07-13 09:25:57 +08:00
continue
2005-12-10 13:32:05 +08:00
opts_seen . append ( related . opts )
2006-05-02 09:31:56 +08:00
rel_opts_name = related . get_accessor_name ( )
if isinstance ( related . field . rel , models . OneToOneRel ) :
2005-07-13 09:25:57 +08:00
try :
2006-05-02 09:31:56 +08:00
sub_obj = getattr ( obj , rel_opts_name )
2005-07-13 09:25:57 +08:00
except ObjectDoesNotExist :
pass
else :
2005-12-08 07:36:31 +08:00
if related . opts . admin :
2005-11-26 05:20:09 +08:00
p = ' %s . %s ' % ( related . opts . app_label , related . opts . get_delete_permission ( ) )
2005-07-13 09:25:57 +08:00
if not user . has_perm ( p ) :
2005-11-26 05:20:09 +08:00
perms_needed . add ( related . opts . verbose_name )
2005-07-13 09:25:57 +08:00
# We don't care about populating deleted_objects now.
continue
2005-11-26 05:20:09 +08:00
if related . field . rel . edit_inline or not related . opts . admin :
2005-07-13 09:25:57 +08:00
# Don't display link to edit, because it either has no
# admin or is edited inline.
2005-11-26 05:20:09 +08:00
nh ( deleted_objects , current_depth , [ ' %s : %s ' % ( capfirst ( related . opts . verbose_name ) , sub_obj ) , [ ] ] )
2005-07-13 09:25:57 +08:00
else :
# Display a link to the admin page.
2005-11-26 05:20:09 +08:00
nh ( deleted_objects , current_depth , [ ' %s : <a href= " ../../../../ %s / %s / %s / " > %s </a> ' % \
2006-05-02 09:31:56 +08:00
( capfirst ( related . opts . verbose_name ) , related . opts . app_label , related . opts . object_name . lower ( ) ,
sub_obj . _get_pk_val ( ) , sub_obj ) , [ ] ] )
2005-11-26 05:20:09 +08:00
_get_deleted_objects ( deleted_objects , perms_needed , user , sub_obj , related . opts , current_depth + 2 )
2005-07-13 09:25:57 +08:00
else :
has_related_objs = False
2006-05-02 09:31:56 +08:00
for sub_obj in getattr ( obj , rel_opts_name ) . all ( ) :
2005-07-13 09:25:57 +08:00
has_related_objs = True
2005-11-26 05:20:09 +08:00
if related . field . rel . edit_inline or not related . opts . admin :
2005-07-13 09:25:57 +08:00
# Don't display link to edit, because it either has no
# admin or is edited inline.
2006-01-15 14:28:41 +08:00
nh ( deleted_objects , current_depth , [ ' %s : %s ' % ( capfirst ( related . opts . verbose_name ) , escape ( str ( 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> ' % \
2006-05-02 09:31:56 +08:00
( capfirst ( related . opts . verbose_name ) , related . opts . app_label , related . opts . object_name . lower ( ) , sub_obj . _get_pk_val ( ) , escape ( str ( sub_obj ) ) ) , [ ] ] )
2005-11-26 05:20:09 +08:00
_get_deleted_objects ( deleted_objects , perms_needed , user , sub_obj , related . opts , current_depth + 2 )
2005-07-13 09:25:57 +08:00
# If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed.
2005-11-26 05:20:09 +08:00
if related . opts . admin and has_related_objs :
p = ' %s . %s ' % ( related . opts . app_label , related . opts . get_delete_permission ( ) )
2005-07-13 09:25:57 +08:00
if not user . has_perm ( p ) :
2006-02-18 02:20:15 +08:00
perms_needed . add ( rel_opts_name )
2005-11-26 05:20:09 +08:00
for related in opts . get_all_related_many_to_many_objects ( ) :
2005-12-10 13:32:05 +08:00
if related . opts in opts_seen :
2005-07-13 09:25:57 +08:00
continue
2005-12-10 13:32:05 +08:00
opts_seen . append ( related . opts )
2006-05-02 09:31:56 +08:00
rel_opts_name = related . get_accessor_name ( )
2005-07-13 09:25:57 +08:00
has_related_objs = False
2006-06-23 02:44:06 +08:00
rel_objs = getattr ( obj , rel_opts_name , None )
if rel_objs :
2005-07-13 09:25:57 +08:00
has_related_objs = True
2006-06-23 02:44:06 +08:00
if has_related_objs :
for sub_obj in rel_objs . all ( ) :
if related . field . rel . edit_inline or not related . 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 %(fieldname)s in %(name)s : %(obj)s ' ) % \
{ ' fieldname ' : related . field . verbose_name , ' name ' : related . opts . verbose_name , ' obj ' : escape ( str ( sub_obj ) ) } , [ ] ] )
else :
# Display a link to the admin page.
nh ( deleted_objects , current_depth , [
( _ ( ' One or more %(fieldname)s in %(name)s : ' ) % { ' fieldname ' : related . field . verbose_name , ' name ' : related . opts . verbose_name } ) + \
( ' <a href= " ../../../../ %s / %s / %s / " > %s </a> ' % \
( related . opts . app_label , related . opts . module_name , sub_obj . _get_pk_val ( ) , escape ( str ( sub_obj ) ) ) ) , [ ] ] )
2005-07-13 09:25:57 +08:00
# If there were related objects, and the user doesn't have
# permission to change them, add the missing perm to perms_needed.
2005-11-26 05:20:09 +08:00
if related . opts . admin and has_related_objs :
p = ' %s . %s ' % ( related . opts . app_label , related . opts . get_change_permission ( ) )
2005-07-13 09:25:57 +08:00
if not user . has_perm ( p ) :
2005-11-26 05:20:09 +08:00
perms_needed . add ( related . opts . verbose_name )
2005-07-13 09:25:57 +08:00
2006-05-02 09:31:56 +08:00
def delete_stage ( request , app_label , model_name , object_id ) :
2005-07-13 09:25:57 +08:00
import sets
2006-05-02 09:31:56 +08:00
model = models . get_model ( app_label , model_name )
object_id = unquote ( object_id )
if model is None :
raise Http404 , " App %r , model %r , not found " % ( app_label , model_name )
opts = model . _meta
2005-07-13 09:25:57 +08:00
if not request . user . has_perm ( app_label + ' . ' + opts . get_delete_permission ( ) ) :
raise PermissionDenied
2006-05-02 09:31:56 +08:00
obj = get_object_or_404 ( model , 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.
2006-01-15 14:28:41 +08:00
deleted_objects = [ ' %s : <a href= " ../../ %s / " > %s </a> ' % ( capfirst ( opts . verbose_name ) , object_id , escape ( str ( 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
2005-11-26 05:20:09 +08:00
obj_display = str ( obj )
2005-07-27 06:43:27 +08:00
obj . delete ( )
2006-05-02 09:31:56 +08:00
LogEntry . objects . log_action ( request . user . id , ContentType . objects . get_for_model ( model ) . id , object_id , obj_display , DELETION )
request . user . message_set . create ( message = _ ( ' The %(name)s " %(obj)s " was deleted successfully. ' ) % { ' name ' : opts . verbose_name , ' obj ' : obj_display } )
2005-07-13 09:25:57 +08:00
return HttpResponseRedirect ( " ../../ " )
2006-05-02 09:31:56 +08:00
extra_context = {
2005-11-26 05:20:09 +08:00
" title " : _ ( " Are you sure? " ) ,
2005-07-13 09:25:57 +08:00
" object_name " : opts . verbose_name ,
" object " : obj ,
" deleted_objects " : deleted_objects ,
" perms_lacking " : perms_needed ,
2006-05-02 09:31:56 +08:00
" opts " : model . _meta ,
}
return render_to_response ( [ " admin/ %s / %s /delete_confirmation.html " % ( app_label , opts . object_name . lower ( ) ) ,
" admin/ %s /delete_confirmation.html " % app_label ,
" admin/delete_confirmation.html " ] , extra_context , context_instance = template . RequestContext ( request ) )
delete_stage = staff_member_required ( never_cache ( delete_stage ) )
def history ( request , app_label , model_name , object_id ) :
model = models . get_model ( app_label , model_name )
object_id = unquote ( object_id )
if model is None :
raise Http404 , " App %r , model %r , not found " % ( app_label , model_name )
action_list = LogEntry . objects . filter ( object_id = object_id ,
content_type__id__exact = ContentType . objects . get_for_model ( model ) . id ) . select_related ( ) . order_by ( ' action_time ' )
2005-07-13 09:25:57 +08:00
# If no history was found, see whether this object even exists.
2006-05-02 09:31:56 +08:00
obj = get_object_or_404 ( model , pk = object_id )
extra_context = {
2005-11-26 05:20:09 +08:00
' title ' : _ ( ' Change history: %s ' ) % obj ,
2005-07-13 09:25:57 +08:00
' action_list ' : action_list ,
2006-05-02 09:31:56 +08:00
' module_name ' : capfirst ( model . _meta . verbose_name_plural ) ,
2005-07-13 09:25:57 +08:00
' object ' : obj ,
2006-05-02 09:31:56 +08:00
}
return render_to_response ( [ " admin/ %s / %s /object_history.html " % ( app_label , model . _meta . object_name . lower ( ) ) ,
" admin/ %s /object_history.html " % app_label ,
" admin/object_history.html " ] , extra_context , context_instance = template . RequestContext ( request ) )
history = staff_member_required ( never_cache ( history ) )
class ChangeList ( object ) :
def __init__ ( self , request , model ) :
self . model = model
self . opts = model . _meta
self . lookup_opts = self . opts
self . manager = self . opts . admin . manager
# Get search parameters from the query string.
try :
self . page_num = int ( request . GET . get ( PAGE_VAR , 0 ) )
except ValueError :
self . page_num = 0
self . show_all = request . GET . has_key ( ALL_VAR )
self . is_popup = request . GET . has_key ( IS_POPUP_VAR )
self . params = dict ( request . GET . items ( ) )
if self . params . has_key ( PAGE_VAR ) :
del self . params [ PAGE_VAR ]
2006-05-31 23:25:23 +08:00
if self . params . has_key ( ERROR_FLAG ) :
del self . params [ ERROR_FLAG ]
2006-05-02 09:31:56 +08:00
self . order_field , self . order_type = self . get_ordering ( )
self . query = request . GET . get ( SEARCH_VAR , ' ' )
self . query_set = self . get_query_set ( )
self . get_results ( request )
self . title = ( self . is_popup and _ ( ' Select %s ' ) % self . opts . verbose_name or _ ( ' Select %s to change ' ) % self . opts . verbose_name )
self . filter_specs , self . has_filters = self . get_filters ( request )
self . pk_attname = self . lookup_opts . pk . attname
def get_filters ( self , request ) :
filter_specs = [ ]
if self . lookup_opts . admin . list_filter and not self . opts . one_to_one_field :
filter_fields = [ self . lookup_opts . get_field ( field_name ) \
for field_name in self . lookup_opts . admin . list_filter ]
for f in filter_fields :
2006-06-17 03:42:06 +08:00
spec = FilterSpec . create ( f , request , self . params , self . model )
2006-05-02 09:31:56 +08:00
if spec and spec . has_output ( ) :
filter_specs . append ( spec )
return filter_specs , bool ( filter_specs )
2006-06-03 21:37:34 +08:00
def get_query_string ( self , new_params = None , remove = None ) :
if new_params is None : new_params = { }
if remove is None : remove = [ ]
2006-05-02 09:31:56 +08:00
p = self . 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 get_results ( self , request ) :
paginator = ObjectPaginator ( self . query_set , self . lookup_opts . admin . list_per_page )
# Get the number of objects, with admin filters applied.
try :
result_count = paginator . hits
# 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 :
raise IncorrectLookupParameters
# Get the total number of objects, with no admin filters applied.
# Perform a slight optimization: Check to see whether any filters were
# given. If not, use paginator.hits to calculate the number of objects,
# because we've already done paginator.hits and the value is cached.
if isinstance ( self . query_set . _filters , models . Q ) and not self . query_set . _filters . kwargs :
full_result_count = result_count
else :
full_result_count = self . manager . count ( )
can_show_all = result_count < = MAX_SHOW_ALL_ALLOWED
multi_page = result_count > self . lookup_opts . admin . list_per_page
# Get the list of objects to display on this page.
if ( self . show_all and can_show_all ) or not multi_page :
result_list = list ( self . query_set )
else :
try :
result_list = paginator . get_page ( self . page_num )
except InvalidPage :
result_list = ( )
self . result_count = result_count
self . full_result_count = full_result_count
self . result_list = result_list
self . can_show_all = can_show_all
self . multi_page = multi_page
self . paginator = paginator
def get_ordering ( self ) :
lookup_opts , params = self . lookup_opts , self . params
# For ordering, first check the "ordering" parameter in the admin options,
# 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.
ordering = lookup_opts . admin . ordering or lookup_opts . ordering or [ ' - ' + lookup_opts . pk . name ]
# Normalize it to new-style ordering.
ordering = handle_legacy_orderlist ( ordering )
if ordering [ 0 ] . startswith ( ' - ' ) :
order_field , order_type = ordering [ 0 ] [ 1 : ] , ' desc '
else :
order_field , order_type = ordering [ 0 ] , ' asc '
if params . has_key ( ORDER_VAR ) :
try :
try :
f = lookup_opts . get_field ( lookup_opts . admin . list_display [ int ( params [ ORDER_VAR ] ) ] )
except models . FieldDoesNotExist :
pass
else :
if not isinstance ( f . rel , models . ManyToOneRel ) 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 ]
return order_field , order_type
def get_query_set ( self ) :
qs = self . manager . get_query_set ( )
lookup_params = self . params . copy ( ) # a dictionary of the query string
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 ]
# Apply lookup parameters from the query string.
qs = qs . filter ( * * lookup_params )
# Use select_related() if one of the list_display options is a field
# with a relationship.
if self . lookup_opts . admin . list_select_related :
qs = qs . select_related ( )
else :
for field_name in self . lookup_opts . admin . list_display :
try :
f = self . lookup_opts . get_field ( field_name )
except models . FieldDoesNotExist :
pass
else :
if isinstance ( f . rel , models . ManyToOneRel ) :
qs = qs . select_related ( )
break
# Calculate lookup_order_field.
# If the order-by field is a field with a relationship, order by the
# value in the related table.
lookup_order_field = self . order_field
try :
f = self . lookup_opts . get_field ( self . order_field , many_to_many = False )
except models . FieldDoesNotExist :
pass
else :
if isinstance ( f . rel , models . OneToOneRel ) :
# For OneToOneFields, don't try to order by the related object's ordering criteria.
pass
elif isinstance ( f . rel , models . ManyToOneRel ) :
rel_ordering = f . rel . to . _meta . ordering and f . rel . to . _meta . ordering [ 0 ] or f . rel . to . _meta . pk . column
lookup_order_field = ' %s . %s ' % ( f . rel . to . _meta . db_table , rel_ordering )
# Set ordering.
qs = qs . order_by ( ( self . order_type == ' desc ' and ' - ' or ' ' ) + lookup_order_field )
# Apply keyword searches.
2006-08-18 10:48:34 +08:00
def construct_search ( field_name ) :
if field_name . startswith ( ' ^ ' ) :
return " %s __istartswith " % field_name [ 1 : ]
elif field_name . startswith ( ' = ' ) :
return " %s __iexact " % field_name [ 1 : ]
elif field_name . startswith ( ' @ ' ) :
return " %s __search " % field_name [ 1 : ]
else :
return " %s __icontains " % field_name
2006-08-19 05:39:29 +08:00
2006-05-02 09:31:56 +08:00
if self . lookup_opts . admin . search_fields and self . query :
for bit in self . query . split ( ) :
2006-08-18 10:48:34 +08:00
or_queries = [ models . Q ( * * { construct_search ( field_name ) : bit } ) for field_name in self . lookup_opts . admin . search_fields ]
2006-05-02 09:31:56 +08:00
other_qs = QuerySet ( self . model )
2006-09-22 21:10:46 +08:00
if qs . _select_related :
other_qs = other_qs . select_related ( )
2006-05-02 09:31:56 +08:00
other_qs = other_qs . filter ( reduce ( operator . or_ , or_queries ) )
qs = qs & other_qs
if self . opts . one_to_one_field :
2006-05-06 08:26:24 +08:00
qs = qs . complex_filter ( self . opts . one_to_one_field . rel . limit_choices_to )
2006-05-02 09:31:56 +08:00
return qs
def url_for_result ( self , result ) :
return " %s / " % quote ( getattr ( result , self . pk_attname ) )
def change_list ( request , app_label , model_name ) :
model = models . get_model ( app_label , model_name )
if model is None :
raise Http404 , " App %r , model %r , not found " % ( app_label , model_name )
if not request . user . has_perm ( app_label + ' . ' + model . _meta . get_change_permission ( ) ) :
raise PermissionDenied
try :
cl = ChangeList ( request , model )
except IncorrectLookupParameters :
2006-05-31 23:25:23 +08:00
# Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1'
# parameter via the query string. If wacky parameters were given and
# the 'invalid=1' parameter was already in the query string, something
# is screwed up with the database, so display an error page.
if ERROR_FLAG in request . GET . keys ( ) :
return render_to_response ( ' admin/invalid_setup.html ' , { ' title ' : _ ( ' Database error ' ) } )
return HttpResponseRedirect ( request . path + ' ? ' + ERROR_FLAG + ' =1 ' )
2006-05-02 09:31:56 +08:00
c = template . RequestContext ( request , {
' title ' : cl . title ,
' is_popup ' : cl . is_popup ,
' cl ' : cl ,
} )
c . update ( { ' has_add_permission ' : c [ ' perms ' ] [ app_label ] [ cl . opts . get_add_permission ( ) ] } ) ,
return render_to_response ( [ ' admin/ %s / %s /change_list.html ' % ( app_label , cl . opts . object_name . lower ( ) ) ,
' admin/ %s /change_list.html ' % app_label ,
' admin/change_list.html ' ] , context_instance = c )
change_list = staff_member_required ( never_cache ( change_list ) )