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.
This commit is contained in:
parent
497930b7f6
commit
fd219fa24c
|
@ -202,9 +202,10 @@ class InlineAdminFormSet(object):
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
|
for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
|
||||||
|
view_on_site_url = self.opts.get_view_on_site_url(original)
|
||||||
yield InlineAdminForm(self.formset, form, self.fieldsets,
|
yield InlineAdminForm(self.formset, form, self.fieldsets,
|
||||||
self.prepopulated_fields, original, self.readonly_fields,
|
self.prepopulated_fields, original, self.readonly_fields,
|
||||||
model_admin=self.opts)
|
model_admin=self.opts, view_on_site_url=view_on_site_url)
|
||||||
for form in self.formset.extra_forms:
|
for form in self.formset.extra_forms:
|
||||||
yield InlineAdminForm(self.formset, form, self.fieldsets,
|
yield InlineAdminForm(self.formset, form, self.fieldsets,
|
||||||
self.prepopulated_fields, None, self.readonly_fields,
|
self.prepopulated_fields, None, self.readonly_fields,
|
||||||
|
@ -242,13 +243,14 @@ class InlineAdminForm(AdminForm):
|
||||||
A wrapper around an inline form for use in the admin system.
|
A wrapper around an inline form for use in the admin system.
|
||||||
"""
|
"""
|
||||||
def __init__(self, formset, form, fieldsets, prepopulated_fields, original,
|
def __init__(self, formset, form, fieldsets, prepopulated_fields, original,
|
||||||
readonly_fields=None, model_admin=None):
|
readonly_fields=None, model_admin=None, view_on_site_url=None):
|
||||||
self.formset = formset
|
self.formset = formset
|
||||||
self.model_admin = model_admin
|
self.model_admin = model_admin
|
||||||
self.original = original
|
self.original = original
|
||||||
if original is not None:
|
if original is not None:
|
||||||
self.original_content_type_id = ContentType.objects.get_for_model(original).pk
|
self.original_content_type_id = ContentType.objects.get_for_model(original).pk
|
||||||
self.show_url = original and hasattr(original, 'get_absolute_url')
|
self.show_url = original and view_on_site_url is not None
|
||||||
|
self.absolute_url = view_on_site_url
|
||||||
super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields,
|
super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields,
|
||||||
readonly_fields, model_admin)
|
readonly_fields, model_admin)
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
|
||||||
formfield_overrides = {}
|
formfield_overrides = {}
|
||||||
readonly_fields = ()
|
readonly_fields = ()
|
||||||
ordering = None
|
ordering = None
|
||||||
|
view_on_site = True
|
||||||
|
|
||||||
# validation
|
# validation
|
||||||
validator_class = validation.BaseValidator
|
validator_class = validation.BaseValidator
|
||||||
|
@ -243,6 +244,19 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
|
||||||
|
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
|
def get_view_on_site_url(self, obj=None):
|
||||||
|
if obj is None or not self.view_on_site:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if callable(self.view_on_site):
|
||||||
|
return self.view_on_site(obj)
|
||||||
|
elif self.view_on_site:
|
||||||
|
# use the ContentType lookup if view_on_site is True
|
||||||
|
return reverse('admin:view_on_site', kwargs={
|
||||||
|
'content_type_id': ContentType.objects.get_for_model(obj).pk,
|
||||||
|
'object_id': obj.pk
|
||||||
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def declared_fieldsets(self):
|
def declared_fieldsets(self):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
|
@ -971,6 +985,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
app_label = opts.app_label
|
app_label = opts.app_label
|
||||||
preserved_filters = self.get_preserved_filters(request)
|
preserved_filters = self.get_preserved_filters(request)
|
||||||
form_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, form_url)
|
form_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, form_url)
|
||||||
|
view_on_site_url = self.get_view_on_site_url(obj)
|
||||||
context.update({
|
context.update({
|
||||||
'add': add,
|
'add': add,
|
||||||
'change': change,
|
'change': change,
|
||||||
|
@ -978,7 +993,8 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
'has_change_permission': self.has_change_permission(request, obj),
|
'has_change_permission': self.has_change_permission(request, obj),
|
||||||
'has_delete_permission': self.has_delete_permission(request, obj),
|
'has_delete_permission': self.has_delete_permission(request, obj),
|
||||||
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
|
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
|
||||||
'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
|
'has_absolute_url': view_on_site_url is not None,
|
||||||
|
'absolute_url': view_on_site_url,
|
||||||
'form_url': form_url,
|
'form_url': form_url,
|
||||||
'opts': opts,
|
'opts': opts,
|
||||||
'content_type_id': ContentType.objects.get_for_model(self.model).id,
|
'content_type_id': ContentType.objects.get_for_model(self.model).id,
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
|
{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
|
||||||
<a href="{% add_preserved_filters history_url %}" class="historylink">{% trans "History" %}</a>
|
<a href="{% add_preserved_filters history_url %}" class="historylink">{% trans "History" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
{% if has_absolute_url %}<li><a href="{{ absolute_url }}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}{% endif %}
|
{% endif %}{% endif %}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
||||||
<h3><b>{{ inline_admin_formset.opts.verbose_name|capfirst }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
|
<h3><b>{{ inline_admin_formset.opts.verbose_name|capfirst }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
|
||||||
{% if inline_admin_form.show_url %}<a href="{% url 'admin:view_on_site' inline_admin_form.original_content_type_id inline_admin_form.original.pk %}">{% trans "View on site" %}</a>{% endif %}
|
{% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% trans "View on site" %}</a>{% endif %}
|
||||||
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
|
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
|
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<td class="original">
|
<td class="original">
|
||||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||||
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
|
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
|
||||||
{% if inline_admin_form.show_url %}<a href="{% url 'admin:view_on_site' inline_admin_form.original_content_type_id inline_admin_form.original.pk %}">{% trans "View on site" %}</a>{% endif %}
|
{% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% trans "View on site" %}</a>{% endif %}
|
||||||
</p>{% endif %}
|
</p>{% endif %}
|
||||||
{% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
{% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||||
{{ inline_admin_form.fk_field.field }}
|
{{ inline_admin_form.fk_field.field }}
|
||||||
|
|
|
@ -164,6 +164,11 @@ class BaseValidator(object):
|
||||||
for idx, f in enumerate(val):
|
for idx, f in enumerate(val):
|
||||||
get_field(cls, model, "prepopulated_fields['%s'][%d]" % (field, idx), f)
|
get_field(cls, model, "prepopulated_fields['%s'][%d]" % (field, idx), f)
|
||||||
|
|
||||||
|
def validate_view_on_site_url(self, cls, model):
|
||||||
|
if hasattr(cls, 'view_on_site'):
|
||||||
|
if not callable(cls.view_on_site) and not isinstance(cls.view_on_site, bool):
|
||||||
|
raise ImproperlyConfigured("%s.view_on_site is not a callable or a boolean value." % cls.__name__)
|
||||||
|
|
||||||
def validate_ordering(self, cls, model):
|
def validate_ordering(self, cls, model):
|
||||||
" Validate that ordering refers to existing fields or is random. "
|
" Validate that ordering refers to existing fields or is random. "
|
||||||
# ordering = None
|
# ordering = None
|
||||||
|
|
|
@ -1091,6 +1091,37 @@ subclass::
|
||||||
:meth:`ModelAdmin.get_search_results` to provide additional or alternate
|
:meth:`ModelAdmin.get_search_results` to provide additional or alternate
|
||||||
search behavior.
|
search behavior.
|
||||||
|
|
||||||
|
.. attribute:: ModelAdmin.view_on_site
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
Set ``view_on_site`` to control whether or not to display the "View on site" link.
|
||||||
|
This link should bring you to a URL where you can display the saved object.
|
||||||
|
|
||||||
|
This value can be either a boolean flag or a callable. If ``True`` (the
|
||||||
|
default), the object's :meth:`~django.db.models.Model.get_absolute_url`
|
||||||
|
method will be used to generate the url.
|
||||||
|
|
||||||
|
If your model has a :meth:`~django.db.models.Model.get_absolute_url` method
|
||||||
|
but you don't want the "View on site" button to appear, you only need to set
|
||||||
|
``view_on_site`` to ``False``::
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
class PersonAdmin(admin.ModelAdmin):
|
||||||
|
view_on_site = False
|
||||||
|
|
||||||
|
In case it is a callable, it accepts the model instance as a parameter.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
class PersonAdmin(admin.ModelAdmin):
|
||||||
|
def view_on_site(self, obj):
|
||||||
|
return 'http://example.com' + reverse('person-detail',
|
||||||
|
kwargs={'slug': obj.slug})
|
||||||
|
|
||||||
Custom template options
|
Custom template options
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,10 @@ Minor features
|
||||||
<django.contrib.admin.ModelAdmin.list_display_links>` ``= None`` to disable
|
<django.contrib.admin.ModelAdmin.list_display_links>` ``= None`` to disable
|
||||||
links on the change list page grid.
|
links on the change list page grid.
|
||||||
|
|
||||||
|
* You may now specify :attr:`ModelAdmin.view_on_site
|
||||||
|
<django.contrib.admin.ModelAdmin.view_on_site>` to control whether or not to
|
||||||
|
display the "View on site" link.
|
||||||
|
|
||||||
:mod:`django.contrib.auth`
|
:mod:`django.contrib.auth`
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
|
||||||
AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
|
AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
|
||||||
RelatedPrepopulated, UndeletableObject, UnchangeableObject, UserMessenger, Simple, Choice,
|
RelatedPrepopulated, UndeletableObject, UnchangeableObject, UserMessenger, Simple, Choice,
|
||||||
ShortMessage, Telegram, FilteredManager, EmptyModelHidden,
|
ShortMessage, Telegram, FilteredManager, EmptyModelHidden,
|
||||||
EmptyModelVisible, EmptyModelMixin)
|
EmptyModelVisible, EmptyModelMixin, State, City, Restaurant, Worker)
|
||||||
|
|
||||||
|
|
||||||
def callable_year(dt_value):
|
def callable_year(dt_value):
|
||||||
|
@ -74,6 +74,7 @@ class ChapterXtra1Admin(admin.ModelAdmin):
|
||||||
class ArticleAdmin(admin.ModelAdmin):
|
class ArticleAdmin(admin.ModelAdmin):
|
||||||
list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year')
|
list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year')
|
||||||
list_filter = ('date', 'section')
|
list_filter = ('date', 'section')
|
||||||
|
view_on_site = False
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Some fields', {
|
('Some fields', {
|
||||||
'classes': ('collapse',),
|
'classes': ('collapse',),
|
||||||
|
@ -735,6 +736,35 @@ class EmptyModelMixinAdmin(admin.ModelAdmin):
|
||||||
form = FormWithVisibleAndHiddenField
|
form = FormWithVisibleAndHiddenField
|
||||||
fieldsets = EmptyModelVisibleAdmin.fieldsets
|
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 = admin.AdminSite(name="admin")
|
||||||
site.register(Article, ArticleAdmin)
|
site.register(Article, ArticleAdmin)
|
||||||
site.register(CustomArticle, CustomArticleAdmin)
|
site.register(CustomArticle, CustomArticleAdmin)
|
||||||
|
@ -785,6 +815,10 @@ site.register(MainPrepopulated, MainPrepopulatedAdmin)
|
||||||
site.register(UnorderedObject, UnorderedObjectAdmin)
|
site.register(UnorderedObject, UnorderedObjectAdmin)
|
||||||
site.register(UndeletableObject, UndeletableObjectAdmin)
|
site.register(UndeletableObject, UndeletableObjectAdmin)
|
||||||
site.register(UnchangeableObject, UnchangeableObjectAdmin)
|
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.
|
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
|
||||||
# That way we cover all four cases:
|
# That way we cover all four cases:
|
||||||
|
|
|
@ -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>
|
|
@ -717,3 +717,19 @@ class EmptyModelHidden(models.Model):
|
||||||
|
|
||||||
class EmptyModelMixin(models.Model):
|
class EmptyModelMixin(models.Model):
|
||||||
""" See ticket #11277. """
|
""" 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)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import unittest
|
||||||
from django.conf import settings, global_settings
|
from django.conf import settings, global_settings
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.files import temp as tempfile
|
from django.core.files import temp as tempfile
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
# Register auth models with the admin.
|
# Register auth models with the admin.
|
||||||
from django.contrib.auth import get_permission_codename
|
from django.contrib.auth import get_permission_codename
|
||||||
|
@ -48,8 +49,8 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount,
|
||||||
AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable,
|
AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable,
|
||||||
Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject,
|
Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject,
|
||||||
Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage,
|
Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage,
|
||||||
Telegram, Pizza, Topping, FilteredManager)
|
Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker)
|
||||||
from .admin import site, site2
|
from .admin import site, site2, CityAdmin
|
||||||
|
|
||||||
|
|
||||||
ERROR_MESSAGE = "Please enter the correct username and password \
|
ERROR_MESSAGE = "Please enter the correct username and password \
|
||||||
|
@ -4597,3 +4598,87 @@ class TestLabelVisibility(TestCase):
|
||||||
|
|
||||||
def assert_fieldline_hidden(self, response):
|
def assert_fieldline_hidden(self, response):
|
||||||
self.assertContains(response, '<div class="form-row hidden')
|
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),
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue