# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from datetime import datetime
from django import forms
from django.conf import settings
from django.contrib import admin
from django.contrib.admin import widgets
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models import CharField, DateField
from django.test import TestCase as DjangoTestCase
from django.test.utils import override_settings
from django.utils import six
from django.utils import translation
from django.utils.unittest import TestCase
from . import models
from .widgetadmin import site as widget_admin_site
admin_static_prefix = lambda: {
'ADMIN_STATIC_PREFIX': "%sadmin/" % settings.STATIC_URL,
}
class AdminFormfieldForDBFieldTests(TestCase):
"""
Tests for correct behavior of ModelAdmin.formfield_for_dbfield
"""
def assertFormfield(self, model, fieldname, widgetclass, **admin_overrides):
"""
Helper to call formfield_for_dbfield for a given model and field name
and verify that the returned formfield is appropriate.
"""
# Override any settings on the model admin
class MyModelAdmin(admin.ModelAdmin):
pass
for k in admin_overrides:
setattr(MyModelAdmin, k, admin_overrides[k])
# Construct the admin, and ask it for a formfield
ma = MyModelAdmin(model, admin.site)
ff = ma.formfield_for_dbfield(model._meta.get_field(fieldname), request=None)
# "unwrap" the widget wrapper, if needed
if isinstance(ff.widget, widgets.RelatedFieldWidgetWrapper):
widget = ff.widget.widget
else:
widget = ff.widget
# Check that we got a field of the right type
self.assertTrue(
isinstance(widget, widgetclass),
"Wrong widget for %s.%s: expected %s, got %s" % \
(model.__class__.__name__, fieldname, widgetclass, type(widget))
)
# Return the formfield so that other tests can continue
return ff
def testDateField(self):
self.assertFormfield(models.Event, 'start_date', widgets.AdminDateWidget)
def testDateTimeField(self):
self.assertFormfield(models.Member, 'birthdate', widgets.AdminSplitDateTime)
def testTimeField(self):
self.assertFormfield(models.Event, 'start_time', widgets.AdminTimeWidget)
def testTextField(self):
self.assertFormfield(models.Event, 'description', widgets.AdminTextareaWidget)
def testURLField(self):
self.assertFormfield(models.Event, 'link', widgets.AdminURLFieldWidget)
def testIntegerField(self):
self.assertFormfield(models.Event, 'min_age', widgets.AdminIntegerFieldWidget)
def testCharField(self):
self.assertFormfield(models.Member, 'name', widgets.AdminTextInputWidget)
def testEmailField(self):
self.assertFormfield(models.Member, 'email', widgets.AdminEmailInputWidget)
def testFileField(self):
self.assertFormfield(models.Album, 'cover_art', widgets.AdminFileWidget)
def testForeignKey(self):
self.assertFormfield(models.Event, 'band', forms.Select)
def testRawIDForeignKey(self):
self.assertFormfield(models.Event, 'band', widgets.ForeignKeyRawIdWidget,
raw_id_fields=['band'])
def testRadioFieldsForeignKey(self):
ff = self.assertFormfield(models.Event, 'band', widgets.AdminRadioSelect,
radio_fields={'band':admin.VERTICAL})
self.assertEqual(ff.empty_label, None)
def testManyToMany(self):
self.assertFormfield(models.Band, 'members', forms.SelectMultiple)
def testRawIDManyTOMany(self):
self.assertFormfield(models.Band, 'members', widgets.ManyToManyRawIdWidget,
raw_id_fields=['members'])
def testFilteredManyToMany(self):
self.assertFormfield(models.Band, 'members', widgets.FilteredSelectMultiple,
filter_vertical=['members'])
def testFormfieldOverrides(self):
self.assertFormfield(models.Event, 'start_date', forms.TextInput,
formfield_overrides={DateField: {'widget': forms.TextInput}})
def testFormfieldOverridesWidgetInstances(self):
"""
Test that widget instances in formfield_overrides are not shared between
different fields. (#19423)
"""
class BandAdmin(admin.ModelAdmin):
formfield_overrides = {
CharField: {'widget': forms.TextInput(attrs={'size':'10'})}
}
ma = BandAdmin(models.Band, admin.site)
f1 = ma.formfield_for_dbfield(models.Band._meta.get_field('name'), request=None)
f2 = ma.formfield_for_dbfield(models.Band._meta.get_field('style'), request=None)
self.assertNotEqual(f1.widget, f2.widget)
self.assertEqual(f1.widget.attrs['maxlength'], '100')
self.assertEqual(f2.widget.attrs['maxlength'], '20')
self.assertEqual(f2.widget.attrs['size'], '10')
def testFieldWithChoices(self):
self.assertFormfield(models.Member, 'gender', forms.Select)
def testChoicesWithRadioFields(self):
self.assertFormfield(models.Member, 'gender', widgets.AdminRadioSelect,
radio_fields={'gender':admin.VERTICAL})
def testInheritance(self):
self.assertFormfield(models.Album, 'backside_art', widgets.AdminFileWidget)
def test_m2m_widgets(self):
"""m2m fields help text as it applies to admin app (#9321)."""
class AdvisorAdmin(admin.ModelAdmin):
filter_vertical=['companies']
self.assertFormfield(models.Advisor, 'companies', widgets.FilteredSelectMultiple,
filter_vertical=['companies'])
ma = AdvisorAdmin(models.Advisor, admin.site)
f = ma.formfield_for_dbfield(models.Advisor._meta.get_field('companies'), request=None)
self.assertEqual(six.text_type(f.help_text), ' Hold down "Control", or "Command" on a Mac, to select more than one.')
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminFormfieldForDBFieldWithRequestTests(DjangoTestCase):
fixtures = ["admin-widgets-users.xml"]
def testFilterChoicesByRequestUser(self):
"""
Ensure the user can only see their own cars in the foreign key dropdown.
"""
self.client.login(username="super", password="secret")
response = self.client.get("/widget_admin/admin_widgets/cartire/add/")
self.assertNotContains(response, "BMW M3")
self.assertContains(response, "Volkswagon Passat")
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminForeignKeyWidgetChangeList(DjangoTestCase):
fixtures = ["admin-widgets-users.xml"]
admin_root = '/widget_admin'
def setUp(self):
self.client.login(username="super", password="secret")
def tearDown(self):
self.client.logout()
def test_changelist_foreignkey(self):
response = self.client.get('%s/admin_widgets/car/' % self.admin_root)
self.assertContains(response, '%s/auth/user/add/' % self.admin_root)
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminForeignKeyRawIdWidget(DjangoTestCase):
fixtures = ["admin-widgets-users.xml"]
admin_root = '/widget_admin'
def setUp(self):
self.client.login(username="super", password="secret")
def tearDown(self):
self.client.logout()
def test_nonexistent_target_id(self):
band = models.Band.objects.create(name='Bogey Blues')
pk = band.pk
band.delete()
post_data = {
"band": '%s' % pk,
}
# Try posting with a non-existent pk in a raw id field: this
# should result in an error message, not a server exception.
response = self.client.post('%s/admin_widgets/event/add/' % self.admin_root,
post_data)
self.assertContains(response,
'Select a valid choice. That choice is not one of the available choices.')
def test_invalid_target_id(self):
for test_str in ('Iñtërnâtiônàlizætiøn', "1234'", -1234):
# This should result in an error message, not a server exception.
response = self.client.post('%s/admin_widgets/event/add/' % self.admin_root,
{"band": test_str})
self.assertContains(response,
'Select a valid choice. That choice is not one of the available choices.')
def test_url_params_from_lookup_dict_any_iterable(self):
lookup1 = widgets.url_params_from_lookup_dict({'color__in': ('red', 'blue')})
lookup2 = widgets.url_params_from_lookup_dict({'color__in': ['red', 'blue']})
self.assertEqual(lookup1, {'color__in': 'red,blue'})
self.assertEqual(lookup1, lookup2)
def test_url_params_from_lookup_dict_callable(self):
def my_callable():
return 'works'
lookup1 = widgets.url_params_from_lookup_dict({'myfield': my_callable})
lookup2 = widgets.url_params_from_lookup_dict({'myfield': my_callable()})
self.assertEqual(lookup1, lookup2)
class FilteredSelectMultipleWidgetTest(DjangoTestCase):
def test_render(self):
w = widgets.FilteredSelectMultiple('test', False)
self.assertHTMLEqual(
w.render('test', 'test'),
'\n' % admin_static_prefix()
)
def test_stacked_render(self):
w = widgets.FilteredSelectMultiple('test', True)
self.assertHTMLEqual(
w.render('test', 'test'),
'\n' % admin_static_prefix()
)
class AdminDateWidgetTest(DjangoTestCase):
def test_attrs(self):
"""
Ensure that user-supplied attrs are used.
Refs #12073.
"""
w = widgets.AdminDateWidget()
self.assertHTMLEqual(
w.render('test', datetime(2007, 12, 1, 9, 30)),
'',
)
# pass attrs to widget
w = widgets.AdminDateWidget(attrs={'size': 20, 'class': 'myDateField'})
self.assertHTMLEqual(
w.render('test', datetime(2007, 12, 1, 9, 30)),
'',
)
class AdminTimeWidgetTest(DjangoTestCase):
def test_attrs(self):
"""
Ensure that user-supplied attrs are used.
Refs #12073.
"""
w = widgets.AdminTimeWidget()
self.assertHTMLEqual(
w.render('test', datetime(2007, 12, 1, 9, 30)),
'',
)
# pass attrs to widget
w = widgets.AdminTimeWidget(attrs={'size': 20, 'class': 'myTimeField'})
self.assertHTMLEqual(
w.render('test', datetime(2007, 12, 1, 9, 30)),
'',
)
class AdminSplitDateTimeWidgetTest(DjangoTestCase):
def test_render(self):
w = widgets.AdminSplitDateTime()
self.assertHTMLEqual(
w.render('test', datetime(2007, 12, 1, 9, 30)),
'
Date: Time:
',
)
def test_localization(self):
w = widgets.AdminSplitDateTime()
with self.settings(USE_L10N=True):
with translation.override('de-at'):
w.is_localized = True
self.assertHTMLEqual(
w.render('test', datetime(2007, 12, 1, 9, 30)),
'
Datum: Zeit:
',
)
class AdminURLWidgetTest(DjangoTestCase):
def test_render(self):
w = widgets.AdminURLFieldWidget()
self.assertHTMLEqual(
w.render('test', ''),
''
)
self.assertHTMLEqual(
w.render('test', 'http://example.com'),
'
'
)
def test_render_quoting(self):
# WARNING: Don't use assertHTMLEqual in that testcase!
# assertHTMLEqual will get rid of some escapes which are tested here!
w = widgets.AdminURLFieldWidget()
self.assertEqual(
w.render('test', 'http://example.com/some text'),
'