diff options
Diffstat (limited to 'tests/admin_views/admin.py')
-rw-r--r-- | tests/admin_views/admin.py | 750 |
1 files changed, 750 insertions, 0 deletions
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py new file mode 100644 index 0000000000..d4348968e0 --- /dev/null +++ b/tests/admin_views/admin.py @@ -0,0 +1,750 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, unicode_literals + +import tempfile +import os + +from django import forms +from django.contrib import admin +from django.contrib.admin.views.main import ChangeList +from django.core.files.storage import FileSystemStorage +from django.core.mail import EmailMessage +from django.conf.urls import patterns, url +from django.db import models +from django.forms.models import BaseModelFormSet +from django.http import HttpResponse +from django.contrib.admin import BooleanFieldListFilter + +from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, + Widget, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link, + PrePopulatedPost, PrePopulatedSubPost, CustomArticle, Section, + ModelWithStringPrimaryKey, Color, Thing, Actor, Inquisition, Sketch, Person, + Persona, Subscriber, ExternalSubscriber, OldSubscriber, Vodcast, EmptyModel, + Fabric, Gallery, Language, Recommendation, Recommender, Collector, Post, + Gadget, Villain, SuperVillain, Plot, PlotDetails, CyclicOne, CyclicTwo, + WorkHour, Reservation, FoodDelivery, RowLevelChangePermissionModel, Paper, + CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, + Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, + AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, + AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated, + RelatedPrepopulated, UndeletableObject, UserMessenger, Simple, Choice, + ShortMessage, Telegram) + + +def callable_year(dt_value): + try: + return dt_value.year + except AttributeError: + return None +callable_year.admin_order_field = 'date' + + +class ArticleInline(admin.TabularInline): + model = Article + prepopulated_fields = { + 'title' : ('content',) + } + fieldsets=( + ('Some fields', { + 'classes': ('collapse',), + 'fields': ('title', 'content') + }), + ('Some other fields', { + 'classes': ('wide',), + 'fields': ('date', 'section') + }) + ) + +class ChapterInline(admin.TabularInline): + model = Chapter + + +class ChapterXtra1Admin(admin.ModelAdmin): + list_filter = ('chap', + 'chap__title', + 'chap__book', + 'chap__book__name', + 'chap__book__promo', + 'chap__book__promo__name',) + + +class ArticleAdmin(admin.ModelAdmin): + list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year') + list_filter = ('date', 'section') + fieldsets=( + ('Some fields', { + 'classes': ('collapse',), + 'fields': ('title', 'content') + }), + ('Some other fields', { + 'classes': ('wide',), + 'fields': ('date', 'section') + }) + ) + + def changelist_view(self, request): + "Test that extra_context works" + return super(ArticleAdmin, self).changelist_view( + request, extra_context={ + 'extra_var': 'Hello!' + } + ) + + def modeladmin_year(self, obj): + return obj.date.year + modeladmin_year.admin_order_field = 'date' + modeladmin_year.short_description = None + + def delete_model(self, request, obj): + EmailMessage( + 'Greetings from a deleted object', + 'I hereby inform you that some user deleted me', + 'from@example.com', + ['to@example.com'] + ).send() + return super(ArticleAdmin, self).delete_model(request, obj) + + def save_model(self, request, obj, form, change=True): + EmailMessage( + 'Greetings from a created object', + 'I hereby inform you that some user created me', + 'from@example.com', + ['to@example.com'] + ).send() + return super(ArticleAdmin, self).save_model(request, obj, form, change) + + +class RowLevelChangePermissionModelAdmin(admin.ModelAdmin): + def has_change_permission(self, request, obj=None): + """ Only allow changing objects with even id number """ + return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0) + + +class CustomArticleAdmin(admin.ModelAdmin): + """ + Tests various hooks for using custom templates and contexts. + """ + change_list_template = 'custom_admin/change_list.html' + change_form_template = 'custom_admin/change_form.html' + add_form_template = 'custom_admin/add_form.html' + object_history_template = 'custom_admin/object_history.html' + delete_confirmation_template = 'custom_admin/delete_confirmation.html' + delete_selected_confirmation_template = 'custom_admin/delete_selected_confirmation.html' + + def changelist_view(self, request): + "Test that extra_context works" + return super(CustomArticleAdmin, self).changelist_view( + request, extra_context={ + 'extra_var': 'Hello!' + } + ) + + +class ThingAdmin(admin.ModelAdmin): + list_filter = ('color__warm', 'color__value', 'pub_date',) + + +class InquisitionAdmin(admin.ModelAdmin): + list_display = ('leader', 'country', 'expected') + + +class SketchAdmin(admin.ModelAdmin): + raw_id_fields = ('inquisition',) + + +class FabricAdmin(admin.ModelAdmin): + list_display = ('surface',) + list_filter = ('surface',) + + +class BasePersonModelFormSet(BaseModelFormSet): + def clean(self): + for person_dict in self.cleaned_data: + person = person_dict.get('id') + alive = person_dict.get('alive') + if person and alive and person.name == "Grace Hopper": + raise forms.ValidationError("Grace is not a Zombie") + + +class PersonAdmin(admin.ModelAdmin): + list_display = ('name', 'gender', 'alive') + list_editable = ('gender', 'alive') + list_filter = ('gender',) + search_fields = ('^name',) + save_as = True + + def get_changelist_formset(self, request, **kwargs): + return super(PersonAdmin, self).get_changelist_formset(request, + formset=BasePersonModelFormSet, **kwargs) + + def queryset(self, request): + # Order by a field that isn't in list display, to be able to test + # whether ordering is preserved. + return super(PersonAdmin, self).queryset(request).order_by('age') + + +class FooAccount(Account): + """A service-specific account of type Foo.""" + servicename = 'foo' + + +class BarAccount(Account): + """A service-specific account of type Bar.""" + servicename = 'bar' + + +class FooAccountAdmin(admin.StackedInline): + model = FooAccount + extra = 1 + + +class BarAccountAdmin(admin.StackedInline): + model = BarAccount + extra = 1 + + +class PersonaAdmin(admin.ModelAdmin): + inlines = ( + FooAccountAdmin, + BarAccountAdmin + ) + + +class SubscriberAdmin(admin.ModelAdmin): + actions = ['mail_admin'] + + def mail_admin(self, request, selected): + EmailMessage( + 'Greetings from a ModelAdmin action', + 'This is the test email from a admin action', + 'from@example.com', + ['to@example.com'] + ).send() + + +def external_mail(modeladmin, request, selected): + EmailMessage( + 'Greetings from a function action', + 'This is the test email from a function action', + 'from@example.com', + ['to@example.com'] + ).send() +external_mail.short_description = 'External mail (Another awesome action)' + + +def redirect_to(modeladmin, request, selected): + from django.http import HttpResponseRedirect + return HttpResponseRedirect('/some-where-else/') +redirect_to.short_description = 'Redirect to (Awesome action)' + + +class ExternalSubscriberAdmin(admin.ModelAdmin): + actions = [redirect_to, external_mail] + + +class Podcast(Media): + release_date = models.DateField() + + class Meta: + ordering = ('release_date',) # overridden in PodcastAdmin + + +class PodcastAdmin(admin.ModelAdmin): + list_display = ('name', 'release_date') + list_editable = ('release_date',) + date_hierarchy = 'release_date' + ordering = ('name',) + + +class VodcastAdmin(admin.ModelAdmin): + list_display = ('name', 'released') + list_editable = ('released',) + + ordering = ('name',) + + +class ChildInline(admin.StackedInline): + model = Child + + +class ParentAdmin(admin.ModelAdmin): + model = Parent + inlines = [ChildInline] + + list_editable = ('name',) + + def save_related(self, request, form, formsets, change): + super(ParentAdmin, self).save_related(request, form, formsets, change) + first_name, last_name = form.instance.name.split() + for child in form.instance.child_set.all(): + if len(child.name.split()) < 2: + child.name = child.name + ' ' + last_name + child.save() + + +class EmptyModelAdmin(admin.ModelAdmin): + def queryset(self, request): + return super(EmptyModelAdmin, self).queryset(request).filter(pk__gt=1) + + +class OldSubscriberAdmin(admin.ModelAdmin): + actions = None + + +temp_storage = FileSystemStorage(tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR'])) +UPLOAD_TO = os.path.join(temp_storage.location, 'test_upload') + + +class PictureInline(admin.TabularInline): + model = Picture + extra = 1 + + +class GalleryAdmin(admin.ModelAdmin): + inlines = [PictureInline] + + +class PictureAdmin(admin.ModelAdmin): + pass + + +class LanguageAdmin(admin.ModelAdmin): + list_display = ['iso', 'shortlist', 'english_name', 'name'] + list_editable = ['shortlist'] + + +class RecommendationAdmin(admin.ModelAdmin): + search_fields = ('=titletranslation__text', '=recommender__titletranslation__text',) + + +class WidgetInline(admin.StackedInline): + model = Widget + + +class DooHickeyInline(admin.StackedInline): + model = DooHickey + + +class GrommetInline(admin.StackedInline): + model = Grommet + + +class WhatsitInline(admin.StackedInline): + model = Whatsit + + +class FancyDoodadInline(admin.StackedInline): + model = FancyDoodad + + +class CategoryAdmin(admin.ModelAdmin): + list_display = ('id', 'collector', 'order') + list_editable = ('order',) + + +class CategoryInline(admin.StackedInline): + model = Category + + +class CollectorAdmin(admin.ModelAdmin): + inlines = [ + WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, + FancyDoodadInline, CategoryInline + ] + + +class LinkInline(admin.TabularInline): + model = Link + extra = 1 + + readonly_fields = ("posted", "multiline") + + def multiline(self, instance): + return "InlineMultiline\ntest\nstring" + + +class SubPostInline(admin.TabularInline): + model = PrePopulatedSubPost + + prepopulated_fields = { + 'subslug' : ('subtitle',) + } + + def get_readonly_fields(self, request, obj=None): + if obj and obj.published: + return ('subslug',) + return self.readonly_fields + + def get_prepopulated_fields(self, request, obj=None): + if obj and obj.published: + return {} + return self.prepopulated_fields + + +class PrePopulatedPostAdmin(admin.ModelAdmin): + list_display = ['title', 'slug'] + prepopulated_fields = { + 'slug' : ('title',) + } + + inlines = [SubPostInline] + + def get_readonly_fields(self, request, obj=None): + if obj and obj.published: + return ('slug',) + return self.readonly_fields + + def get_prepopulated_fields(self, request, obj=None): + if obj and obj.published: + return {} + return self.prepopulated_fields + + +class PostAdmin(admin.ModelAdmin): + list_display = ['title', 'public'] + readonly_fields = ( + 'posted', 'awesomeness_level', 'coolness', 'value', 'multiline', + lambda obj: "foo" + ) + + inlines = [ + LinkInline + ] + + def coolness(self, instance): + if instance.pk: + return "%d amount of cool." % instance.pk + else: + return "Unkown coolness." + + def value(self, instance): + return 1000 + + def multiline(self, instance): + return "Multiline\ntest\nstring" + + value.short_description = 'Value in $US' + + +class CustomChangeList(ChangeList): + def get_query_set(self, request): + return self.root_query_set.filter(pk=9999) # Does not exist + + +class GadgetAdmin(admin.ModelAdmin): + def get_changelist(self, request, **kwargs): + return CustomChangeList + + +class PizzaAdmin(admin.ModelAdmin): + readonly_fields = ('toppings',) + + +class WorkHourAdmin(admin.ModelAdmin): + list_display = ('datum', 'employee') + list_filter = ('employee',) + + +class FoodDeliveryAdmin(admin.ModelAdmin): + list_display=('reference', 'driver', 'restaurant') + list_editable = ('driver', 'restaurant') + + +class CoverLetterAdmin(admin.ModelAdmin): + """ + A ModelAdmin with a custom queryset() method that uses defer(), to test + verbose_name display in messages shown after adding/editing CoverLetter + instances. + Note that the CoverLetter model defines a __unicode__ method. + For testing fix for ticket #14529. + """ + + def queryset(self, request): + return super(CoverLetterAdmin, self).queryset(request).defer('date_written') + + +class PaperAdmin(admin.ModelAdmin): + """ + A ModelAdmin with a custom queryset() method that uses only(), to test + verbose_name display in messages shown after adding/editing Paper + instances. + For testing fix for ticket #14529. + """ + + def queryset(self, request): + return super(PaperAdmin, self).queryset(request).only('title') + + +class ShortMessageAdmin(admin.ModelAdmin): + """ + A ModelAdmin with a custom queryset() method that uses defer(), to test + verbose_name display in messages shown after adding/editing ShortMessage + instances. + For testing fix for ticket #14529. + """ + + def queryset(self, request): + return super(ShortMessageAdmin, self).queryset(request).defer('timestamp') + + +class TelegramAdmin(admin.ModelAdmin): + """ + A ModelAdmin with a custom queryset() method that uses only(), to test + verbose_name display in messages shown after adding/editing Telegram + instances. + Note that the Telegram model defines a __unicode__ method. + For testing fix for ticket #14529. + """ + + def queryset(self, request): + return super(TelegramAdmin, self).queryset(request).only('title') + + +class StoryForm(forms.ModelForm): + class Meta: + widgets = {'title': forms.HiddenInput} + + +class StoryAdmin(admin.ModelAdmin): + list_display = ('id', 'title', 'content') + list_display_links = ('title',) # 'id' not in list_display_links + list_editable = ('content', ) + form = StoryForm + ordering = ["-pk"] + + +class OtherStoryAdmin(admin.ModelAdmin): + list_display = ('id', 'title', 'content') + list_display_links = ('title', 'id') # 'id' in list_display_links + list_editable = ('content', ) + ordering = ["-pk"] + + +class ComplexSortedPersonAdmin(admin.ModelAdmin): + list_display = ('name', 'age', 'is_employee', 'colored_name') + ordering = ('name',) + + def colored_name(self, obj): + return '<span style="color: #%s;">%s</span>' % ('ff00ff', obj.name) + colored_name.allow_tags = True + colored_name.admin_order_field = 'name' + + +class AlbumAdmin(admin.ModelAdmin): + list_filter = ['title'] + + +class WorkHourAdmin(admin.ModelAdmin): + list_display = ('datum', 'employee') + list_filter = ('employee',) + + +class PrePopulatedPostLargeSlugAdmin(admin.ModelAdmin): + prepopulated_fields = { + 'slug' : ('title',) + } + + +class AdminOrderedFieldAdmin(admin.ModelAdmin): + ordering = ('order',) + list_display = ('stuff', 'order') + +class AdminOrderedModelMethodAdmin(admin.ModelAdmin): + ordering = ('order',) + list_display = ('stuff', 'some_order') + +class AdminOrderedAdminMethodAdmin(admin.ModelAdmin): + def some_admin_order(self, obj): + return obj.order + some_admin_order.admin_order_field = 'order' + ordering = ('order',) + list_display = ('stuff', 'some_admin_order') + +def admin_ordered_callable(obj): + return obj.order +admin_ordered_callable.admin_order_field = 'order' +class AdminOrderedCallableAdmin(admin.ModelAdmin): + ordering = ('order',) + list_display = ('stuff', admin_ordered_callable) + +class ReportAdmin(admin.ModelAdmin): + def extra(self, request): + return HttpResponse() + + def get_urls(self): + # Corner case: Don't call parent implementation + return patterns('', + url(r'^extra/$', + self.extra, + name='cable_extra'), + ) + + +class CustomTemplateBooleanFieldListFilter(BooleanFieldListFilter): + template = 'custom_filter_template.html' + +class CustomTemplateFilterColorAdmin(admin.ModelAdmin): + list_filter = (('warm', CustomTemplateBooleanFieldListFilter),) + + +# For Selenium Prepopulated tests ------------------------------------- +class RelatedPrepopulatedInline1(admin.StackedInline): + fieldsets = ( + (None, { + 'fields': (('pubdate', 'status'), ('name', 'slug1', 'slug2',),) + }), + ) + model = RelatedPrepopulated + extra = 1 + prepopulated_fields = {'slug1': ['name', 'pubdate'], + 'slug2': ['status', 'name']} + +class RelatedPrepopulatedInline2(admin.TabularInline): + model = RelatedPrepopulated + extra = 1 + prepopulated_fields = {'slug1': ['name', 'pubdate'], + 'slug2': ['status', 'name']} + +class MainPrepopulatedAdmin(admin.ModelAdmin): + inlines = [RelatedPrepopulatedInline1, RelatedPrepopulatedInline2] + fieldsets = ( + (None, { + 'fields': (('pubdate', 'status'), ('name', 'slug1', 'slug2',),) + }), + ) + prepopulated_fields = {'slug1': ['name', 'pubdate'], + 'slug2': ['status', 'name']} + + +class UnorderedObjectAdmin(admin.ModelAdmin): + list_display = ['name'] + list_editable = ['name'] + list_per_page = 2 + + +class UndeletableObjectAdmin(admin.ModelAdmin): + def change_view(self, *args, **kwargs): + kwargs['extra_context'] = {'show_delete': False} + return super(UndeletableObjectAdmin, self).change_view(*args, **kwargs) + + +def callable_on_unknown(obj): + return obj.unknown + + +class AttributeErrorRaisingAdmin(admin.ModelAdmin): + list_display = [callable_on_unknown, ] + +class MessageTestingAdmin(admin.ModelAdmin): + actions = ["message_debug", "message_info", "message_success", + "message_warning", "message_error", "message_extra_tags"] + + def message_debug(self, request, selected): + self.message_user(request, "Test debug", level="debug") + + def message_info(self, request, selected): + self.message_user(request, "Test info", level="info") + + def message_success(self, request, selected): + self.message_user(request, "Test success", level="success") + + def message_warning(self, request, selected): + self.message_user(request, "Test warning", level="warning") + + def message_error(self, request, selected): + self.message_user(request, "Test error", level="error") + + def message_extra_tags(self, request, selected): + self.message_user(request, "Test tags", extra_tags="extra_tag") + + +class ChoiceList(admin.ModelAdmin): + list_display = ['choice'] + readonly_fields = ['choice'] + fields = ['choice'] + + +site = admin.AdminSite(name="admin") +site.register(Article, ArticleAdmin) +site.register(CustomArticle, CustomArticleAdmin) +site.register(Section, save_as=True, inlines=[ArticleInline]) +site.register(ModelWithStringPrimaryKey) +site.register(Color) +site.register(Thing, ThingAdmin) +site.register(Actor) +site.register(Inquisition, InquisitionAdmin) +site.register(Sketch, SketchAdmin) +site.register(Person, PersonAdmin) +site.register(Persona, PersonaAdmin) +site.register(Subscriber, SubscriberAdmin) +site.register(ExternalSubscriber, ExternalSubscriberAdmin) +site.register(OldSubscriber, OldSubscriberAdmin) +site.register(Podcast, PodcastAdmin) +site.register(Vodcast, VodcastAdmin) +site.register(Parent, ParentAdmin) +site.register(EmptyModel, EmptyModelAdmin) +site.register(Fabric, FabricAdmin) +site.register(Gallery, GalleryAdmin) +site.register(Picture, PictureAdmin) +site.register(Language, LanguageAdmin) +site.register(Recommendation, RecommendationAdmin) +site.register(Recommender) +site.register(Collector, CollectorAdmin) +site.register(Category, CategoryAdmin) +site.register(Post, PostAdmin) +site.register(Gadget, GadgetAdmin) +site.register(Villain) +site.register(SuperVillain) +site.register(Plot) +site.register(PlotDetails) +site.register(CyclicOne) +site.register(CyclicTwo) +site.register(WorkHour, WorkHourAdmin) +site.register(Reservation) +site.register(FoodDelivery, FoodDeliveryAdmin) +site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin) +site.register(Paper, PaperAdmin) +site.register(CoverLetter, CoverLetterAdmin) +site.register(ShortMessage, ShortMessageAdmin) +site.register(Telegram, TelegramAdmin) +site.register(Story, StoryAdmin) +site.register(OtherStory, OtherStoryAdmin) +site.register(Report, ReportAdmin) +site.register(MainPrepopulated, MainPrepopulatedAdmin) +site.register(UnorderedObject, UnorderedObjectAdmin) +site.register(UndeletableObject, UndeletableObjectAdmin) + +# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. +# That way we cover all four cases: +# related ForeignKey object registered in admin +# related ForeignKey object not registered in admin +# related OneToOne object registered in admin +# related OneToOne object not registered in admin +# when deleting Book so as exercise all four troublesome (w.r.t escaping +# and calling force_text to avoid problems on Python 2.3) paths through +# contrib.admin.util's get_deleted_objects function. +site.register(Book, inlines=[ChapterInline]) +site.register(Promo) +site.register(ChapterXtra1, ChapterXtra1Admin) +site.register(Pizza, PizzaAdmin) +site.register(Topping) +site.register(Album, AlbumAdmin) +site.register(Question) +site.register(Answer) +site.register(PrePopulatedPost, PrePopulatedPostAdmin) +site.register(ComplexSortedPerson, ComplexSortedPersonAdmin) +site.register(PrePopulatedPostLargeSlug, PrePopulatedPostLargeSlugAdmin) +site.register(AdminOrderedField, AdminOrderedFieldAdmin) +site.register(AdminOrderedModelMethod, AdminOrderedModelMethodAdmin) +site.register(AdminOrderedAdminMethod, AdminOrderedAdminMethodAdmin) +site.register(AdminOrderedCallable, AdminOrderedCallableAdmin) +site.register(Color2, CustomTemplateFilterColorAdmin) +site.register(Simple, AttributeErrorRaisingAdmin) +site.register(UserMessenger, MessageTestingAdmin) +site.register(Choice, ChoiceList) + +# Register core models we need in our tests +from django.contrib.auth.models import User, Group +from django.contrib.auth.admin import UserAdmin, GroupAdmin +site.register(User, UserAdmin) +site.register(Group, GroupAdmin) |