1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
7 from django
import forms
8 from django
.contrib
import admin
9 from django
.contrib
.admin
.views
.main
import ChangeList
10 from django
.core
.files
.storage
import FileSystemStorage
11 from django
.core
.mail
import EmailMessage
12 from django
.conf
.urls
import patterns
, url
13 from django
.db
import models
14 from django
.forms
.models
import BaseModelFormSet
15 from django
.http
import HttpResponse
16 from django
.contrib
.admin
import BooleanFieldListFilter
18 from .models
import (Article
, Chapter
, Account
, Media
, Child
, Parent
, Picture
,
19 Widget
, DooHickey
, Grommet
, Whatsit
, FancyDoodad
, Category
, Link
,
20 PrePopulatedPost
, PrePopulatedSubPost
, CustomArticle
, Section
,
21 ModelWithStringPrimaryKey
, Color
, Thing
, Actor
, Inquisition
, Sketch
, Person
,
22 Persona
, Subscriber
, ExternalSubscriber
, OldSubscriber
, Vodcast
, EmptyModel
,
23 Fabric
, Gallery
, Language
, Recommendation
, Recommender
, Collector
, Post
,
24 Gadget
, Villain
, SuperVillain
, Plot
, PlotDetails
, CyclicOne
, CyclicTwo
,
25 WorkHour
, Reservation
, FoodDelivery
, RowLevelChangePermissionModel
, Paper
,
26 CoverLetter
, Story
, OtherStory
, Book
, Promo
, ChapterXtra1
, Pizza
, Topping
,
27 Album
, Question
, Answer
, ComplexSortedPerson
, PrePopulatedPostLargeSlug
,
28 AdminOrderedField
, AdminOrderedModelMethod
, AdminOrderedAdminMethod
,
29 AdminOrderedCallable
, Report
, Color2
, UnorderedObject
, MainPrepopulated
,
33 def callable_year(dt_value
):
35 callable_year
.admin_order_field
= 'date'
38 class ArticleInline(admin
.TabularInline
):
40 prepopulated_fields
= {
41 'title' : ('content',)
45 'classes': ('collapse',),
46 'fields': ('title', 'content')
48 ('Some other fields', {
50 'fields': ('date', 'section')
54 class ChapterInline(admin
.TabularInline
):
58 class ChapterXtra1Admin(admin
.ModelAdmin
):
59 list_filter
= ('chap',
64 'chap__book__promo__name',)
67 class ArticleAdmin(admin
.ModelAdmin
):
68 list_display
= ('content', 'date', callable_year
, 'model_year', 'modeladmin_year')
69 list_filter
= ('date', 'section')
71 def changelist_view(self
, request
):
72 "Test that extra_context works"
73 return super(ArticleAdmin
, self
).changelist_view(
74 request
, extra_context
={
79 def modeladmin_year(self
, obj
):
81 modeladmin_year
.admin_order_field
= 'date'
82 modeladmin_year
.short_description
= None
84 def delete_model(self
, request
, obj
):
86 'Greetings from a deleted object',
87 'I hereby inform you that some user deleted me',
91 return super(ArticleAdmin
, self
).delete_model(request
, obj
)
93 def save_model(self
, request
, obj
, form
, change
=True):
95 'Greetings from a created object',
96 'I hereby inform you that some user created me',
100 return super(ArticleAdmin
, self
).save_model(request
, obj
, form
, change
)
103 class RowLevelChangePermissionModelAdmin(admin
.ModelAdmin
):
104 def has_change_permission(self
, request
, obj
=None):
105 """ Only allow changing objects with even id number """
106 return request
.user
.is_staff
and (obj
is not None) and (obj
.id % 2 == 0)
109 class CustomArticleAdmin(admin
.ModelAdmin
):
111 Tests various hooks for using custom templates and contexts.
113 change_list_template
= 'custom_admin/change_list.html'
114 change_form_template
= 'custom_admin/change_form.html'
115 add_form_template
= 'custom_admin/add_form.html'
116 object_history_template
= 'custom_admin/object_history.html'
117 delete_confirmation_template
= 'custom_admin/delete_confirmation.html'
118 delete_selected_confirmation_template
= 'custom_admin/delete_selected_confirmation.html'
120 def changelist_view(self
, request
):
121 "Test that extra_context works"
122 return super(CustomArticleAdmin
, self
).changelist_view(
123 request
, extra_context
={
124 'extra_var': 'Hello!'
129 class ThingAdmin(admin
.ModelAdmin
):
130 list_filter
= ('color__warm', 'color__value', 'pub_date',)
133 class InquisitionAdmin(admin
.ModelAdmin
):
134 list_display
= ('leader', 'country', 'expected')
137 class SketchAdmin(admin
.ModelAdmin
):
138 raw_id_fields
= ('inquisition',)
141 class FabricAdmin(admin
.ModelAdmin
):
142 list_display
= ('surface',)
143 list_filter
= ('surface',)
146 class BasePersonModelFormSet(BaseModelFormSet
):
148 for person_dict
in self
.cleaned_data
:
149 person
= person_dict
.get('id')
150 alive
= person_dict
.get('alive')
151 if person
and alive
and person
.name
== "Grace Hopper":
152 raise forms
.ValidationError("Grace is not a Zombie")
155 class PersonAdmin(admin
.ModelAdmin
):
156 list_display
= ('name', 'gender', 'alive')
157 list_editable
= ('gender', 'alive')
158 list_filter
= ('gender',)
159 search_fields
= ('^name',)
162 def get_changelist_formset(self
, request
, **kwargs
):
163 return super(PersonAdmin
, self
).get_changelist_formset(request
,
164 formset
=BasePersonModelFormSet
, **kwargs
)
166 def queryset(self
, request
):
167 # Order by a field that isn't in list display, to be able to test
168 # whether ordering is preserved.
169 return super(PersonAdmin
, self
).queryset(request
).order_by('age')
172 class FooAccount(Account
):
173 """A service-specific account of type Foo."""
177 class BarAccount(Account
):
178 """A service-specific account of type Bar."""
182 class FooAccountAdmin(admin
.StackedInline
):
187 class BarAccountAdmin(admin
.StackedInline
):
192 class PersonaAdmin(admin
.ModelAdmin
):
199 class SubscriberAdmin(admin
.ModelAdmin
):
200 actions
= ['mail_admin']
202 def mail_admin(self
, request
, selected
):
204 'Greetings from a ModelAdmin action',
205 'This is the test email from a admin action',
211 def external_mail(modeladmin
, request
, selected
):
213 'Greetings from a function action',
214 'This is the test email from a function action',
218 external_mail
.short_description
= 'External mail (Another awesome action)'
221 def redirect_to(modeladmin
, request
, selected
):
222 from django
.http
import HttpResponseRedirect
223 return HttpResponseRedirect('/some-where-else/')
224 redirect_to
.short_description
= 'Redirect to (Awesome action)'
227 class ExternalSubscriberAdmin(admin
.ModelAdmin
):
228 actions
= [redirect_to
, external_mail
]
231 class Podcast(Media
):
232 release_date
= models
.DateField()
235 ordering
= ('release_date',) # overridden in PodcastAdmin
238 class PodcastAdmin(admin
.ModelAdmin
):
239 list_display
= ('name', 'release_date')
240 list_editable
= ('release_date',)
241 date_hierarchy
= 'release_date'
245 class VodcastAdmin(admin
.ModelAdmin
):
246 list_display
= ('name', 'released')
247 list_editable
= ('released',)
252 class ChildInline(admin
.StackedInline
):
256 class ParentAdmin(admin
.ModelAdmin
):
258 inlines
= [ChildInline
]
260 list_editable
= ('name',)
262 def save_related(self
, request
, form
, formsets
, change
):
263 super(ParentAdmin
, self
).save_related(request
, form
, formsets
, change
)
264 first_name
, last_name
= form
.instance
.name
.split()
265 for child
in form
.instance
.child_set
.all():
266 if len(child
.name
.split()) < 2:
267 child
.name
= child
.name
+ ' ' + last_name
271 class EmptyModelAdmin(admin
.ModelAdmin
):
272 def queryset(self
, request
):
273 return super(EmptyModelAdmin
, self
).queryset(request
).filter(pk__gt
=1)
276 class OldSubscriberAdmin(admin
.ModelAdmin
):
280 temp_storage
= FileSystemStorage(tempfile
.mkdtemp(dir=os
.environ
['DJANGO_TEST_TEMP_DIR']))
281 UPLOAD_TO
= os
.path
.join(temp_storage
.location
, 'test_upload')
284 class PictureInline(admin
.TabularInline
):
289 class GalleryAdmin(admin
.ModelAdmin
):
290 inlines
= [PictureInline
]
293 class PictureAdmin(admin
.ModelAdmin
):
297 class LanguageAdmin(admin
.ModelAdmin
):
298 list_display
= ['iso', 'shortlist', 'english_name', 'name']
299 list_editable
= ['shortlist']
302 class RecommendationAdmin(admin
.ModelAdmin
):
303 search_fields
= ('=titletranslation__text', '=recommender__titletranslation__text',)
306 class WidgetInline(admin
.StackedInline
):
310 class DooHickeyInline(admin
.StackedInline
):
314 class GrommetInline(admin
.StackedInline
):
318 class WhatsitInline(admin
.StackedInline
):
322 class FancyDoodadInline(admin
.StackedInline
):
326 class CategoryAdmin(admin
.ModelAdmin
):
327 list_display
= ('id', 'collector', 'order')
328 list_editable
= ('order',)
331 class CategoryInline(admin
.StackedInline
):
335 class CollectorAdmin(admin
.ModelAdmin
):
337 WidgetInline
, DooHickeyInline
, GrommetInline
, WhatsitInline
,
338 FancyDoodadInline
, CategoryInline
342 class LinkInline(admin
.TabularInline
):
346 readonly_fields
= ("posted",)
349 class SubPostInline(admin
.TabularInline
):
350 model
= PrePopulatedSubPost
352 prepopulated_fields
= {
353 'subslug' : ('subtitle',)
356 def get_readonly_fields(self
, request
, obj
=None):
357 if obj
and obj
.published
:
359 return self
.readonly_fields
361 def get_prepopulated_fields(self
, request
, obj
=None):
362 if obj
and obj
.published
:
364 return self
.prepopulated_fields
367 class PrePopulatedPostAdmin(admin
.ModelAdmin
):
368 list_display
= ['title', 'slug']
369 prepopulated_fields
= {
373 inlines
= [SubPostInline
]
375 def get_readonly_fields(self
, request
, obj
=None):
376 if obj
and obj
.published
:
378 return self
.readonly_fields
380 def get_prepopulated_fields(self
, request
, obj
=None):
381 if obj
and obj
.published
:
383 return self
.prepopulated_fields
386 class PostAdmin(admin
.ModelAdmin
):
387 list_display
= ['title', 'public']
388 readonly_fields
= ('posted', 'awesomeness_level', 'coolness', 'value', lambda obj
: "foo")
394 def coolness(self
, instance
):
396 return "%d amount of cool." % instance
.pk
398 return "Unkown coolness."
400 def value(self
, instance
):
402 value
.short_description
= 'Value in $US'
405 class CustomChangeList(ChangeList
):
406 def get_query_set(self
, request
):
407 return self
.root_query_set
.filter(pk
=9999) # Does not exist
410 class GadgetAdmin(admin
.ModelAdmin
):
411 def get_changelist(self
, request
, **kwargs
):
412 return CustomChangeList
415 class PizzaAdmin(admin
.ModelAdmin
):
416 readonly_fields
= ('toppings',)
419 class WorkHourAdmin(admin
.ModelAdmin
):
420 list_display
= ('datum', 'employee')
421 list_filter
= ('employee',)
424 class FoodDeliveryAdmin(admin
.ModelAdmin
):
425 list_display
=('reference', 'driver', 'restaurant')
426 list_editable
= ('driver', 'restaurant')
429 class PaperAdmin(admin
.ModelAdmin
):
431 A ModelAdmin with a custom queryset() method that uses only(), to test
432 verbose_name display in messages shown after adding Paper instances.
435 def queryset(self
, request
):
436 return super(PaperAdmin
, self
).queryset(request
).only('title')
439 class CoverLetterAdmin(admin
.ModelAdmin
):
441 A ModelAdmin with a custom queryset() method that uses only(), to test
442 verbose_name display in messages shown after adding CoverLetter instances.
443 Note that the CoverLetter model defines a __unicode__ method.
446 def queryset(self
, request
):
447 return super(CoverLetterAdmin
, self
).queryset(request
).defer('date_written')
450 class StoryForm(forms
.ModelForm
):
452 widgets
= {'title': forms
.HiddenInput
}
455 class StoryAdmin(admin
.ModelAdmin
):
456 list_display
= ('id', 'title', 'content')
457 list_display_links
= ('title',) # 'id' not in list_display_links
458 list_editable
= ('content', )
463 class OtherStoryAdmin(admin
.ModelAdmin
):
464 list_display
= ('id', 'title', 'content')
465 list_display_links
= ('title', 'id') # 'id' in list_display_links
466 list_editable
= ('content', )
470 class ComplexSortedPersonAdmin(admin
.ModelAdmin
):
471 list_display
= ('name', 'age', 'is_employee', 'colored_name')
474 def colored_name(self
, obj
):
475 return '<span style="color: #%s;">%s</span>' % ('ff00ff', obj
.name
)
476 colored_name
.allow_tags
= True
477 colored_name
.admin_order_field
= 'name'
480 class AlbumAdmin(admin
.ModelAdmin
):
481 list_filter
= ['title']
484 class WorkHourAdmin(admin
.ModelAdmin
):
485 list_display
= ('datum', 'employee')
486 list_filter
= ('employee',)
489 class PrePopulatedPostLargeSlugAdmin(admin
.ModelAdmin
):
490 prepopulated_fields
= {
495 class AdminOrderedFieldAdmin(admin
.ModelAdmin
):
496 ordering
= ('order',)
497 list_display
= ('stuff', 'order')
499 class AdminOrderedModelMethodAdmin(admin
.ModelAdmin
):
500 ordering
= ('order',)
501 list_display
= ('stuff', 'some_order')
503 class AdminOrderedAdminMethodAdmin(admin
.ModelAdmin
):
504 def some_admin_order(self
, obj
):
506 some_admin_order
.admin_order_field
= 'order'
507 ordering
= ('order',)
508 list_display
= ('stuff', 'some_admin_order')
510 def admin_ordered_callable(obj
):
512 admin_ordered_callable
.admin_order_field
= 'order'
513 class AdminOrderedCallableAdmin(admin
.ModelAdmin
):
514 ordering
= ('order',)
515 list_display
= ('stuff', admin_ordered_callable
)
517 class ReportAdmin(admin
.ModelAdmin
):
518 def extra(self
, request
):
519 return HttpResponse()
522 # Corner case: Don't call parent implementation
530 class CustomTemplateBooleanFieldListFilter(BooleanFieldListFilter
):
531 template
= 'custom_filter_template.html'
533 class CustomTemplateFilterColorAdmin(admin
.ModelAdmin
):
534 list_filter
= (('warm', CustomTemplateBooleanFieldListFilter
),)
537 # For Selenium Prepopulated tests -------------------------------------
538 class RelatedPrepopulatedInline1(admin
.StackedInline
):
541 'fields': (('pubdate', 'status'), ('name', 'slug1', 'slug2',),)
544 model
= RelatedPrepopulated
546 prepopulated_fields
= {'slug1': ['name', 'pubdate'],
547 'slug2': ['status', 'name']}
549 class RelatedPrepopulatedInline2(admin
.TabularInline
):
550 model
= RelatedPrepopulated
552 prepopulated_fields
= {'slug1': ['name', 'pubdate'],
553 'slug2': ['status', 'name']}
555 class MainPrepopulatedAdmin(admin
.ModelAdmin
):
556 inlines
= [RelatedPrepopulatedInline1
, RelatedPrepopulatedInline2
]
559 'fields': (('pubdate', 'status'), ('name', 'slug1', 'slug2',),)
562 prepopulated_fields
= {'slug1': ['name', 'pubdate'],
563 'slug2': ['status', 'name']}
566 class UnorderedObjectAdmin(admin
.ModelAdmin
):
567 list_display
= ['name']
568 list_editable
= ['name']
573 site
= admin
.AdminSite(name
="admin")
574 site
.register(Article
, ArticleAdmin
)
575 site
.register(CustomArticle
, CustomArticleAdmin
)
576 site
.register(Section
, save_as
=True, inlines
=[ArticleInline
])
577 site
.register(ModelWithStringPrimaryKey
)
579 site
.register(Thing
, ThingAdmin
)
581 site
.register(Inquisition
, InquisitionAdmin
)
582 site
.register(Sketch
, SketchAdmin
)
583 site
.register(Person
, PersonAdmin
)
584 site
.register(Persona
, PersonaAdmin
)
585 site
.register(Subscriber
, SubscriberAdmin
)
586 site
.register(ExternalSubscriber
, ExternalSubscriberAdmin
)
587 site
.register(OldSubscriber
, OldSubscriberAdmin
)
588 site
.register(Podcast
, PodcastAdmin
)
589 site
.register(Vodcast
, VodcastAdmin
)
590 site
.register(Parent
, ParentAdmin
)
591 site
.register(EmptyModel
, EmptyModelAdmin
)
592 site
.register(Fabric
, FabricAdmin
)
593 site
.register(Gallery
, GalleryAdmin
)
594 site
.register(Picture
, PictureAdmin
)
595 site
.register(Language
, LanguageAdmin
)
596 site
.register(Recommendation
, RecommendationAdmin
)
597 site
.register(Recommender
)
598 site
.register(Collector
, CollectorAdmin
)
599 site
.register(Category
, CategoryAdmin
)
600 site
.register(Post
, PostAdmin
)
601 site
.register(Gadget
, GadgetAdmin
)
602 site
.register(Villain
)
603 site
.register(SuperVillain
)
605 site
.register(PlotDetails
)
606 site
.register(CyclicOne
)
607 site
.register(CyclicTwo
)
608 site
.register(WorkHour
, WorkHourAdmin
)
609 site
.register(Reservation
)
610 site
.register(FoodDelivery
, FoodDeliveryAdmin
)
611 site
.register(RowLevelChangePermissionModel
, RowLevelChangePermissionModelAdmin
)
612 site
.register(Paper
, PaperAdmin
)
613 site
.register(CoverLetter
, CoverLetterAdmin
)
614 site
.register(Story
, StoryAdmin
)
615 site
.register(OtherStory
, OtherStoryAdmin
)
616 site
.register(Report
, ReportAdmin
)
617 site
.register(MainPrepopulated
, MainPrepopulatedAdmin
)
618 site
.register(UnorderedObject
, UnorderedObjectAdmin
)
620 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
621 # That way we cover all four cases:
622 # related ForeignKey object registered in admin
623 # related ForeignKey object not registered in admin
624 # related OneToOne object registered in admin
625 # related OneToOne object not registered in admin
626 # when deleting Book so as exercise all four troublesome (w.r.t escaping
627 # and calling force_unicode to avoid problems on Python 2.3) paths through
628 # contrib.admin.util's get_deleted_objects function.
629 site
.register(Book
, inlines
=[ChapterInline
])
631 site
.register(ChapterXtra1
, ChapterXtra1Admin
)
632 site
.register(Pizza
, PizzaAdmin
)
633 site
.register(Topping
)
634 site
.register(Album
, AlbumAdmin
)
635 site
.register(Question
)
636 site
.register(Answer
)
637 site
.register(PrePopulatedPost
, PrePopulatedPostAdmin
)
638 site
.register(ComplexSortedPerson
, ComplexSortedPersonAdmin
)
639 site
.register(PrePopulatedPostLargeSlug
, PrePopulatedPostLargeSlugAdmin
)
640 site
.register(AdminOrderedField
, AdminOrderedFieldAdmin
)
641 site
.register(AdminOrderedModelMethod
, AdminOrderedModelMethodAdmin
)
642 site
.register(AdminOrderedAdminMethod
, AdminOrderedAdminMethodAdmin
)
643 site
.register(AdminOrderedCallable
, AdminOrderedCallableAdmin
)
644 site
.register(Color2
, CustomTemplateFilterColorAdmin
)
646 # Register core models we need in our tests
647 from django
.contrib
.auth
.models
import User
, Group
648 from django
.contrib
.auth
.admin
import UserAdmin
, GroupAdmin
649 site
.register(User
, UserAdmin
)
650 site
.register(Group
, GroupAdmin
)