Author: julien Date: 2012-02-19 08:42:12 -0800 (Sun, 19 Feb 2012) New Revision: 17562
Modified: django/trunk/django/contrib/admin/templates/admin/edit_inline/stacked.html django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html django/trunk/django/contrib/admin/templates/admin/includes/fieldset.html django/trunk/django/contrib/admin/templates/admin/prepopulated_fields_js.html django/trunk/django/contrib/admin/tests.py django/trunk/tests/regressiontests/admin_views/admin.py django/trunk/tests/regressiontests/admin_views/models.py django/trunk/tests/regressiontests/admin_views/tests.py Log: Fixed #13068 (again) -- Corrected the admin stacked inline template to allow prepopulated fields to work (Thanks Stanislas Guerra for the report). Also fixed a regression introduced in [16953] where prepopulated fields wouldn't be recognized any more due to the additional "field-" CSS class name prefix. Modified: django/trunk/django/contrib/admin/templates/admin/edit_inline/stacked.html =================================================================== --- django/trunk/django/contrib/admin/templates/admin/edit_inline/stacked.html 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/django/contrib/admin/templates/admin/edit_inline/stacked.html 2012-02-19 16:42:12 UTC (rev 17562) @@ -27,14 +27,14 @@ var count = i + 1; $(this).html($(this).html().replace(/(#\d+)/g, "#" + count)); }); - } + }; var reinitDateTimeShortCuts = function() { // Reinitialize the calendar and clock widgets by force, yuck. if (typeof DateTimeShortcuts != "undefined") { $(".datetimeshortcuts").remove(); DateTimeShortcuts.init(); } - } + }; var updateSelectFilter = function() { // If any SelectFilter widgets were added, instantiate a new instance. if (typeof SelectFilter != "undefined"){ @@ -47,7 +47,7 @@ SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}"); }); } - } + }; var initPrepopulatedFields = function(row) { row.find('.prepopulated_field').each(function() { var field = $(this); @@ -55,13 +55,13 @@ var dependency_list = input.data('dependency_list') || []; var dependencies = []; $.each(dependency_list, function(i, field_name) { - dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id')); + dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id')); }); if (dependencies.length) { input.prepopulate(dependencies, input.attr('maxlength')); } }); - } + }; $(rows).formset({ prefix: "{{ inline_admin_formset.formset.prefix }}", addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}", Modified: django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html =================================================================== --- django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html 2012-02-19 16:42:12 UTC (rev 17562) @@ -22,7 +22,7 @@ {% if inline_admin_form.form.non_field_errors %} <tr><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr> {% endif %} - <tr class="{% cycle "row1" "row2" %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last %} empty-form{% endif %}" + <tr class="form-row {% cycle "row1" "row2" %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last %} empty-form{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}"> <td class="original"> {% if inline_admin_form.original or inline_admin_form.show_url %}<p> @@ -103,7 +103,7 @@ var dependency_list = input.data('dependency_list') || []; var dependencies = []; $.each(dependency_list, function(i, field_name) { - dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id')); + dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id')); }); if (dependencies.length) { input.prepopulate(dependencies, input.attr('maxlength')); Modified: django/trunk/django/contrib/admin/templates/admin/includes/fieldset.html =================================================================== --- django/trunk/django/contrib/admin/templates/admin/includes/fieldset.html 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/django/contrib/admin/templates/admin/includes/fieldset.html 2012-02-19 16:42:12 UTC (rev 17562) @@ -7,7 +7,7 @@ <div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}"> {% if line.fields|length_is:'1' %}{{ line.errors }}{% endif %} {% for field in line %} - <div{% if not line.fields|length_is:'1' %} class="field-box{% if not field.is_readonly and field.errors %} errors{% endif %}"{% endif %}> + <div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}"{% endif %}> {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %} {% if field.is_checkbox %} {{ field.field }}{{ field.label_tag }} Modified: django/trunk/django/contrib/admin/templates/admin/prepopulated_fields_js.html =================================================================== --- django/trunk/django/contrib/admin/templates/admin/prepopulated_fields_js.html 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/django/contrib/admin/templates/admin/prepopulated_fields_js.html 2012-02-19 16:42:12 UTC (rev 17562) @@ -1,7 +1,7 @@ {% load l10n %} <script type="text/javascript"> (function($) { - var field = null; + var field; {% for field in prepopulated_fields %} field = { @@ -13,10 +13,13 @@ {% for dependency in field.dependencies %} field['dependency_ids'].push('#{{ dependency.auto_id }}'); - field['dependency_list'].push('.{{ dependency.name }}'); + field['dependency_list'].push('{{ dependency.name }}'); {% endfor %} - $('.empty-form .{{ field.field.name }}').addClass('prepopulated_field'); + {% comment %} + Mark prepopulated fields in the main form and stacked inlines (.empty-form .form-row) and in tabular inlines (.empty-form.form-row) + {% endcomment %} + $('.empty-form .form-row .field-{{ field.field.name }}, .empty-form.form-row .field-{{ field.field.name }}').addClass('prepopulated_field'); $(field.id).data('dependency_list', field['dependency_list']) .prepopulate(field['dependency_ids'], field.maxLength); {% endfor %} Modified: django/trunk/django/contrib/admin/tests.py =================================================================== --- django/trunk/django/contrib/admin/tests.py 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/django/contrib/admin/tests.py 2012-02-19 16:42:12 UTC (rev 17562) @@ -1,4 +1,5 @@ import sys +from selenium.common.exceptions import NoSuchElementException from django.test import LiveServerTestCase from django.utils.importlib import import_module @@ -71,4 +72,16 @@ with Django. """ return self.selenium.execute_script( - 'return django.jQuery("%s").css("%s")' % (selector, attribute)) \ No newline at end of file + 'return django.jQuery("%s").css("%s")' % (selector, attribute)) + + def select_option(self, selector, value): + """ + Helper function to select the <OPTION> that has the value `value` and + that is in the <SELECT> widget identified by the CSS selector `selector`. + """ + options = self.selenium.find_elements_by_css_selector('%s option' % selector) + for option in options: + if option.get_attribute('value') == value: + option.click() + return + raise NoSuchElementException('Option "%s" not found in "%s"' % (value, selector)) \ No newline at end of file Modified: django/trunk/tests/regressiontests/admin_views/admin.py =================================================================== --- django/trunk/tests/regressiontests/admin_views/admin.py 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/tests/regressiontests/admin_views/admin.py 2012-02-19 16:42:12 UTC (rev 17562) @@ -26,7 +26,7 @@ CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping, Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug, AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, - AdminOrderedCallable, Report, Color2) + AdminOrderedCallable, Report, Color2, MainPrepopulated, RelatedPrepopulated) def callable_year(dt_value): @@ -532,6 +532,38 @@ 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']} + + + + site = admin.AdminSite(name="admin") site.register(Article, ArticleAdmin) site.register(CustomArticle, CustomArticleAdmin) @@ -576,6 +608,7 @@ site.register(Story, StoryAdmin) site.register(OtherStory, OtherStoryAdmin) site.register(Report, ReportAdmin) +site.register(MainPrepopulated, MainPrepopulatedAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: Modified: django/trunk/tests/regressiontests/admin_views/models.py =================================================================== --- django/trunk/tests/regressiontests/admin_views/models.py 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/tests/regressiontests/admin_views/models.py 2012-02-19 16:42:12 UTC (rev 17562) @@ -575,3 +575,25 @@ def __unicode__(self): return self.title + + +class MainPrepopulated(models.Model): + name = models.CharField(max_length=100) + pubdate = models.DateField() + status = models.CharField( + max_length=20, + choices=(('option one', 'Option One'), + ('option two', 'Option Two'))) + slug1 = models.SlugField() + slug2 = models.SlugField() + +class RelatedPrepopulated(models.Model): + parent = models.ForeignKey(MainPrepopulated) + name = models.CharField(max_length=75) + pubdate = models.DateField() + status = models.CharField( + max_length=20, + choices=(('option one', 'Option One'), + ('option two', 'Option Two'))) + slug1 = models.SlugField(max_length=50) + slug2 = models.SlugField(max_length=60) \ No newline at end of file Modified: django/trunk/tests/regressiontests/admin_views/tests.py =================================================================== --- django/trunk/tests/regressiontests/admin_views/tests.py 2012-02-19 09:22:18 UTC (rev 17561) +++ django/trunk/tests/regressiontests/admin_views/tests.py 2012-02-19 16:42:12 UTC (rev 17562) @@ -17,7 +17,8 @@ from django.contrib.admin.sites import LOGIN_FORM_KEY from django.contrib.admin.util import quote from django.contrib.admin.views.main import IS_POPUP_VAR -from django.contrib.auth import REDIRECT_FIELD_NAME, admin +from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase +from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.models import Group, User, Permission, UNUSABLE_PASSWORD from django.contrib.contenttypes.models import ContentType from django.forms.util import ErrorList @@ -40,7 +41,7 @@ FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory, ComplexSortedPerson, Parent, Child, AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, - Report) + Report, MainPrepopulated, RelatedPrepopulated) ERROR_MESSAGE = "Please enter the correct username and password \ @@ -2892,6 +2893,110 @@ response = self.client.get('/test_admin/admin/admin_views/prepopulatedpostlargeslug/add/') self.assertContains(response, "maxLength: 1000") # instead of 1,000 + +class SeleniumPrePopulatedTests(AdminSeleniumWebDriverTestCase): + urls = "regressiontests.admin_views.urls" + fixtures = ['admin-views-users.xml'] + + def test_basic(self): + """ + Ensure that the Javascript-automated prepopulated fields work with the + main form and with stacked and tabular inlines. + Refs #13068, #9264, #9983, #9784. + """ + self.admin_login(username='super', password='secret', login_url='/test_admin/admin/') + self.selenium.get('%s%s' % (self.live_server_url, + '/test_admin/admin/admin_views/mainprepopulated/add/')) + + # Main form ---------------------------------------------------------- + self.selenium.find_element_by_css_selector('#id_pubdate').send_keys('2012-02-18') + self.select_option('#id_status', 'option two') + self.selenium.find_element_by_css_selector('#id_name').send_keys(u' this is the mAin nÀMë and it\'s awεšome') + slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value') + self.assertEqual(slug1, 'main-name-and-its-awesome-2012-02-18') + self.assertEqual(slug2, 'option-two-main-name-and-its-awesome') + + # Stacked inlines ---------------------------------------------------- + # Initial inline + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-pubdate').send_keys('2011-12-17') + self.select_option('#id_relatedprepopulated_set-0-status', 'option one') + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-name').send_keys(u' here is a sŤāÇkeð inline ! ') + slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-slug2').get_attribute('value') + self.assertEqual(slug1, 'here-stacked-inline-2011-12-17') + self.assertEqual(slug2, 'option-one-here-stacked-inline') + + # Add an inline + self.selenium.find_element_by_css_selector('#relatedprepopulated_set-group .add-row a').click() + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-pubdate').send_keys('1999-01-25') + self.select_option('#id_relatedprepopulated_set-1-status', 'option two') + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-name').send_keys(u' now you haVe anöther sŤāÇkeð inline with a very ... loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog text... ') + slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-slug2').get_attribute('value') + self.assertEqual(slug1, 'now-you-have-another-stacked-inline-very-loooooooo') # 50 characters maximum for slug1 field + self.assertEqual(slug2, 'option-two-now-you-have-another-stacked-inline-very-looooooo') # 60 characters maximum for slug2 field + + # Tabular inlines ---------------------------------------------------- + # Initial inline + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-pubdate').send_keys('1234-12-07') + self.select_option('#id_relatedprepopulated_set-2-0-status', 'option two') + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-name').send_keys(u'And now, with a tÃbűlaŘ inline !!!') + slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-slug2').get_attribute('value') + self.assertEqual(slug1, 'and-now-tabular-inline-1234-12-07') + self.assertEqual(slug2, 'option-two-and-now-tabular-inline') + + # Add an inline + self.selenium.find_element_by_css_selector('#relatedprepopulated_set-2-group .add-row a').click() + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-pubdate').send_keys('1981-08-22') + self.select_option('#id_relatedprepopulated_set-2-1-status', 'option one') + self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-name').send_keys(u'a tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters') + slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-slug2').get_attribute('value') + self.assertEqual(slug1, 'tabular-inline-ignored-characters-1981-08-22') + self.assertEqual(slug2, 'option-one-tabular-inline-ignored-characters') + + # Save and check that everything is properly stored in the database + self.selenium.find_element_by_xpath('//input[@value="Save"]').click() + self.assertEqual(MainPrepopulated.objects.all().count(), 1) + MainPrepopulated.objects.get( + name=u' this is the mAin nÀMë and it\'s awεšome', + pubdate='2012-02-18', + status='option two', + slug1='main-name-and-its-awesome-2012-02-18', + slug2='option-two-main-name-and-its-awesome', + ) + self.assertEqual(RelatedPrepopulated.objects.all().count(), 4) + RelatedPrepopulated.objects.get( + name=u' here is a sŤāÇkeð inline ! ', + pubdate='2011-12-17', + status='option one', + slug1='here-stacked-inline-2011-12-17', + slug2='option-one-here-stacked-inline', + ) + RelatedPrepopulated.objects.get( + name=u' now you haVe anöther sŤāÇkeð inline with a very ... loooooooooooooooooo', # 75 characters in name field + pubdate='1999-01-25', + status='option two', + slug1='now-you-have-another-stacked-inline-very-loooooooo', + slug2='option-two-now-you-have-another-stacked-inline-very-looooooo', + ) + RelatedPrepopulated.objects.get( + name=u'And now, with a tÃbűlaŘ inline !!!', + pubdate='1234-12-07', + status='option two', + slug1='and-now-tabular-inline-1234-12-07', + slug2='option-two-and-now-tabular-inline', + ) + RelatedPrepopulated.objects.get( + name=u'a tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters', + pubdate='1981-08-22', + status='option one', + slug1='tabular-inline-ignored-characters-1981-08-22', + slug2='option-one-tabular-inline-ignored-characters', + ) + class ReadonlyTest(TestCase): urls = "regressiontests.admin_views.urls" fixtures = ['admin-views-users.xml'] -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-updates@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.