Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #13068 (again) -- Corrected the admin stacked inline template t…

…o 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.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17562 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f0c2228709b0481eac09c860d7a4a30d3987cc19 1 parent cfd0cb0
Julien Phalip jphalip authored
10 django/contrib/admin/templates/admin/edit_inline/stacked.html
View
@@ -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 %}",
4 django/contrib/admin/templates/admin/edit_inline/tabular.html
View
@@ -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'));
2  django/contrib/admin/templates/admin/includes/fieldset.html
View
@@ -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 }}
9 django/contrib/admin/templates/admin/prepopulated_fields_js.html
View
@@ -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 %}
15 django/contrib/admin/tests.py
View
@@ -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 @@ def get_css_value(self, selector, attribute):
with Django.
"""
return self.selenium.execute_script(
- 'return django.jQuery("%s").css("%s")' % (selector, attribute))
+ '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))
35 tests/regressiontests/admin_views/admin.py
View
@@ -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 CustomTemplateBooleanFieldListFilter(BooleanFieldListFilter):
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 @@ class CustomTemplateFilterColorAdmin(admin.ModelAdmin):
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:
22 tests/regressiontests/admin_views/models.py
View
@@ -575,3 +575,25 @@ class Report(models.Model):
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)
109 tests/regressiontests/admin_views/tests.py
View
@@ -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 @@ def test_prepopulated_maxlength_localized(self):
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']
Please sign in to comment.
Something went wrong with that request. Please try again.