1 |
|
2 from django import forms |
1 from django import forms |
3 from django.conf import settings |
2 from django.conf import settings |
4 from django.utils.html import escape |
3 from django.contrib.admin.util import flatten_fieldsets, lookup_field |
|
4 from django.contrib.admin.util import display_for_field, label_for_field |
|
5 from django.contrib.contenttypes.models import ContentType |
|
6 from django.core.exceptions import ObjectDoesNotExist |
|
7 from django.db.models.fields import FieldDoesNotExist |
|
8 from django.db.models.fields.related import ManyToManyRel |
|
9 from django.forms.util import flatatt |
|
10 from django.template.defaultfilters import capfirst |
|
11 from django.utils.encoding import force_unicode, smart_unicode |
|
12 from django.utils.html import escape, conditional_escape |
5 from django.utils.safestring import mark_safe |
13 from django.utils.safestring import mark_safe |
6 from django.utils.encoding import force_unicode |
|
7 from django.contrib.admin.util import flatten_fieldsets |
|
8 from django.contrib.contenttypes.models import ContentType |
|
9 from django.utils.translation import ugettext_lazy as _ |
14 from django.utils.translation import ugettext_lazy as _ |
|
15 |
10 |
16 |
11 ACTION_CHECKBOX_NAME = '_selected_action' |
17 ACTION_CHECKBOX_NAME = '_selected_action' |
12 |
18 |
13 class ActionForm(forms.Form): |
19 class ActionForm(forms.Form): |
14 action = forms.ChoiceField(label=_('Action:')) |
20 action = forms.ChoiceField(label=_('Action:')) |
|
21 select_across = forms.BooleanField(label='', required=False, initial=0, |
|
22 widget=forms.HiddenInput({'class': 'select-across'})) |
15 |
23 |
16 checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False) |
24 checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False) |
17 |
25 |
18 class AdminForm(object): |
26 class AdminForm(object): |
19 def __init__(self, form, fieldsets, prepopulated_fields): |
27 def __init__(self, form, fieldsets, prepopulated_fields, readonly_fields=None, model_admin=None): |
20 self.form, self.fieldsets = form, normalize_fieldsets(fieldsets) |
28 self.form, self.fieldsets = form, normalize_fieldsets(fieldsets) |
21 self.prepopulated_fields = [{ |
29 self.prepopulated_fields = [{ |
22 'field': form[field_name], |
30 'field': form[field_name], |
23 'dependencies': [form[f] for f in dependencies] |
31 'dependencies': [form[f] for f in dependencies] |
24 } for field_name, dependencies in prepopulated_fields.items()] |
32 } for field_name, dependencies in prepopulated_fields.items()] |
|
33 self.model_admin = model_admin |
|
34 if readonly_fields is None: |
|
35 readonly_fields = () |
|
36 self.readonly_fields = readonly_fields |
25 |
37 |
26 def __iter__(self): |
38 def __iter__(self): |
27 for name, options in self.fieldsets: |
39 for name, options in self.fieldsets: |
28 yield Fieldset(self.form, name, **options) |
40 yield Fieldset(self.form, name, |
|
41 readonly_fields=self.readonly_fields, |
|
42 model_admin=self.model_admin, |
|
43 **options |
|
44 ) |
29 |
45 |
30 def first_field(self): |
46 def first_field(self): |
31 try: |
47 try: |
32 fieldset_name, fieldset_options = self.fieldsets[0] |
48 fieldset_name, fieldset_options = self.fieldsets[0] |
33 field_name = fieldset_options['fields'][0] |
49 field_name = fieldset_options['fields'][0] |
47 media = media + fs.media |
63 media = media + fs.media |
48 return media |
64 return media |
49 media = property(_media) |
65 media = property(_media) |
50 |
66 |
51 class Fieldset(object): |
67 class Fieldset(object): |
52 def __init__(self, form, name=None, fields=(), classes=(), description=None): |
68 def __init__(self, form, name=None, readonly_fields=(), fields=(), classes=(), |
|
69 description=None, model_admin=None): |
53 self.form = form |
70 self.form = form |
54 self.name, self.fields = name, fields |
71 self.name, self.fields = name, fields |
55 self.classes = u' '.join(classes) |
72 self.classes = u' '.join(classes) |
56 self.description = description |
73 self.description = description |
|
74 self.model_admin = model_admin |
|
75 self.readonly_fields = readonly_fields |
57 |
76 |
58 def _media(self): |
77 def _media(self): |
59 if 'collapse' in self.classes: |
78 if 'collapse' in self.classes: |
60 return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX]) |
79 js = ['js/jquery.min.js', 'js/jquery.init.js', 'js/collapse.min.js'] |
|
80 return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js]) |
61 return forms.Media() |
81 return forms.Media() |
62 media = property(_media) |
82 media = property(_media) |
63 |
83 |
64 def __iter__(self): |
84 def __iter__(self): |
65 for field in self.fields: |
85 for field in self.fields: |
66 yield Fieldline(self.form, field) |
86 yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin) |
67 |
87 |
68 class Fieldline(object): |
88 class Fieldline(object): |
69 def __init__(self, form, field): |
89 def __init__(self, form, field, readonly_fields=None, model_admin=None): |
70 self.form = form # A django.forms.Form instance |
90 self.form = form # A django.forms.Form instance |
71 if isinstance(field, basestring): |
91 if not hasattr(field, "__iter__"): |
72 self.fields = [field] |
92 self.fields = [field] |
73 else: |
93 else: |
74 self.fields = field |
94 self.fields = field |
|
95 self.model_admin = model_admin |
|
96 if readonly_fields is None: |
|
97 readonly_fields = () |
|
98 self.readonly_fields = readonly_fields |
75 |
99 |
76 def __iter__(self): |
100 def __iter__(self): |
77 for i, field in enumerate(self.fields): |
101 for i, field in enumerate(self.fields): |
78 yield AdminField(self.form, field, is_first=(i == 0)) |
102 if field in self.readonly_fields: |
|
103 yield AdminReadonlyField(self.form, field, is_first=(i == 0), |
|
104 model_admin=self.model_admin) |
|
105 else: |
|
106 yield AdminField(self.form, field, is_first=(i == 0)) |
79 |
107 |
80 def errors(self): |
108 def errors(self): |
81 return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields]).strip('\n')) |
109 return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields]).strip('\n')) |
82 |
110 |
83 class AdminField(object): |
111 class AdminField(object): |
84 def __init__(self, form, field, is_first): |
112 def __init__(self, form, field, is_first): |
85 self.field = form[field] # A django.forms.BoundField instance |
113 self.field = form[field] # A django.forms.BoundField instance |
86 self.is_first = is_first # Whether this field is first on the line |
114 self.is_first = is_first # Whether this field is first on the line |
98 if not self.is_first: |
126 if not self.is_first: |
99 classes.append(u'inline') |
127 classes.append(u'inline') |
100 attrs = classes and {'class': u' '.join(classes)} or {} |
128 attrs = classes and {'class': u' '.join(classes)} or {} |
101 return self.field.label_tag(contents=contents, attrs=attrs) |
129 return self.field.label_tag(contents=contents, attrs=attrs) |
102 |
130 |
|
131 class AdminReadonlyField(object): |
|
132 def __init__(self, form, field, is_first, model_admin=None): |
|
133 label = label_for_field(field, form._meta.model, model_admin) |
|
134 # Make self.field look a little bit like a field. This means that |
|
135 # {{ field.name }} must be a useful class name to identify the field. |
|
136 # For convenience, store other field-related data here too. |
|
137 if callable(field): |
|
138 class_name = field.__name__ != '<lambda>' and field.__name__ or '' |
|
139 else: |
|
140 class_name = field |
|
141 self.field = { |
|
142 'name': class_name, |
|
143 'label': label, |
|
144 'field': field, |
|
145 } |
|
146 self.form = form |
|
147 self.model_admin = model_admin |
|
148 self.is_first = is_first |
|
149 self.is_checkbox = False |
|
150 self.is_readonly = True |
|
151 |
|
152 def label_tag(self): |
|
153 attrs = {} |
|
154 if not self.is_first: |
|
155 attrs["class"] = "inline" |
|
156 label = self.field['label'] |
|
157 contents = capfirst(force_unicode(escape(label))) + u":" |
|
158 return mark_safe('<label%(attrs)s>%(contents)s</label>' % { |
|
159 "attrs": flatatt(attrs), |
|
160 "contents": contents, |
|
161 }) |
|
162 |
|
163 def contents(self): |
|
164 from django.contrib.admin.templatetags.admin_list import _boolean_icon |
|
165 from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE |
|
166 field, obj, model_admin = self.field['field'], self.form.instance, self.model_admin |
|
167 try: |
|
168 f, attr, value = lookup_field(field, obj, model_admin) |
|
169 except (AttributeError, ValueError, ObjectDoesNotExist): |
|
170 result_repr = EMPTY_CHANGELIST_VALUE |
|
171 else: |
|
172 if f is None: |
|
173 boolean = getattr(attr, "boolean", False) |
|
174 if boolean: |
|
175 result_repr = _boolean_icon(value) |
|
176 else: |
|
177 result_repr = smart_unicode(value) |
|
178 if getattr(attr, "allow_tags", False): |
|
179 result_repr = mark_safe(result_repr) |
|
180 else: |
|
181 if value is None: |
|
182 result_repr = EMPTY_CHANGELIST_VALUE |
|
183 elif isinstance(f.rel, ManyToManyRel): |
|
184 result_repr = ", ".join(map(unicode, value.all())) |
|
185 else: |
|
186 result_repr = display_for_field(value, f) |
|
187 return conditional_escape(result_repr) |
|
188 |
103 class InlineAdminFormSet(object): |
189 class InlineAdminFormSet(object): |
104 """ |
190 """ |
105 A wrapper around an inline formset for use in the admin system. |
191 A wrapper around an inline formset for use in the admin system. |
106 """ |
192 """ |
107 def __init__(self, inline, formset, fieldsets): |
193 def __init__(self, inline, formset, fieldsets, readonly_fields=None, model_admin=None): |
108 self.opts = inline |
194 self.opts = inline |
109 self.formset = formset |
195 self.formset = formset |
110 self.fieldsets = fieldsets |
196 self.fieldsets = fieldsets |
|
197 self.model_admin = model_admin |
|
198 if readonly_fields is None: |
|
199 readonly_fields = () |
|
200 self.readonly_fields = readonly_fields |
111 |
201 |
112 def __iter__(self): |
202 def __iter__(self): |
113 for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): |
203 for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): |
114 yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, original) |
204 yield InlineAdminForm(self.formset, form, self.fieldsets, |
|
205 self.opts.prepopulated_fields, original, self.readonly_fields, |
|
206 model_admin=self.model_admin) |
115 for form in self.formset.extra_forms: |
207 for form in self.formset.extra_forms: |
116 yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None) |
208 yield InlineAdminForm(self.formset, form, self.fieldsets, |
|
209 self.opts.prepopulated_fields, None, self.readonly_fields, |
|
210 model_admin=self.model_admin) |
|
211 yield InlineAdminForm(self.formset, self.formset.empty_form, |
|
212 self.fieldsets, self.opts.prepopulated_fields, None, |
|
213 self.readonly_fields, model_admin=self.model_admin) |
117 |
214 |
118 def fields(self): |
215 def fields(self): |
119 fk = getattr(self.formset, "fk", None) |
216 fk = getattr(self.formset, "fk", None) |
120 for field_name in flatten_fieldsets(self.fieldsets): |
217 for i, field in enumerate(flatten_fieldsets(self.fieldsets)): |
121 if fk and fk.name == field_name: |
218 if fk and fk.name == field: |
122 continue |
219 continue |
123 yield self.formset.form.base_fields[field_name] |
220 if field in self.readonly_fields: |
|
221 yield { |
|
222 'label': label_for_field(field, self.opts.model, self.model_admin), |
|
223 'widget': { |
|
224 'is_hidden': False |
|
225 }, |
|
226 'required': False |
|
227 } |
|
228 else: |
|
229 yield self.formset.form.base_fields[field] |
124 |
230 |
125 def _media(self): |
231 def _media(self): |
126 media = self.opts.media + self.formset.media |
232 media = self.opts.media + self.formset.media |
127 for fs in self: |
233 for fs in self: |
128 media = media + fs.media |
234 media = media + fs.media |
131 |
237 |
132 class InlineAdminForm(AdminForm): |
238 class InlineAdminForm(AdminForm): |
133 """ |
239 """ |
134 A wrapper around an inline form for use in the admin system. |
240 A wrapper around an inline form for use in the admin system. |
135 """ |
241 """ |
136 def __init__(self, formset, form, fieldsets, prepopulated_fields, original): |
242 def __init__(self, formset, form, fieldsets, prepopulated_fields, original, |
|
243 readonly_fields=None, model_admin=None): |
137 self.formset = formset |
244 self.formset = formset |
|
245 self.model_admin = model_admin |
138 self.original = original |
246 self.original = original |
139 if original is not None: |
247 if original is not None: |
140 self.original_content_type_id = ContentType.objects.get_for_model(original).pk |
248 self.original_content_type_id = ContentType.objects.get_for_model(original).pk |
141 self.show_url = original and hasattr(original, 'get_absolute_url') |
249 self.show_url = original and hasattr(original, 'get_absolute_url') |
142 super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields) |
250 super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields, |
|
251 readonly_fields, model_admin) |
143 |
252 |
144 def __iter__(self): |
253 def __iter__(self): |
145 for name, options in self.fieldsets: |
254 for name, options in self.fieldsets: |
146 yield InlineFieldset(self.formset, self.form, name, **options) |
255 yield InlineFieldset(self.formset, self.form, name, |
|
256 self.readonly_fields, model_admin=self.model_admin, **options) |
147 |
257 |
148 def has_auto_field(self): |
258 def has_auto_field(self): |
149 if self.form._meta.model._meta.has_auto_field: |
259 if self.form._meta.model._meta.has_auto_field: |
150 return True |
260 return True |
151 # Also search any parents for an auto field. |
261 # Also search any parents for an auto field. |