parts/django/tests/regressiontests/admin_views/models.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 # -*- coding: utf-8 -*-
       
     2 import datetime
       
     3 import tempfile
       
     4 import os
       
     5 
       
     6 from django.contrib import admin
       
     7 from django.core.files.storage import FileSystemStorage
       
     8 from django.contrib.admin.views.main import ChangeList
       
     9 from django.core.mail import EmailMessage
       
    10 from django.db import models
       
    11 from django import forms
       
    12 from django.forms.models import BaseModelFormSet
       
    13 from django.contrib.auth.models import User
       
    14 from django.contrib.contenttypes import generic
       
    15 from django.contrib.contenttypes.models import ContentType
       
    16 
       
    17 class Section(models.Model):
       
    18     """
       
    19     A simple section that links to articles, to test linking to related items
       
    20     in admin views.
       
    21     """
       
    22     name = models.CharField(max_length=100)
       
    23 
       
    24 class Article(models.Model):
       
    25     """
       
    26     A simple article to test admin views. Test backwards compatibility.
       
    27     """
       
    28     title = models.CharField(max_length=100)
       
    29     content = models.TextField()
       
    30     date = models.DateTimeField()
       
    31     section = models.ForeignKey(Section, null=True, blank=True)
       
    32 
       
    33     def __unicode__(self):
       
    34         return self.title
       
    35 
       
    36     def model_year(self):
       
    37         return self.date.year
       
    38     model_year.admin_order_field = 'date'
       
    39     model_year.short_description = ''
       
    40 
       
    41 class Book(models.Model):
       
    42     """
       
    43     A simple book that has chapters.
       
    44     """
       
    45     name = models.CharField(max_length=100, verbose_name=u'¿Name?')
       
    46 
       
    47     def __unicode__(self):
       
    48         return self.name
       
    49 
       
    50 class Promo(models.Model):
       
    51     name = models.CharField(max_length=100, verbose_name=u'¿Name?')
       
    52     book = models.ForeignKey(Book)
       
    53 
       
    54     def __unicode__(self):
       
    55         return self.name
       
    56 
       
    57 class Chapter(models.Model):
       
    58     title = models.CharField(max_length=100, verbose_name=u'¿Title?')
       
    59     content = models.TextField()
       
    60     book = models.ForeignKey(Book)
       
    61 
       
    62     def __unicode__(self):
       
    63         return self.title
       
    64 
       
    65     class Meta:
       
    66         # Use a utf-8 bytestring to ensure it works (see #11710)
       
    67         verbose_name = '¿Chapter?'
       
    68 
       
    69 class ChapterXtra1(models.Model):
       
    70     chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?')
       
    71     xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
       
    72 
       
    73     def __unicode__(self):
       
    74         return u'¿Xtra1: %s' % self.xtra
       
    75 
       
    76 class ChapterXtra2(models.Model):
       
    77     chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?')
       
    78     xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
       
    79 
       
    80     def __unicode__(self):
       
    81         return u'¿Xtra2: %s' % self.xtra
       
    82 
       
    83 def callable_year(dt_value):
       
    84     return dt_value.year
       
    85 callable_year.admin_order_field = 'date'
       
    86 
       
    87 class ArticleInline(admin.TabularInline):
       
    88     model = Article
       
    89 
       
    90 class ChapterInline(admin.TabularInline):
       
    91     model = Chapter
       
    92 
       
    93 class ArticleAdmin(admin.ModelAdmin):
       
    94     list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year')
       
    95     list_filter = ('date', 'section')
       
    96 
       
    97     def changelist_view(self, request):
       
    98         "Test that extra_context works"
       
    99         return super(ArticleAdmin, self).changelist_view(
       
   100             request, extra_context={
       
   101                 'extra_var': 'Hello!'
       
   102             }
       
   103         )
       
   104 
       
   105     def modeladmin_year(self, obj):
       
   106         return obj.date.year
       
   107     modeladmin_year.admin_order_field = 'date'
       
   108     modeladmin_year.short_description = None
       
   109 
       
   110 class CustomArticle(models.Model):
       
   111     content = models.TextField()
       
   112     date = models.DateTimeField()
       
   113 
       
   114 class CustomArticleAdmin(admin.ModelAdmin):
       
   115     """
       
   116     Tests various hooks for using custom templates and contexts.
       
   117     """
       
   118     change_list_template = 'custom_admin/change_list.html'
       
   119     change_form_template = 'custom_admin/change_form.html'
       
   120     add_form_template = 'custom_admin/add_form.html'
       
   121     object_history_template = 'custom_admin/object_history.html'
       
   122     delete_confirmation_template = 'custom_admin/delete_confirmation.html'
       
   123     delete_selected_confirmation_template = 'custom_admin/delete_selected_confirmation.html'
       
   124 
       
   125     def changelist_view(self, request):
       
   126         "Test that extra_context works"
       
   127         return super(CustomArticleAdmin, self).changelist_view(
       
   128             request, extra_context={
       
   129                 'extra_var': 'Hello!'
       
   130             }
       
   131         )
       
   132 
       
   133 class ModelWithStringPrimaryKey(models.Model):
       
   134     id = models.CharField(max_length=255, primary_key=True)
       
   135 
       
   136     def __unicode__(self):
       
   137         return self.id
       
   138 
       
   139 class Color(models.Model):
       
   140     value = models.CharField(max_length=10)
       
   141     warm = models.BooleanField()
       
   142     def __unicode__(self):
       
   143         return self.value
       
   144 
       
   145 class Thing(models.Model):
       
   146     title = models.CharField(max_length=20)
       
   147     color = models.ForeignKey(Color, limit_choices_to={'warm': True})
       
   148     def __unicode__(self):
       
   149         return self.title
       
   150 
       
   151 class ThingAdmin(admin.ModelAdmin):
       
   152     list_filter = ('color',)
       
   153 
       
   154 class Fabric(models.Model):
       
   155     NG_CHOICES = (
       
   156         ('Textured', (
       
   157                 ('x', 'Horizontal'),
       
   158                 ('y', 'Vertical'),
       
   159             )
       
   160         ),
       
   161         ('plain', 'Smooth'),
       
   162     )
       
   163     surface = models.CharField(max_length=20, choices=NG_CHOICES)
       
   164 
       
   165 class FabricAdmin(admin.ModelAdmin):
       
   166     list_display = ('surface',)
       
   167     list_filter = ('surface',)
       
   168 
       
   169 class Person(models.Model):
       
   170     GENDER_CHOICES = (
       
   171         (1, "Male"),
       
   172         (2, "Female"),
       
   173     )
       
   174     name = models.CharField(max_length=100)
       
   175     gender = models.IntegerField(choices=GENDER_CHOICES)
       
   176     alive = models.BooleanField()
       
   177 
       
   178     def __unicode__(self):
       
   179         return self.name
       
   180 
       
   181     class Meta:
       
   182         ordering = ["id"]
       
   183 
       
   184 class BasePersonModelFormSet(BaseModelFormSet):
       
   185     def clean(self):
       
   186         for person_dict in self.cleaned_data:
       
   187             person = person_dict.get('id')
       
   188             alive = person_dict.get('alive')
       
   189             if person and alive and person.name == "Grace Hopper":
       
   190                 raise forms.ValidationError, "Grace is not a Zombie"
       
   191 
       
   192 class PersonAdmin(admin.ModelAdmin):
       
   193     list_display = ('name', 'gender', 'alive')
       
   194     list_editable = ('gender', 'alive')
       
   195     list_filter = ('gender',)
       
   196     search_fields = (u'name',)
       
   197     ordering = ["id"]
       
   198     save_as = True
       
   199 
       
   200     def get_changelist_formset(self, request, **kwargs):
       
   201         return super(PersonAdmin, self).get_changelist_formset(request,
       
   202             formset=BasePersonModelFormSet, **kwargs)
       
   203 
       
   204 
       
   205 class Persona(models.Model):
       
   206     """
       
   207     A simple persona associated with accounts, to test inlining of related
       
   208     accounts which inherit from a common accounts class.
       
   209     """
       
   210     name = models.CharField(blank=False,  max_length=80)
       
   211     def __unicode__(self):
       
   212         return self.name
       
   213 
       
   214 class Account(models.Model):
       
   215     """
       
   216     A simple, generic account encapsulating the information shared by all
       
   217     types of accounts.
       
   218     """
       
   219     username = models.CharField(blank=False,  max_length=80)
       
   220     persona = models.ForeignKey(Persona, related_name="accounts")
       
   221     servicename = u'generic service'
       
   222 
       
   223     def __unicode__(self):
       
   224         return "%s: %s" % (self.servicename, self.username)
       
   225 
       
   226 class FooAccount(Account):
       
   227     """A service-specific account of type Foo."""
       
   228     servicename = u'foo'
       
   229 
       
   230 class BarAccount(Account):
       
   231     """A service-specific account of type Bar."""
       
   232     servicename = u'bar'
       
   233 
       
   234 class FooAccountAdmin(admin.StackedInline):
       
   235     model = FooAccount
       
   236     extra = 1
       
   237 
       
   238 class BarAccountAdmin(admin.StackedInline):
       
   239     model = BarAccount
       
   240     extra = 1
       
   241 
       
   242 class PersonaAdmin(admin.ModelAdmin):
       
   243     inlines = (
       
   244         FooAccountAdmin,
       
   245         BarAccountAdmin
       
   246     )
       
   247 
       
   248 class Subscriber(models.Model):
       
   249     name = models.CharField(blank=False, max_length=80)
       
   250     email = models.EmailField(blank=False, max_length=175)
       
   251 
       
   252     def __unicode__(self):
       
   253         return "%s (%s)" % (self.name, self.email)
       
   254 
       
   255 class SubscriberAdmin(admin.ModelAdmin):
       
   256     actions = ['mail_admin']
       
   257 
       
   258     def mail_admin(self, request, selected):
       
   259         EmailMessage(
       
   260             'Greetings from a ModelAdmin action',
       
   261             'This is the test email from a admin action',
       
   262             'from@example.com',
       
   263             ['to@example.com']
       
   264         ).send()
       
   265 
       
   266 class ExternalSubscriber(Subscriber):
       
   267     pass
       
   268 
       
   269 class OldSubscriber(Subscriber):
       
   270     pass
       
   271 
       
   272 def external_mail(modeladmin, request, selected):
       
   273     EmailMessage(
       
   274         'Greetings from a function action',
       
   275         'This is the test email from a function action',
       
   276         'from@example.com',
       
   277         ['to@example.com']
       
   278     ).send()
       
   279 
       
   280 def redirect_to(modeladmin, request, selected):
       
   281     from django.http import HttpResponseRedirect
       
   282     return HttpResponseRedirect('/some-where-else/')
       
   283 
       
   284 class ExternalSubscriberAdmin(admin.ModelAdmin):
       
   285     actions = [external_mail, redirect_to]
       
   286 
       
   287 class Media(models.Model):
       
   288     name = models.CharField(max_length=60)
       
   289 
       
   290 class Podcast(Media):
       
   291     release_date = models.DateField()
       
   292 
       
   293 class PodcastAdmin(admin.ModelAdmin):
       
   294     list_display = ('name', 'release_date')
       
   295     list_editable = ('release_date',)
       
   296 
       
   297     ordering = ('name',)
       
   298 
       
   299 class Vodcast(Media):
       
   300     media = models.OneToOneField(Media, primary_key=True, parent_link=True)
       
   301     released = models.BooleanField(default=False)
       
   302 
       
   303 class VodcastAdmin(admin.ModelAdmin):
       
   304     list_display = ('name', 'released')
       
   305     list_editable = ('released',)
       
   306 
       
   307     ordering = ('name',)
       
   308 
       
   309 class Parent(models.Model):
       
   310     name = models.CharField(max_length=128)
       
   311 
       
   312 class Child(models.Model):
       
   313     parent = models.ForeignKey(Parent, editable=False)
       
   314     name = models.CharField(max_length=30, blank=True)
       
   315 
       
   316 class ChildInline(admin.StackedInline):
       
   317     model = Child
       
   318 
       
   319 class ParentAdmin(admin.ModelAdmin):
       
   320     model = Parent
       
   321     inlines = [ChildInline]
       
   322 
       
   323 class EmptyModel(models.Model):
       
   324     def __unicode__(self):
       
   325         return "Primary key = %s" % self.id
       
   326 
       
   327 class EmptyModelAdmin(admin.ModelAdmin):
       
   328     def queryset(self, request):
       
   329         return super(EmptyModelAdmin, self).queryset(request).filter(pk__gt=1)
       
   330 
       
   331 class OldSubscriberAdmin(admin.ModelAdmin):
       
   332     actions = None
       
   333 
       
   334 temp_storage = FileSystemStorage(tempfile.mkdtemp())
       
   335 UPLOAD_TO = os.path.join(temp_storage.location, 'test_upload')
       
   336 
       
   337 class Gallery(models.Model):
       
   338     name = models.CharField(max_length=100)
       
   339 
       
   340 class Picture(models.Model):
       
   341     name = models.CharField(max_length=100)
       
   342     image = models.FileField(storage=temp_storage, upload_to='test_upload')
       
   343     gallery = models.ForeignKey(Gallery, related_name="pictures")
       
   344 
       
   345 class PictureInline(admin.TabularInline):
       
   346     model = Picture
       
   347     extra = 1
       
   348 
       
   349 class GalleryAdmin(admin.ModelAdmin):
       
   350     inlines = [PictureInline]
       
   351 
       
   352 class PictureAdmin(admin.ModelAdmin):
       
   353     pass
       
   354 
       
   355 class Language(models.Model):
       
   356     iso = models.CharField(max_length=5, primary_key=True)
       
   357     name = models.CharField(max_length=50)
       
   358     english_name = models.CharField(max_length=50)
       
   359     shortlist = models.BooleanField(default=False)
       
   360 
       
   361     class Meta:
       
   362         ordering = ('iso',)
       
   363 
       
   364 class LanguageAdmin(admin.ModelAdmin):
       
   365     list_display = ['iso', 'shortlist', 'english_name', 'name']
       
   366     list_editable = ['shortlist']
       
   367 
       
   368 # a base class for Recommender and Recommendation
       
   369 class Title(models.Model):
       
   370     pass
       
   371 
       
   372 class TitleTranslation(models.Model):
       
   373     title = models.ForeignKey(Title)
       
   374     text = models.CharField(max_length=100)
       
   375 
       
   376 class Recommender(Title):
       
   377     pass
       
   378 
       
   379 class Recommendation(Title):
       
   380     recommender = models.ForeignKey(Recommender)
       
   381 
       
   382 class RecommendationAdmin(admin.ModelAdmin):
       
   383     search_fields = ('titletranslation__text', 'recommender__titletranslation__text',)
       
   384 
       
   385 class Collector(models.Model):
       
   386     name = models.CharField(max_length=100)
       
   387 
       
   388 class Widget(models.Model):
       
   389     owner = models.ForeignKey(Collector)
       
   390     name = models.CharField(max_length=100)
       
   391 
       
   392 class DooHickey(models.Model):
       
   393     code = models.CharField(max_length=10, primary_key=True)
       
   394     owner = models.ForeignKey(Collector)
       
   395     name = models.CharField(max_length=100)
       
   396 
       
   397 class Grommet(models.Model):
       
   398     code = models.AutoField(primary_key=True)
       
   399     owner = models.ForeignKey(Collector)
       
   400     name = models.CharField(max_length=100)
       
   401 
       
   402 class Whatsit(models.Model):
       
   403     index = models.IntegerField(primary_key=True)
       
   404     owner = models.ForeignKey(Collector)
       
   405     name = models.CharField(max_length=100)
       
   406 
       
   407 class Doodad(models.Model):
       
   408     name = models.CharField(max_length=100)
       
   409 
       
   410 class FancyDoodad(Doodad):
       
   411     owner = models.ForeignKey(Collector)
       
   412     expensive = models.BooleanField(default=True)
       
   413 
       
   414 class WidgetInline(admin.StackedInline):
       
   415     model = Widget
       
   416 
       
   417 class DooHickeyInline(admin.StackedInline):
       
   418     model = DooHickey
       
   419 
       
   420 class GrommetInline(admin.StackedInline):
       
   421     model = Grommet
       
   422 
       
   423 class WhatsitInline(admin.StackedInline):
       
   424     model = Whatsit
       
   425 
       
   426 class FancyDoodadInline(admin.StackedInline):
       
   427     model = FancyDoodad
       
   428 
       
   429 class Category(models.Model):
       
   430     collector = models.ForeignKey(Collector)
       
   431     order = models.PositiveIntegerField()
       
   432 
       
   433     class Meta:
       
   434         ordering = ('order',)
       
   435 
       
   436     def __unicode__(self):
       
   437         return u'%s:o%s' % (self.id, self.order)
       
   438 
       
   439 class CategoryAdmin(admin.ModelAdmin):
       
   440     list_display = ('id', 'collector', 'order')
       
   441     list_editable = ('order',)
       
   442 
       
   443 class CategoryInline(admin.StackedInline):
       
   444     model = Category
       
   445 
       
   446 class CollectorAdmin(admin.ModelAdmin):
       
   447     inlines = [
       
   448         WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline,
       
   449         FancyDoodadInline, CategoryInline
       
   450     ]
       
   451 
       
   452 class Link(models.Model):
       
   453     posted = models.DateField(
       
   454         default=lambda: datetime.date.today() - datetime.timedelta(days=7)
       
   455     )
       
   456     url = models.URLField()
       
   457     post = models.ForeignKey("Post")
       
   458 
       
   459 
       
   460 class LinkInline(admin.TabularInline):
       
   461     model = Link
       
   462     extra = 1
       
   463 
       
   464     readonly_fields = ("posted",)
       
   465 
       
   466 
       
   467 class Post(models.Model):
       
   468     title = models.CharField(max_length=100)
       
   469     content = models.TextField()
       
   470     posted = models.DateField(default=datetime.date.today)
       
   471     public = models.NullBooleanField()
       
   472 
       
   473     def awesomeness_level(self):
       
   474         return "Very awesome."
       
   475 
       
   476 class PostAdmin(admin.ModelAdmin):
       
   477     list_display = ['title', 'public']
       
   478     readonly_fields = ('posted', 'awesomeness_level', 'coolness', 'value', lambda obj: "foo")
       
   479 
       
   480     inlines = [
       
   481         LinkInline
       
   482     ]
       
   483 
       
   484     def coolness(self, instance):
       
   485         if instance.pk:
       
   486             return "%d amount of cool." % instance.pk
       
   487         else:
       
   488             return "Unkown coolness."
       
   489 
       
   490     def value(self, instance):
       
   491         return 1000
       
   492     value.short_description = 'Value in $US'
       
   493 
       
   494 class Gadget(models.Model):
       
   495     name = models.CharField(max_length=100)
       
   496 
       
   497     def __unicode__(self):
       
   498         return self.name
       
   499 
       
   500 class CustomChangeList(ChangeList):
       
   501     def get_query_set(self):
       
   502         return self.root_query_set.filter(pk=9999) # Does not exist
       
   503 
       
   504 class GadgetAdmin(admin.ModelAdmin):
       
   505     def get_changelist(self, request, **kwargs):
       
   506         return CustomChangeList
       
   507 
       
   508 class Villain(models.Model):
       
   509     name = models.CharField(max_length=100)
       
   510 
       
   511     def __unicode__(self):
       
   512         return self.name
       
   513 
       
   514 class SuperVillain(Villain):
       
   515     pass
       
   516 
       
   517 class FunkyTag(models.Model):
       
   518     "Because we all know there's only one real use case for GFKs."
       
   519     name = models.CharField(max_length=25)
       
   520     content_type = models.ForeignKey(ContentType)
       
   521     object_id = models.PositiveIntegerField()
       
   522     content_object = generic.GenericForeignKey('content_type', 'object_id')
       
   523 
       
   524     def __unicode__(self):
       
   525         return self.name
       
   526 
       
   527 class Plot(models.Model):
       
   528     name = models.CharField(max_length=100)
       
   529     team_leader = models.ForeignKey(Villain, related_name='lead_plots')
       
   530     contact = models.ForeignKey(Villain, related_name='contact_plots')
       
   531     tags = generic.GenericRelation(FunkyTag)
       
   532 
       
   533     def __unicode__(self):
       
   534         return self.name
       
   535 
       
   536 class PlotDetails(models.Model):
       
   537     details = models.CharField(max_length=100)
       
   538     plot = models.OneToOneField(Plot)
       
   539 
       
   540     def __unicode__(self):
       
   541         return self.details
       
   542 
       
   543 class SecretHideout(models.Model):
       
   544     """ Secret! Not registered with the admin! """
       
   545     location = models.CharField(max_length=100)
       
   546     villain = models.ForeignKey(Villain)
       
   547 
       
   548     def __unicode__(self):
       
   549         return self.location
       
   550 
       
   551 class SuperSecretHideout(models.Model):
       
   552     """ Secret! Not registered with the admin! """
       
   553     location = models.CharField(max_length=100)
       
   554     supervillain = models.ForeignKey(SuperVillain)
       
   555 
       
   556     def __unicode__(self):
       
   557         return self.location
       
   558 
       
   559 class CyclicOne(models.Model):
       
   560     name = models.CharField(max_length=25)
       
   561     two = models.ForeignKey('CyclicTwo')
       
   562 
       
   563     def __unicode__(self):
       
   564         return self.name
       
   565 
       
   566 class CyclicTwo(models.Model):
       
   567     name = models.CharField(max_length=25)
       
   568     one = models.ForeignKey(CyclicOne)
       
   569 
       
   570     def __unicode__(self):
       
   571         return self.name
       
   572 
       
   573 class Topping(models.Model):
       
   574     name = models.CharField(max_length=20)
       
   575 
       
   576 class Pizza(models.Model):
       
   577     name = models.CharField(max_length=20)
       
   578     toppings = models.ManyToManyField('Topping')
       
   579 
       
   580 class PizzaAdmin(admin.ModelAdmin):
       
   581     readonly_fields = ('toppings',)
       
   582 
       
   583 class Album(models.Model):
       
   584     owner = models.ForeignKey(User)
       
   585     title = models.CharField(max_length=30)
       
   586 
       
   587 class AlbumAdmin(admin.ModelAdmin):
       
   588     list_filter = ['title']
       
   589 
       
   590 admin.site.register(Article, ArticleAdmin)
       
   591 admin.site.register(CustomArticle, CustomArticleAdmin)
       
   592 admin.site.register(Section, save_as=True, inlines=[ArticleInline])
       
   593 admin.site.register(ModelWithStringPrimaryKey)
       
   594 admin.site.register(Color)
       
   595 admin.site.register(Thing, ThingAdmin)
       
   596 admin.site.register(Person, PersonAdmin)
       
   597 admin.site.register(Persona, PersonaAdmin)
       
   598 admin.site.register(Subscriber, SubscriberAdmin)
       
   599 admin.site.register(ExternalSubscriber, ExternalSubscriberAdmin)
       
   600 admin.site.register(OldSubscriber, OldSubscriberAdmin)
       
   601 admin.site.register(Podcast, PodcastAdmin)
       
   602 admin.site.register(Vodcast, VodcastAdmin)
       
   603 admin.site.register(Parent, ParentAdmin)
       
   604 admin.site.register(EmptyModel, EmptyModelAdmin)
       
   605 admin.site.register(Fabric, FabricAdmin)
       
   606 admin.site.register(Gallery, GalleryAdmin)
       
   607 admin.site.register(Picture, PictureAdmin)
       
   608 admin.site.register(Language, LanguageAdmin)
       
   609 admin.site.register(Recommendation, RecommendationAdmin)
       
   610 admin.site.register(Recommender)
       
   611 admin.site.register(Collector, CollectorAdmin)
       
   612 admin.site.register(Category, CategoryAdmin)
       
   613 admin.site.register(Post, PostAdmin)
       
   614 admin.site.register(Gadget, GadgetAdmin)
       
   615 admin.site.register(Villain)
       
   616 admin.site.register(SuperVillain)
       
   617 admin.site.register(Plot)
       
   618 admin.site.register(PlotDetails)
       
   619 admin.site.register(CyclicOne)
       
   620 admin.site.register(CyclicTwo)
       
   621 
       
   622 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
       
   623 # That way we cover all four cases:
       
   624 #     related ForeignKey object registered in admin
       
   625 #     related ForeignKey object not registered in admin
       
   626 #     related OneToOne object registered in admin
       
   627 #     related OneToOne object not registered in admin
       
   628 # when deleting Book so as exercise all four troublesome (w.r.t escaping
       
   629 # and calling force_unicode to avoid problems on Python 2.3) paths through
       
   630 # contrib.admin.util's get_deleted_objects function.
       
   631 admin.site.register(Book, inlines=[ChapterInline])
       
   632 admin.site.register(Promo)
       
   633 admin.site.register(ChapterXtra1)
       
   634 admin.site.register(Pizza, PizzaAdmin)
       
   635 admin.site.register(Topping)
       
   636 admin.site.register(Album, AlbumAdmin)