diff options
| author | Unai Zalakain <unai@gisa-elkartea.org> | 2013-10-24 17:28:09 +0200 |
|---|---|---|
| committer | Simon Charette <charette.s@gmail.com> | 2013-10-29 17:10:12 -0400 |
| commit | fd219fa24c7911adab60e1f5e4fd3d7f9d82a969 (patch) | |
| tree | bebe575f54927599841c3be976eccfeeb7d84e49 /tests | |
| parent | 497930b7f69e2c298faa3f784c4523ea351e5e6f (diff) | |
Fixed #8261 -- ModelAdmin hook for customising the "show on site" button
``ModelAdmin.view_on_site`` defines wether to show a link to the object on the
admin detail page. If ``True``, cleverness (i.e. ``Model.get_absolute_url``) is
used to get the url. If it's a callable, the callable is called with the object
as the only parameter. If ``False``, not link is displayed.
With the aim of maitaining backwards compatibility, ``True`` is the default.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/admin_views/admin.py | 36 | ||||
| -rw-r--r-- | tests/admin_views/fixtures/admin-views-restaurants.xml | 63 | ||||
| -rw-r--r-- | tests/admin_views/models.py | 16 | ||||
| -rw-r--r-- | tests/admin_views/tests.py | 89 |
4 files changed, 201 insertions, 3 deletions
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 7dd6c91e71..36b35aeefe 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -31,7 +31,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated, RelatedPrepopulated, UndeletableObject, UnchangeableObject, UserMessenger, Simple, Choice, ShortMessage, Telegram, FilteredManager, EmptyModelHidden, - EmptyModelVisible, EmptyModelMixin) + EmptyModelVisible, EmptyModelMixin, State, City, Restaurant, Worker) def callable_year(dt_value): @@ -74,6 +74,7 @@ class ChapterXtra1Admin(admin.ModelAdmin): class ArticleAdmin(admin.ModelAdmin): list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year') list_filter = ('date', 'section') + view_on_site = False fieldsets = ( ('Some fields', { 'classes': ('collapse',), @@ -735,6 +736,35 @@ class EmptyModelMixinAdmin(admin.ModelAdmin): form = FormWithVisibleAndHiddenField fieldsets = EmptyModelVisibleAdmin.fieldsets +class CityInlineAdmin(admin.TabularInline): + model = City + view_on_site = False + +class StateAdmin(admin.ModelAdmin): + inlines = [CityInlineAdmin] + +class RestaurantInlineAdmin(admin.TabularInline): + model = Restaurant + view_on_site = True + +class CityAdmin(admin.ModelAdmin): + inlines = [RestaurantInlineAdmin] + view_on_site = True + +class WorkerAdmin(admin.ModelAdmin): + def view_on_site(self, obj): + return '/worker/%s/%s/' % (obj.surname, obj.name) + +class WorkerInlineAdmin(admin.TabularInline): + model = Worker + + def view_on_site(self, obj): + return '/worker_inline/%s/%s/' % (obj.surname, obj.name) + +class RestaurantAdmin(admin.ModelAdmin): + inlines = [WorkerInlineAdmin] + view_on_site = False + site = admin.AdminSite(name="admin") site.register(Article, ArticleAdmin) site.register(CustomArticle, CustomArticleAdmin) @@ -785,6 +815,10 @@ site.register(MainPrepopulated, MainPrepopulatedAdmin) site.register(UnorderedObject, UnorderedObjectAdmin) site.register(UndeletableObject, UndeletableObjectAdmin) site.register(UnchangeableObject, UnchangeableObjectAdmin) +site.register(State, StateAdmin) +site.register(City, CityAdmin) +site.register(Restaurant, RestaurantAdmin) +site.register(Worker, WorkerAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: diff --git a/tests/admin_views/fixtures/admin-views-restaurants.xml b/tests/admin_views/fixtures/admin-views-restaurants.xml new file mode 100644 index 0000000000..81e67ee8d0 --- /dev/null +++ b/tests/admin_views/fixtures/admin-views-restaurants.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object pk="1" model="admin_views.state"> + <field type="CharField" name="name">New York</field> + </object> + <object pk="2" model="admin_views.state"> + <field type="CharField" name="name">Illinois</field> + </object> + <object pk="3" model="admin_views.state"> + <field type="CharField" name="name">California</field> + </object> + <object pk="1" model="admin_views.city"> + <field to="admin_views.state" name="state" rel="ManyToOneRel">1</field> + <field type="CharField" name="name">New York</field> + </object> + <object pk="2" model="admin_views.city"> + <field to="admin_views.state" name="state" rel="ManyToOneRel">2</field> + <field type="CharField" name="name">Chicago</field> + </object> + <object pk="3" model="admin_views.city"> + <field to="admin_views.state" name="state" rel="ManyToOneRel">3</field> + <field type="CharField" name="name">San Francisco</field> + </object> + <object pk="1" model="admin_views.restaurant"> + <field to="admin_views.city" name="city" rel="ManyToOneRel">1</field> + <field type="CharField" name="name">Italian Pizza</field> + </object> + <object pk="2" model="admin_views.restaurant"> + <field to="admin_views.city" name="city" rel="ManyToOneRel">1</field> + <field type="CharField" name="name">Boulevard</field> + </object> + <object pk="3" model="admin_views.restaurant"> + <field to="admin_views.city" name="city" rel="ManyToOneRel">2</field> + <field type="CharField" name="name">Chinese Dinner</field> + </object> + <object pk="4" model="admin_views.restaurant"> + <field to="admin_views.city" name="city" rel="ManyToOneRel">2</field> + <field type="CharField" name="name">Angels</field> + </object> + <object pk="5" model="admin_views.restaurant"> + <field to="admin_views.city" name="city" rel="ManyToOneRel">2</field> + <field type="CharField" name="name">Take Away</field> + </object> + <object pk="6" model="admin_views.restaurant"> + <field to="admin_views.city" name="city" rel="ManyToOneRel">3</field> + <field type="CharField" name="name">The Unknown Restaurant</field> + </object> + <object pk="1" model="admin_views.worker"> + <field to="admin_views.restaurant" name="work_at" rel="ManyToOneRel">1</field> + <field type="CharField" name="name">Mario</field> + <field type="CharField" name="surname">Rossi</field> + </object> + <object pk="2" model="admin_views.worker"> + <field to="admin_views.restaurant" name="work_at" rel="ManyToOneRel">1</field> + <field type="CharField" name="name">Antonio</field> + <field type="CharField" name="surname">Bianchi</field> + </object> + <object pk="3" model="admin_views.worker"> + <field to="admin_views.restaurant" name="work_at" rel="ManyToOneRel">1</field> + <field type="CharField" name="name">John</field> + <field type="CharField" name="surname">Doe</field> + </object> +</django-objects> diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index bab144e6b5..67f3b4216a 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -717,3 +717,19 @@ class EmptyModelHidden(models.Model): class EmptyModelMixin(models.Model): """ See ticket #11277. """ + +class State(models.Model): + name = models.CharField(max_length=100) + +class City(models.Model): + state = models.ForeignKey(State) + name = models.CharField(max_length=100) + +class Restaurant(models.Model): + city = models.ForeignKey(City) + name = models.CharField(max_length=100) + +class Worker(models.Model): + work_at = models.ForeignKey(Restaurant) + name = models.CharField(max_length=50) + surname = models.CharField(max_length=50) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 16fea943f9..d5232fb8f2 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -9,6 +9,7 @@ import unittest from django.conf import settings, global_settings from django.core import mail from django.core.files import temp as tempfile +from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse, NoReverseMatch # Register auth models with the admin. from django.contrib.auth import get_permission_codename @@ -48,8 +49,8 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount, AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject, Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage, - Telegram, Pizza, Topping, FilteredManager) -from .admin import site, site2 + Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker) +from .admin import site, site2, CityAdmin ERROR_MESSAGE = "Please enter the correct username and password \ @@ -4597,3 +4598,87 @@ class TestLabelVisibility(TestCase): def assert_fieldline_hidden(self, response): self.assertContains(response, '<div class="form-row hidden') + + +@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class AdminViewOnSiteTest(TestCase): + urls = "admin_views.urls" + fixtures = ['admin-views-users.xml', 'admin-views-restaurants.xml'] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def test_validate(self): + "Ensure that the view_on_site value is either a boolean or a callable" + CityAdmin.view_on_site = True + CityAdmin.validate(City) + CityAdmin.view_on_site = False + CityAdmin.validate(City) + CityAdmin.view_on_site = lambda obj: obj.get_absolute_url() + CityAdmin.validate(City) + CityAdmin.view_on_site = [] + with self.assertRaisesMessage(ImproperlyConfigured, 'CityAdmin.view_on_site is not a callable or a boolean value.'): + CityAdmin.validate(City) + + def test_false(self): + "Ensure that the 'View on site' button is not displayed if view_on_site is False" + response = self.client.get('/test_admin/admin/admin_views/restaurant/1/') + content_type_pk = ContentType.objects.get_for_model(Restaurant).pk + self.assertNotContains(response, + '"/test_admin/admin/r/%s/1/"' % content_type_pk, + ) + + def test_true(self): + "Ensure that the default behaviour is followed if view_on_site is True" + response = self.client.get('/test_admin/admin/admin_views/city/1/') + content_type_pk = ContentType.objects.get_for_model(City).pk + self.assertContains(response, + '"/test_admin/admin/r/%s/1/"' % content_type_pk, + ) + + def test_callable(self): + "Ensure that the right link is displayed if view_on_site is a callable" + response = self.client.get('/test_admin/admin/admin_views/worker/1/') + worker = Worker.objects.get(pk=1) + self.assertContains(response, + '"/worker/%s/%s/"' % (worker.surname, worker.name), + ) + + +@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class InlineAdminViewOnSiteTest(TestCase): + urls = "admin_views.urls" + fixtures = ['admin-views-users.xml', 'admin-views-restaurants.xml'] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def test_false(self): + "Ensure that the 'View on site' button is not displayed if view_on_site is False" + response = self.client.get('/test_admin/admin/admin_views/state/1/') + content_type_pk = ContentType.objects.get_for_model(City).pk + self.assertNotContains(response, + '/test_admin/admin/r/%s/1/' % content_type_pk, + ) + + def test_true(self): + "Ensure that the 'View on site' button is displayed if view_on_site is True" + response = self.client.get('/test_admin/admin/admin_views/city/1/') + content_type_pk = ContentType.objects.get_for_model(Restaurant).pk + self.assertContains(response, + '/test_admin/admin/r/%s/1/' % content_type_pk, + ) + + def test_callable(self): + "Ensure that the right link is displayed if view_on_site is a callable" + response = self.client.get('/test_admin/admin/admin_views/restaurant/1/') + worker = Worker.objects.get(pk=1) + self.assertContains(response, + '"/worker_inline/%s/%s/"' % (worker.surname, worker.name), + ) |
