1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

Merge remote-tracking branch 'core/master' into schema-alteration

Conflicts:
	django/core/management/commands/flush.py
	django/core/management/commands/syncdb.py
	django/db/models/loading.py
	docs/internals/deprecation.txt
	docs/ref/django-admin.txt
	docs/releases/1.7.txt
This commit is contained in:
Andrew Godwin
2013-08-09 14:17:30 +01:00
489 changed files with 3840 additions and 1593 deletions

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib import admin
from django.core.paginator import Paginator
@@ -107,3 +105,10 @@ class DynamicListFilterChildAdmin(admin.ModelAdmin):
my_list_filter.remove('parent')
return my_list_filter
class DynamicSearchFieldsChildAdmin(admin.ModelAdmin):
search_fields = ('name',)
def get_search_fields(self, request):
search_fields = super(DynamicSearchFieldsChildAdmin, self).get_search_fields(request)
search_fields += ('age',)
return search_fields

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime
@@ -18,7 +18,8 @@ from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin,
DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin,
FilteredChildAdmin, CustomPaginator, site as custom_site,
SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin)
SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin,
DynamicSearchFieldsChildAdmin)
from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
UnorderedObject, OrderedObject, CustomIdUser)
@@ -91,7 +92,7 @@ class ChangeListTests(TestCase):
context = Context({'cl': cl})
table_output = template.render(context)
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % link
row_html = '<tbody><tr class="row1"><th class="field-name"><a href="%s">name</a></th><td class="field-parent nowrap">(None)</td></tr></tbody>' % link
self.assertFalse(table_output.find(row_html) == -1,
'Failed to find expected row element: %s' % table_output)
@@ -114,7 +115,7 @@ class ChangeListTests(TestCase):
context = Context({'cl': cl})
table_output = template.render(context)
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % link
row_html = '<tbody><tr class="row1"><th class="field-name"><a href="%s">name</a></th><td class="field-parent nowrap">Parent object</td></tr></tbody>' % link
self.assertFalse(table_output.find(row_html) == -1,
'Failed to find expected row element: %s' % table_output)
@@ -150,7 +151,7 @@ class ChangeListTests(TestCase):
# make sure that list editable fields are rendered in divs correctly
editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />'
self.assertInHTML('<td>%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field')
self.assertInHTML('<td class="field-name">%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field')
def test_result_list_editable(self):
"""
@@ -588,6 +589,13 @@ class ChangeListTests(TestCase):
response = m.changelist_view(request)
self.assertEqual(response.context_data['cl'].list_filter, ('parent', 'name', 'age'))
def test_dynamic_search_fields(self):
child = self._create_superuser('child')
m = DynamicSearchFieldsChildAdmin(Child, admin.site)
request = self._mocked_authenticated_request('/child/', child)
response = m.changelist_view(request)
self.assertEqual(response.context_data['cl'].search_fields, ('name', 'age'))
def test_pagination_page_range(self):
"""
Regression tests for ticket #15653: ensure the number of pages

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import warnings
from django.contrib.admin.util import quote

View File

@@ -1,6 +1,3 @@
# coding: utf-8
from __future__ import absolute_import
from django.conf.urls import patterns
from . import views

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib import admin
from django import forms

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
from django.contrib.admin.helpers import InlineAdminForm

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf.urls import patterns, include
from . import admin

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.test import TestCase, RequestFactory
from django.contrib import admin

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib import admin
from ..models.foo import Foo

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.db import models
from ..admin import foo

View File

@@ -0,0 +1,9 @@
from django.core.management.base import NoArgsCommand
class Command(NoArgsCommand):
help = "Test color output"
requires_model_validation = False
def handle_noargs(self, **options):
return self.style.SQL_KEYWORD('BEGIN')

View File

@@ -1,3 +1 @@
from __future__ import absolute_import
from ..complex_app.models.bar import Bar

View File

@@ -18,7 +18,7 @@ import unittest
import django
from django import conf, get_version
from django.conf import settings
from django.core.management import BaseCommand, CommandError
from django.core.management import BaseCommand, CommandError, call_command
from django.db import connection
from django.test.runner import DiscoverRunner
from django.utils.encoding import force_text
@@ -45,6 +45,7 @@ class AdminScriptTestCase(unittest.TestCase):
settings_file_path = os.path.join(test_dir, filename)
with open(settings_file_path, 'w') as settings_file:
settings_file.write('# -*- coding: utf-8 -*\n')
settings_file.write('# Settings file automatically generated by admin_scripts test case\n')
exports = [
'DATABASES',
@@ -921,14 +922,21 @@ class ManageAlternateSettings(AdminScriptTestCase):
"alternate: manage.py can execute user commands if settings are provided as argument"
args = ['noargs_command', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertNoOutput(err)
def test_custom_command_with_environment(self):
"alternate: manage.py can execute user commands if settings are provided in environment"
args = ['noargs_command']
out, err = self.run_manage(args, 'alternate_settings')
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertNoOutput(err)
def test_custom_command_output_color(self):
"alternate: manage.py output syntax color can be deactivated with the `--no-color` option"
args = ['noargs_command', '--no-color', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', True), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertNoOutput(err)
@@ -1271,40 +1279,47 @@ class CommandTypes(AdminScriptTestCase):
self.assertNoOutput(err)
self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s).")
def test_no_color(self):
"--no-color prevent colorization of the output"
out = StringIO()
call_command('color_command', no_color=True, stdout=out)
self.assertEqual(out.getvalue(), 'BEGIN\n')
def test_base_command(self):
"User BaseCommands can execute when a label is provided"
args = ['base_command', 'testlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_command_no_label(self):
"User BaseCommands can execute when no labels are provided"
args = ['base_command']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('no_color', False), ('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_command_multiple_label(self):
"User BaseCommands can execute when no labels are provided"
args = ['base_command', 'testlabel', 'anotherlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('no_color', False), ('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_command_with_option(self):
"User BaseCommands can execute with options when a label is provided"
args = ['base_command', 'testlabel', '--option_a=x']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_command_with_options(self):
"User BaseCommands can execute with multiple options when a label is provided"
args = ['base_command', 'testlabel', '-a', 'x', '--option_b=y']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_run_from_argv(self):
"""
@@ -1350,7 +1365,7 @@ class CommandTypes(AdminScriptTestCase):
args = ['noargs_command']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_noargs_with_args(self):
"NoArg Commands raise an error if an argument is provided"
@@ -1365,7 +1380,7 @@ class CommandTypes(AdminScriptTestCase):
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
self.assertOutput(out, os.sep.join(['django', 'contrib', 'auth', 'models.py']))
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "'>, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_app_command_no_apps(self):
"User AppCommands raise an error when no app name is provided"
@@ -1380,10 +1395,10 @@ class CommandTypes(AdminScriptTestCase):
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
self.assertOutput(out, os.sep.join(['django', 'contrib', 'auth', 'models.py']))
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "'>, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'")
self.assertOutput(out, os.sep.join(['django', 'contrib', 'contenttypes', 'models.py']))
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "'>, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_app_command_invalid_appname(self):
"User AppCommands can execute when a single app name is provided"
@@ -1402,7 +1417,7 @@ class CommandTypes(AdminScriptTestCase):
args = ['label_command', 'testlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_label_command_no_label(self):
"User LabelCommands raise an error if no label is provided"
@@ -1415,8 +1430,8 @@ class CommandTypes(AdminScriptTestCase):
args = ['label_command', 'testlabel', 'anotherlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
class ArgumentOrder(AdminScriptTestCase):
"""Tests for 2-stage argument parsing scheme.
@@ -1440,35 +1455,35 @@ class ArgumentOrder(AdminScriptTestCase):
args = ['base_command', 'testlabel', '--settings=alternate_settings', '--option_a=x']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_setting_then_short_option(self):
"Short options passed after settings are correctly handled"
args = ['base_command', 'testlabel', '--settings=alternate_settings', '--option_a=x']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_option_then_setting(self):
"Options passed before settings are correctly handled"
args = ['base_command', 'testlabel', '--option_a=x', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_short_option_then_setting(self):
"Short options passed before settings are correctly handled"
args = ['base_command', 'testlabel', '-a', 'x', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_option_then_setting_then_option(self):
"Options are correctly handled when they are passed before and after a setting"
args = ['base_command', 'testlabel', '--option_a=x', '--settings=alternate_settings', '--option_b=y']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
class StartProject(LiveServerTestCase, AdminScriptTestCase):

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from datetime import datetime
@@ -301,7 +301,7 @@ class UtilTests(SimpleTestCase):
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
'<label for="id_text" class="required inline"><i>text</i>:</label>')
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
'<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i>:</label>')
'<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i></label>')
# normal strings needs to be escaped
class MyForm(forms.Form):
@@ -312,7 +312,7 @@ class UtilTests(SimpleTestCase):
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
'<label for="id_text" class="required inline">&amp;text:</label>')
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
'<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb:</label>')
'<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb</label>')
def test_flatten_fieldsets(self):
"""

View File

@@ -1,9 +1,10 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django import forms
from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from django.test.utils import str_prefix
from .models import Song, Book, Album, TwoAlbumFKAndAnE, State, City
@@ -185,7 +186,8 @@ class ValidationTestCase(TestCase):
readonly_fields = ("title", "nonexistant")
self.assertRaisesMessage(ImproperlyConfigured,
"SongAdmin.readonly_fields[1], 'nonexistant' is not a callable or an attribute of 'SongAdmin' or found in the model 'Song'.",
str_prefix("SongAdmin.readonly_fields[1], %(_)s'nonexistant' is not a callable "
"or an attribute of 'SongAdmin' or found in the model 'Song'."),
SongAdmin.validate,
Song)
@@ -195,7 +197,8 @@ class ValidationTestCase(TestCase):
readonly_fields=['i_dont_exist'] # Missing attribute
self.assertRaisesMessage(ImproperlyConfigured,
"CityInline.readonly_fields[0], 'i_dont_exist' is not a callable or an attribute of 'CityInline' or found in the model 'City'.",
str_prefix("CityInline.readonly_fields[0], %(_)s'i_dont_exist' is not a callable "
"or an attribute of 'CityInline' or found in the model 'City'."),
CityInline.validate,
City)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import tempfile
import os

View File

@@ -1,7 +1,7 @@
"""
A second, custom AdminSite -- see tests.CustomAdminSiteTests.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
from django.conf.urls import patterns
from django.contrib import admin
@@ -13,6 +13,7 @@ from . import models, forms, admin as base_admin
class Admin2(admin.AdminSite):
app_index_template = 'custom_admin/app_index.html'
login_form = forms.CustomAdminAuthenticationForm
login_template = 'custom_admin/login.html'
logout_template = 'custom_admin/logout.html'

View File

@@ -644,8 +644,8 @@ class MainPrepopulated(models.Model):
max_length=20,
choices=(('option one', 'Option One'),
('option two', 'Option Two')))
slug1 = models.SlugField()
slug2 = models.SlugField()
slug1 = models.SlugField(blank=True)
slug2 = models.SlugField(blank=True)
class RelatedPrepopulated(models.Model):
parent = models.ForeignKey(MainPrepopulated)

View File

@@ -1,5 +1,5 @@
# coding: utf-8
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import os
import re
@@ -806,6 +806,12 @@ class CustomModelAdminTest(AdminViewBasicTestCase):
self.assertTemplateUsed(response, 'custom_admin/index.html')
self.assertContains(response, 'Hello from a custom index template *bar*')
def testCustomAdminSiteAppIndexViewandTemplate(self):
response = self.client.get('/test_admin/admin2/admin_views/')
self.assertIsInstance(response, TemplateResponse)
self.assertTemplateUsed(response, 'custom_admin/app_index.html')
self.assertContains(response, 'Hello from a custom app_index template')
def testCustomAdminSitePasswordChangeTemplate(self):
response = self.client.get('/test_admin/admin2/password_change/')
self.assertIsInstance(response, TemplateResponse)
@@ -1496,7 +1502,7 @@ class AdminViewStringPrimaryKeyTest(TestCase):
response = self.client.get(prefix)
# this URL now comes through reverse(), thus url quoting and iri_to_uri encoding
pk_final_url = escape(iri_to_uri(urlquote(quote(self.pk))))
should_contain = """<th><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk))
should_contain = """<th class="field-__str__"><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk))
self.assertContains(response, should_contain)
def test_recentactions_link(self):
@@ -1527,6 +1533,17 @@ class AdminViewStringPrimaryKeyTest(TestCase):
self.assertEqual(counted_presence_before - 1,
counted_presence_after)
def test_logentry_get_admin_url(self):
"LogEntry.get_admin_url returns a URL to edit the entry's object or None for non-existent (possibly deleted) models"
log_entry_name = "Model with string primary key" # capitalized in Recent Actions
logentry = LogEntry.objects.get(content_type__name__iexact=log_entry_name)
model = "modelwithstringprimarykey"
desired_admin_url = "/test_admin/admin/admin_views/%s/%s/" % (model, escape(iri_to_uri(urlquote(quote(self.pk)))))
self.assertEqual(logentry.get_admin_url(), desired_admin_url)
logentry.content_type.model = "non-existent"
self.assertEqual(logentry.get_admin_url(), None)
def test_deleteconfirmation_link(self):
"The link from the delete confirmation page referring back to the changeform of the object should be quoted"
response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/delete/' % quote(self.pk))
@@ -2151,8 +2168,8 @@ class AdminViewListEditable(TestCase):
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
self.assertContains(response, 'id="id_form-1-id"', 1)
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
self.assertContains(response, '<td>%d</td>' % story1.id, 1)
self.assertContains(response, '<td>%d</td>' % story2.id, 1)
self.assertContains(response, '<td class="field-id">%d</td>' % story1.id, 1)
self.assertContains(response, '<td class="field-id">%d</td>' % story2.id, 1)
def test_pk_hidden_fields_with_list_display_links(self):
""" Similarly as test_pk_hidden_fields, but when the hidden pk fields are
@@ -2167,8 +2184,8 @@ class AdminViewListEditable(TestCase):
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
self.assertContains(response, 'id="id_form-1-id"', 1)
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link1, story1.id), 1)
self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link2, story2.id), 1)
self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link1, story1.id), 1)
self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link2, story2.id), 1)
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
@@ -3438,6 +3455,50 @@ class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase):
slug2='option-one-tabular-inline-ignored-characters',
)
def test_populate_existing_object(self):
"""
Ensure that the prepopulation works for existing objects too, as long
as the original field is empty.
Refs #19082.
"""
# Slugs are empty to start with.
item = MainPrepopulated.objects.create(
name=' this is the mAin nÀMë',
pubdate='2012-02-18',
status='option two',
slug1='',
slug2='',
)
self.admin_login(username='super',
password='secret',
login_url='/test_admin/admin/')
object_url = '%s%s' % (
self.live_server_url,
'/test_admin/admin/admin_views/mainprepopulated/{}/'.format(item.id))
self.selenium.get(object_url)
self.selenium.find_element_by_css_selector('#id_name').send_keys(' the best')
# The slugs got prepopulated since they were originally empty
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-best-2012-02-18')
self.assertEqual(slug2, 'option-two-main-name-best')
# Save the object
self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
self.wait_page_loaded()
self.selenium.get(object_url)
self.selenium.find_element_by_css_selector('#id_name').send_keys(' hello')
# The slugs got prepopulated didn't change since they were originally not empty
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-best-2012-02-18')
self.assertEqual(slug2, 'option-two-main-name-best')
def test_collapsible_fieldset(self):
"""
Test that the 'collapse' class in fieldsets definition allows to
@@ -3877,6 +3938,22 @@ class CSSTest(TestCase):
self.assertContains(response,
'<body class="app-admin_views model-section ')
def test_changelist_field_classes(self):
"""
Cells of the change list table should contain the field name in their class attribute
Refs #11195.
"""
Podcast.objects.create(name="Django Dose",
release_date=datetime.date.today())
response = self.client.get('/test_admin/admin/admin_views/podcast/')
self.assertContains(
response, '<th class="field-name">')
self.assertContains(
response, '<td class="field-release_date nowrap">')
self.assertContains(
response, '<td class="action-checkbox">')
try:
import docutils
except ImportError:

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf.urls import patterns, include
from . import views, customadmin, admin

View File

@@ -13,6 +13,7 @@ class Member(models.Model):
name = models.CharField(max_length=100)
birthdate = models.DateTimeField(blank=True, null=True)
gender = models.CharField(max_length=1, blank=True, choices=[('M','Male'), ('F', 'Female')])
email = models.EmailField(blank=True)
def __str__(self):
return self.name
@@ -55,7 +56,8 @@ class Inventory(models.Model):
return self.name
class Event(models.Model):
band = models.ForeignKey(Band, limit_choices_to=models.Q(pk__gt=0))
main_band = models.ForeignKey(Band, limit_choices_to=models.Q(pk__gt=0), related_name='events_main_band_at')
supporting_bands = models.ManyToManyField(Band, null=True, blank=True, related_name='events_supporting_band_at')
start_date = models.DateField(blank=True, null=True)
start_time = models.TimeField(blank=True, null=True)
description = models.TextField(blank=True)

View File

@@ -1,5 +1,5 @@
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from datetime import datetime, timedelta
from unittest import TestCase
@@ -83,19 +83,22 @@ class AdminFormfieldForDBFieldTests(TestCase):
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)
self.assertFormfield(models.Event, 'main_band', forms.Select)
def testRawIDForeignKey(self):
self.assertFormfield(models.Event, 'band', widgets.ForeignKeyRawIdWidget,
raw_id_fields=['band'])
self.assertFormfield(models.Event, 'main_band', widgets.ForeignKeyRawIdWidget,
raw_id_fields=['main_band'])
def testRadioFieldsForeignKey(self):
ff = self.assertFormfield(models.Event, 'band', widgets.AdminRadioSelect,
radio_fields={'band':admin.VERTICAL})
ff = self.assertFormfield(models.Event, 'main_band', widgets.AdminRadioSelect,
radio_fields={'main_band':admin.VERTICAL})
self.assertEqual(ff.empty_label, None)
def testManyToMany(self):
@@ -198,7 +201,7 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase):
pk = band.pk
band.delete()
post_data = {
"band": '%s' % pk,
"main_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.
@@ -212,7 +215,7 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase):
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})
{"main_band": test_str})
self.assertContains(response,
'Select a valid choice. That choice is not one of the available choices.')
@@ -223,6 +226,13 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase):
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):
@@ -300,29 +310,29 @@ class AdminURLWidgetTest(DjangoTestCase):
w = widgets.AdminURLFieldWidget()
self.assertHTMLEqual(
conditional_escape(w.render('test', '')),
'<input class="vURLField" name="test" type="text" />'
'<input class="vURLField" name="test" type="url" />'
)
self.assertHTMLEqual(
conditional_escape(w.render('test', 'http://example.com')),
'<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com" /></p>'
'<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="url" value="http://example.com" /></p>'
)
def test_render_idn(self):
w = widgets.AdminURLFieldWidget()
self.assertHTMLEqual(
conditional_escape(w.render('test', 'http://example-äüö.com')),
'<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
'<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="url" value="http://example-äüö.com" /></p>'
)
def test_render_quoting(self):
w = widgets.AdminURLFieldWidget()
self.assertHTMLEqual(
conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')),
'<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
'<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="url" value="http://example.com/<sometag>some text</sometag>" /></p>'
)
self.assertHTMLEqual(
conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')),
'<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
'<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/&lt;sometag&gt;some text&lt;/sometag&gt;</a><br />Change:<input class="vURLField" name="test" type="url" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
)
@@ -816,3 +826,100 @@ class HorizontalVerticalFilterSeleniumChromeTests(HorizontalVerticalFilterSeleni
class HorizontalVerticalFilterSeleniumIETests(HorizontalVerticalFilterSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminRawIdWidgetSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps
fixtures = ['admin-widgets-users.xml']
urls = "admin_widgets.urls"
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self):
models.Band.objects.create(id=42, name='Bogey Blues')
models.Band.objects.create(id=98, name='Green Potatoes')
super(AdminRawIdWidgetSeleniumFirefoxTests, self).setUp()
def test_foreignkey(self):
self.admin_login(username='super', password='secret', login_url='/')
self.selenium.get(
'%s%s' % (self.live_server_url, '/admin_widgets/event/add/'))
main_window = self.selenium.current_window_handle
# No value has been selected yet
self.assertEqual(
self.selenium.find_element_by_id('id_main_band').get_attribute('value'),
'')
# Open the popup window and click on a band
self.selenium.find_element_by_id('lookup_id_main_band').click()
self.selenium.switch_to_window('id_main_band')
self.wait_page_loaded()
link = self.selenium.find_element_by_link_text('Bogey Blues')
self.assertTrue('/band/42/' in link.get_attribute('href'))
link.click()
# The field now contains the selected band's id
self.selenium.switch_to_window(main_window)
self.assertEqual(
self.selenium.find_element_by_id('id_main_band').get_attribute('value'),
'42')
# Reopen the popup window and click on another band
self.selenium.find_element_by_id('lookup_id_main_band').click()
self.selenium.switch_to_window('id_main_band')
self.wait_page_loaded()
link = self.selenium.find_element_by_link_text('Green Potatoes')
self.assertTrue('/band/98/' in link.get_attribute('href'))
link.click()
# The field now contains the other selected band's id
self.selenium.switch_to_window(main_window)
self.assertEqual(
self.selenium.find_element_by_id('id_main_band').get_attribute('value'),
'98')
def test_many_to_many(self):
self.admin_login(username='super', password='secret', login_url='/')
self.selenium.get(
'%s%s' % (self.live_server_url, '/admin_widgets/event/add/'))
main_window = self.selenium.current_window_handle
# No value has been selected yet
self.assertEqual(
self.selenium.find_element_by_id('id_supporting_bands').get_attribute('value'),
'')
# Open the popup window and click on a band
self.selenium.find_element_by_id('lookup_id_supporting_bands').click()
self.selenium.switch_to_window('id_supporting_bands')
self.wait_page_loaded()
link = self.selenium.find_element_by_link_text('Bogey Blues')
self.assertTrue('/band/42/' in link.get_attribute('href'))
link.click()
# The field now contains the selected band's id
self.selenium.switch_to_window(main_window)
self.assertEqual(
self.selenium.find_element_by_id('id_supporting_bands').get_attribute('value'),
'42')
# Reopen the popup window and click on another band
self.selenium.find_element_by_id('lookup_id_supporting_bands').click()
self.selenium.switch_to_window('id_supporting_bands')
self.wait_page_loaded()
link = self.selenium.find_element_by_link_text('Green Potatoes')
self.assertTrue('/band/98/' in link.get_attribute('href'))
link.click()
# The field now contains the two selected bands' ids
self.selenium.switch_to_window(main_window)
self.assertEqual(
self.selenium.find_element_by_id('id_supporting_bands').get_attribute('value'),
'42,98')
class AdminRawIdWidgetSeleniumChromeTests(AdminRawIdWidgetSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class AdminRawIdWidgetSeleniumIETests(AdminRawIdWidgetSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf.urls import patterns, include
from . import widgetadmin

View File

@@ -1,8 +1,3 @@
"""
"""
from __future__ import absolute_import
from django.contrib import admin
from . import models
@@ -23,7 +18,7 @@ class CarTireAdmin(admin.ModelAdmin):
return super(CarTireAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
class EventAdmin(admin.ModelAdmin):
raw_id_fields = ['band']
raw_id_fields = ['main_band', 'supporting_bands']
class SchoolAdmin(admin.ModelAdmin):
@@ -47,4 +42,4 @@ site.register(models.Bee)
site.register(models.Advisor)
site.register(models.School, SchoolAdmin)
site.register(models.School, SchoolAdmin)

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import datetime
from decimal import Decimal
@@ -585,3 +585,35 @@ class BaseAggregateTestCase(TestCase):
"datetime.date(2008, 1, 1)"
]
)
def test_values_aggregation(self):
# Refs #20782
max_rating = Book.objects.values('rating').aggregate(max_rating=Max('rating'))
self.assertEqual(max_rating['max_rating'], 5)
max_books_per_rating = Book.objects.values('rating').annotate(
books_per_rating=Count('id')
).aggregate(Max('books_per_rating'))
self.assertEqual(
max_books_per_rating,
{'books_per_rating__max': 3})
def test_ticket17424(self):
"""
Check that doing exclude() on a foreign model after annotate()
doesn't crash.
"""
all_books = list(Book.objects.values_list('pk', flat=True).order_by('pk'))
annotated_books = Book.objects.order_by('pk').annotate(one=Count("id"))
# The value doesn't matter, we just need any negative
# constraint on a related model that's a noop.
excluded_books = annotated_books.exclude(publisher__name="__UNLIKELY_VALUE__")
# Try to generate query tree
str(excluded_books.query)
self.assertQuerysetEqual(excluded_books, all_books, lambda x: x.pk)
# Check internal state
self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type)
self.assertIsNone(excluded_books.query.alias_map["aggregation_book"].join_type)

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime
import pickle
@@ -250,6 +250,13 @@ class AggregationTests(TestCase):
'price__max': Decimal("82.80")
})
# Regression for #15624 - Missing SELECT columns when using values, annotate
# and aggregate in a single query
self.assertEqual(
Book.objects.annotate(c=Count('authors')).values('c').aggregate(Max('c')),
{'c__max': 3}
)
def test_field_error(self):
# Bad field requests in aggregates are caught and reported
self.assertRaises(

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import copy
import os
@@ -7,7 +7,8 @@ import time
from unittest import TestCase
from django.conf import Settings
from django.db.models.loading import cache, load_app, get_model, get_models
from django.db.models.loading import cache, load_app, get_model, get_models, AppCache
from django.test.utils import override_settings
from django.utils._os import upath
@@ -61,12 +62,33 @@ class EggLoadingTest(TestCase):
egg_name = '%s/brokenapp.egg' % self.egg_dir
sys.path.append(egg_name)
self.assertRaises(ImportError, load_app, 'broken_app')
raised = None
try:
load_app('broken_app')
except ImportError as e:
# Make sure the message is indicating the actual
# problem in the broken app.
self.assertTrue("modelz" in e.args[0])
raised = e
# Make sure the message is indicating the actual
# problem in the broken app.
self.assertTrue(raised is not None)
self.assertTrue("modelz" in raised.args[0])
def test_missing_app(self):
"""
Test that repeated app loading doesn't succeed in case there is an
error. Refs #17667.
"""
# AppCache is a Borg, so we can instantiate one and change its
# loaded to False to force the following code to actually try to
# populate the cache.
a = AppCache()
a.loaded = False
try:
with override_settings(INSTALLED_APPS=('notexists',)):
self.assertRaises(ImportError, get_model, 'notexists', 'nomodel', seed_cache=True)
self.assertRaises(ImportError, get_model, 'notexists', 'nomodel', seed_cache=True)
finally:
a.loaded = True
class GetModelsTest(TestCase):

View File

@@ -68,11 +68,18 @@ class Reporter(models.Model):
return "%s %s" % (self.first_name, self.last_name)
class ReporterProxy(Reporter):
class Meta:
proxy = True
@python_2_unicode_compatible
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter)
reporter_proxy = models.ForeignKey(ReporterProxy, null=True,
related_name='reporter_proxy')
def __str__(self):
return self.headline

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Unit and doctests for specific database backends.
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime
from decimal import Decimal
@@ -614,12 +614,19 @@ class FkConstraintsTests(TransactionTestCase):
Try to create a model instance that violates a FK constraint. If it
fails it should fail with IntegrityError.
"""
a = models.Article(headline="This is a test", pub_date=datetime.datetime(2005, 7, 27), reporter_id=30)
a1 = models.Article(headline="This is a test", pub_date=datetime.datetime(2005, 7, 27), reporter_id=30)
try:
a.save()
a1.save()
except IntegrityError:
return
self.skipTest("This backend does not support integrity checks.")
pass
else:
self.skipTest("This backend does not support integrity checks.")
# Now that we know this backend supports integrity checks we make sure
# constraints are also enforced for proxy models. Refs #17519
a2 = models.Article(headline='This is another test', reporter=self.r,
pub_date=datetime.datetime(2012, 8, 3),
reporter_proxy_id=30)
self.assertRaises(IntegrityError, a2.save)
def test_integrity_checks_on_update(self):
"""
@@ -628,14 +635,26 @@ class FkConstraintsTests(TransactionTestCase):
"""
# Create an Article.
models.Article.objects.create(headline="Test article", pub_date=datetime.datetime(2010, 9, 4), reporter=self.r)
# Retrive it from the DB
a = models.Article.objects.get(headline="Test article")
a.reporter_id = 30
# Retrieve it from the DB
a1 = models.Article.objects.get(headline="Test article")
a1.reporter_id = 30
try:
a.save()
a1.save()
except IntegrityError:
return
self.skipTest("This backend does not support integrity checks.")
pass
else:
self.skipTest("This backend does not support integrity checks.")
# Now that we know this backend supports integrity checks we make sure
# constraints are also enforced for proxy models. Refs #17519
# Create another article
r_proxy = models.ReporterProxy.objects.get(pk=self.r.pk)
models.Article.objects.create(headline='Another article',
pub_date=datetime.datetime(1988, 5, 15),
reporter=self.r, reporter_proxy=r_proxy)
# Retreive the second article from the DB
a2 = models.Article.objects.get(headline='Another article')
a2.reporter_proxy_id = 30
self.assertRaises(IntegrityError, a2.save)
def test_disable_constraint_checks_manually(self):
"""

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from datetime import datetime
import threading
@@ -6,6 +6,7 @@ import threading
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models.fields import Field, FieldDoesNotExist
from django.db.models.manager import BaseManager
from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS
from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import six
@@ -734,3 +735,59 @@ class ConcurrentSaveTests(TransactionTestCase):
t.join()
a.save()
self.assertEqual(Article.objects.get(pk=a.pk).headline, 'foo')
class ManagerTest(TestCase):
QUERYSET_PROXY_METHODS = [
'none',
'count',
'dates',
'datetimes',
'distinct',
'extra',
'get',
'get_or_create',
'update_or_create',
'create',
'bulk_create',
'filter',
'aggregate',
'annotate',
'complex_filter',
'exclude',
'in_bulk',
'iterator',
'earliest',
'latest',
'first',
'last',
'order_by',
'select_for_update',
'select_related',
'prefetch_related',
'values',
'values_list',
'update',
'reverse',
'defer',
'only',
'using',
'exists',
'_insert',
'_update',
'raw',
]
def test_manager_methods(self):
"""
This test ensures that the correct set of methods from `QuerySet`
are copied onto `Manager`.
It's particularly useful to prevent accidentally leaking new methods
into `Manager`. New `QuerySet` methods that should also be copied onto
`Manager` will need to be added to `ManagerTest.QUERYSET_PROXY_METHODS`.
"""
self.assertEqual(
sorted(BaseManager._get_queryset_methods(QuerySet).keys()),
sorted(self.QUERYSET_PROXY_METHODS),
)

View File

@@ -4,8 +4,6 @@ gets called *again* for each FileField. This test will fail if calling a
ModelForm's save() method causes Model.save() to be called more than once.
"""
from __future__ import absolute_import
import os
import shutil
import unittest

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib import admin
from .models import Story

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from operator import attrgetter

View File

@@ -2,7 +2,7 @@
# Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file.
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import hashlib
import os

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.test import TestCase
from .models import Person

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib.auth.models import User
from django.contrib.comments.forms import CommentForm
from django.contrib.comments.models import Comment

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf import settings
from django.contrib import comments
from django.contrib.comments.models import Comment

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
import time
from django.conf import settings

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib.comments.models import Comment
from django.contrib.comments.moderation import (moderator, CommentModerator,
AlreadyModerated)

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import re

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from xml.etree import ElementTree as ET
from django.conf import settings

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib.comments.models import Comment
from . import CommentTestCase

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.contrib.auth.models import User, Permission
from django.contrib.comments import signals

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib.comments.forms import CommentForm
from django.contrib.comments.models import Comment
from django.contrib.contenttypes.models import ContentType

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf.urls import patterns, url
from django.contrib.comments.feeds import LatestCommentFeed

View File

@@ -1,6 +1,3 @@
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from django.views.decorators.http import condition, etag, last_modified
from django.http import HttpResponse

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import python_2_unicode_compatible

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.conf.urls import patterns

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf.urls import patterns, url
from . import views

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.core.exceptions import FieldError
from django.test import TestCase

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.core.exceptions import FieldError
from django.test import TestCase

View File

@@ -20,6 +20,49 @@ class PersonManager(models.Manager):
def get_fun_people(self):
return self.filter(fun=True)
# An example of a custom manager that sets get_queryset().
class PublishedBookManager(models.Manager):
def get_queryset(self):
return super(PublishedBookManager, self).get_queryset().filter(is_published=True)
# An example of a custom queryset that copies its methods onto the manager.
class CustomQuerySet(models.QuerySet):
def filter(self, *args, **kwargs):
queryset = super(CustomQuerySet, self).filter(fun=True)
queryset._filter_CustomQuerySet = True
return queryset
def public_method(self, *args, **kwargs):
return self.all()
def _private_method(self, *args, **kwargs):
return self.all()
def optout_public_method(self, *args, **kwargs):
return self.all()
optout_public_method.queryset_only = True
def _optin_private_method(self, *args, **kwargs):
return self.all()
_optin_private_method.queryset_only = False
class BaseCustomManager(models.Manager):
def __init__(self, arg):
super(BaseCustomManager, self).__init__()
self.init_arg = arg
def filter(self, *args, **kwargs):
queryset = super(BaseCustomManager, self).filter(fun=True)
queryset._filter_CustomManager = True
return queryset
def manager_only(self):
return self.all()
CustomManager = BaseCustomManager.from_queryset(CustomQuerySet)
@python_2_unicode_compatible
class Person(models.Model):
first_name = models.CharField(max_length=30)
@@ -27,15 +70,12 @@ class Person(models.Model):
fun = models.BooleanField()
objects = PersonManager()
custom_queryset_default_manager = CustomQuerySet.as_manager()
custom_queryset_custom_manager = CustomManager('hello')
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
# An example of a custom manager that sets get_queryset().
class PublishedBookManager(models.Manager):
def get_queryset(self):
return super(PublishedBookManager, self).get_queryset().filter(is_published=True)
@python_2_unicode_compatible
class Book(models.Model):
title = models.CharField(max_length=50)

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.test import TestCase
from django.utils import six
@@ -11,12 +11,54 @@ class CustomManagerTests(TestCase):
p1 = Person.objects.create(first_name="Bugs", last_name="Bunny", fun=True)
p2 = Person.objects.create(first_name="Droopy", last_name="Dog", fun=False)
# Test a custom `Manager` method.
self.assertQuerysetEqual(
Person.objects.get_fun_people(), [
"Bugs Bunny"
],
six.text_type
)
# Test that the methods of a custom `QuerySet` are properly
# copied onto the default `Manager`.
for manager in ['custom_queryset_default_manager',
'custom_queryset_custom_manager']:
manager = getattr(Person, manager)
# Copy public methods.
manager.public_method()
# Don't copy private methods.
with self.assertRaises(AttributeError):
manager._private_method()
# Copy methods with `manager=True` even if they are private.
manager._optin_private_method()
# Don't copy methods with `manager=False` even if they are public.
with self.assertRaises(AttributeError):
manager.optout_public_method()
# Test that the overridden method is called.
queryset = manager.filter()
self.assertQuerysetEqual(queryset, ["Bugs Bunny"], six.text_type)
self.assertEqual(queryset._filter_CustomQuerySet, True)
# Test that specialized querysets inherit from our custom queryset.
queryset = manager.values_list('first_name', flat=True).filter()
self.assertEqual(list(queryset), [six.text_type("Bugs")])
self.assertEqual(queryset._filter_CustomQuerySet, True)
# Test that the custom manager `__init__()` argument has been set.
self.assertEqual(Person.custom_queryset_custom_manager.init_arg, 'hello')
# Test that the custom manager method is only available on the manager.
Person.custom_queryset_custom_manager.manager_only()
with self.assertRaises(AttributeError):
Person.custom_queryset_custom_manager.all().manager_only()
# Test that the queryset method doesn't override the custom manager method.
queryset = Person.custom_queryset_custom_manager.filter()
self.assertQuerysetEqual(queryset, ["Bugs Bunny"], six.text_type)
self.assertEqual(queryset._filter_CustomManager, True)
# The RelatedManager used on the 'books' descriptor extends the default
# manager
self.assertIsInstance(p2.books, PublishedBookManager)

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.test import TestCase

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from datetime import date

View File

@@ -6,7 +6,7 @@ By default, Django adds an ``"id"`` field to each model. But you can override
this behavior by explicitly adding ``primary_key=True`` to a field.
"""
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.db import models

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.db import transaction, IntegrityError
from django.test import TestCase, skipIfDBFeature

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import datetime

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import datetime

View File

@@ -249,9 +249,10 @@ class DefaultFiltersTests(TestCase):
'<a href="https://google.com" rel="nofollow">https://google.com</a>')
# Check urlize doesn't overquote already quoted urls - see #9655
self.assertEqual(urlize('http://hi.baidu.com/%D6%D8%D0%C2%BF'),
'<a href="http://hi.baidu.com/%D6%D8%D0%C2%BF" rel="nofollow">'
'http://hi.baidu.com/%D6%D8%D0%C2%BF</a>')
# The teststring is the urlquoted version of 'http://hi.baidu.com/重新开始'
self.assertEqual(urlize('http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B'),
'<a href="http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B" rel="nofollow">'
'http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B</a>')
self.assertEqual(urlize('www.mystore.com/30%OffCoupons!'),
'<a href="http://www.mystore.com/30%25OffCoupons!" rel="nofollow">'
'www.mystore.com/30%OffCoupons!</a>')

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db.models.query_utils import DeferredAttribute, InvalidQuery
from django.test import TestCase

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from operator import attrgetter

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db import models, IntegrityError, connection
from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import datetime

View File

@@ -2,7 +2,5 @@
Unit-tests for the dispatch project
"""
from __future__ import absolute_import
from .test_dispatcher import DispatcherTests, ReceiverTestCase
from .test_saferef import SaferefTests

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db.models import Max
from django.test import TestCase, skipUnlessDBFeature

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.core.exceptions import ImproperlyConfigured
from django.db.models.loading import get_app
from django.test import TestCase

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.core.exceptions import FieldError
from django.db.models import F

View File

@@ -1,7 +1,7 @@
"""
Spanning tests for all the operations that F() expressions can perform.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
import datetime

View File

@@ -1,10 +1,10 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from collections import OrderedDict
import datetime
from django.contrib.auth.models import User
from django.test import TestCase
from django.utils.datastructures import SortedDict
from .models import TestObject, Order, RevisionableModel
@@ -74,7 +74,7 @@ class ExtraRegressTests(TestCase):
# select portions. Applies when portions are updated or otherwise
# moved around.
qs = User.objects.extra(
select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))),
select=OrderedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))),
select_params=(1, 3)
)
qs = qs.extra(select={"beta": 4})
@@ -180,100 +180,100 @@ class ExtraRegressTests(TestCase):
obj.save()
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()),
[{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}]
)
# Extra clauses after an empty values clause are still included
self.assertEqual(
list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
list(TestObject.objects.values().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}]
)
# Extra columns are ignored if not mentioned in the values() clause
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')),
[{'second': 'second', 'first': 'first'}]
)
# Extra columns after a non-empty values() clause are ignored
self.assertEqual(
list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
list(TestObject.objects.values('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[{'second': 'second', 'first': 'first'}]
)
# Extra columns can be partially returned
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')),
[{'second': 'second', 'foo': 'first', 'first': 'first'}]
)
# Also works if only extra columns are included
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')),
[{'foo': 'first', 'whiz': 'third'}]
)
# Values list works the same way
# All columns are returned for an empty values_list()
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()),
[('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
)
# Extra columns after an empty values_list() are still included
self.assertEqual(
list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
list(TestObject.objects.values_list().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[('first', 'second', 'third', obj.pk, 'first', 'second', 'third')]
)
# Extra columns ignored completely if not mentioned in values_list()
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')),
[('first', 'second')]
)
# Extra columns after a non-empty values_list() clause are ignored completely
self.assertEqual(
list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
list(TestObject.objects.values_list('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
[('first', 'second')]
)
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)),
['second']
)
# Only the extra columns specified in the values_list() are returned
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')),
[('first', 'second', 'third')]
)
# ...also works if only extra columns are included
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')),
[('first', 'third')]
)
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)),
['third']
)
# ... and values are returned in the order they are specified
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')),
[('third', 'first')]
)
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')),
[('first', obj.pk)]
)
self.assertEqual(
list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')),
list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')),
[('third', 'first', 'second', obj.pk)]
)

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from datetime import datetime
from django.test import TestCase

View File

@@ -2,8 +2,6 @@
Tests for field subclassing.
"""
from __future__ import absolute_import
from django.db import models
from django.utils.encoding import force_text

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.core import serializers
from django.test import TestCase

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import errno
import os

View File

@@ -1,5 +1,5 @@
#! -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import base64
import errno

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.conf.urls import patterns
from . import views

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import hashlib
import json

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import os
import gzip

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import warnings

View File

@@ -0,0 +1,2 @@
-- Deprecated search path for custom SQL -- remove in Django 1.9
INSERT INTO fixtures_model_package_book (name) VALUES ('My Deprecated Book');

View File

@@ -0,0 +1 @@
INSERT INTO fixtures_model_package_book (name) VALUES ('My Book');

View File

@@ -5,6 +5,7 @@ import warnings
from django.core import management
from django.db import transaction
from django.test import TestCase, TransactionTestCase
from django.utils.six import StringIO
from .models import Article, Book
@@ -110,3 +111,19 @@ class FixtureTestCase(TestCase):
],
lambda a: a.headline,
)
class InitialSQLTests(TestCase):
def test_custom_sql(self):
"""
#14300 -- Verify that custom_sql_for_model searches `app/sql` and not
`app/models/sql` (the old location will work until Django 1.9)
"""
out = StringIO()
management.call_command("sqlcustom", "fixtures_model_package", stdout=out)
output = out.getvalue()
self.assertTrue("INSERT INTO fixtures_model_package_book (name) "
"VALUES ('My Book')" in output)
# value from deprecated search path models/sql (remove in Django 1.9)
self.assertTrue("Deprecated Book" in output)

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.db import models

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Unittests for fixtures.
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import os
import re
@@ -181,55 +181,68 @@ class TestFixtures(TestCase):
"""
Test for ticket #4371 -- Loading a fixture file with invalid data
using explicit filename.
Validate that error conditions are caught correctly
Test for ticket #18213 -- warning conditions are caught correctly
"""
with six.assertRaisesRegex(self, management.CommandError,
"No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"):
with warnings.catch_warnings(record=True) as warning_list:
warnings.simplefilter("always")
management.call_command(
'loaddata',
'bad_fixture2.xml',
verbosity=0,
)
warning = warning_list.pop()
self.assertEqual(warning.category, RuntimeWarning)
self.assertEqual(str(warning.message), "No fixture data found for 'bad_fixture2'. (File format may be invalid.)")
def test_invalid_data_no_ext(self):
"""
Test for ticket #4371 -- Loading a fixture file with invalid data
without file extension.
Validate that error conditions are caught correctly
Test for ticket #18213 -- warning conditions are caught correctly
"""
with six.assertRaisesRegex(self, management.CommandError,
"No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"):
with warnings.catch_warnings(record=True) as warning_list:
warnings.simplefilter("always")
management.call_command(
'loaddata',
'bad_fixture2',
verbosity=0,
)
warning = warning_list.pop()
self.assertEqual(warning.category, RuntimeWarning)
self.assertEqual(str(warning.message), "No fixture data found for 'bad_fixture2'. (File format may be invalid.)")
def test_empty(self):
"""
Test for ticket #4371 -- Loading a fixture file with no data returns an error.
Validate that error conditions are caught correctly
Test for ticket #18213 -- Loading a fixture file with no data output a warning.
Previously empty fixture raises an error exception, see ticket #4371.
"""
with six.assertRaisesRegex(self, management.CommandError,
"No fixture data found for 'empty'. \(File format may be invalid.\)"):
with warnings.catch_warnings(record=True) as warning_list:
warnings.simplefilter("always")
management.call_command(
'loaddata',
'empty',
verbosity=0,
)
warning = warning_list.pop()
self.assertEqual(warning.category, RuntimeWarning)
self.assertEqual(str(warning.message), "No fixture data found for 'empty'. (File format may be invalid.)")
def test_error_message(self):
"""
(Regression for #9011 - error message is correct)
Regression for #9011 - error message is correct.
Change from error to warning for ticket #18213.
"""
with six.assertRaisesRegex(self, management.CommandError,
"^No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)$"):
with warnings.catch_warnings(record=True) as warning_list:
warnings.simplefilter("always")
management.call_command(
'loaddata',
'bad_fixture2',
'animal',
verbosity=0,
)
warning = warning_list.pop()
self.assertEqual(warning.category, RuntimeWarning)
self.assertEqual(str(warning.message), "No fixture data found for 'bad_fixture2'. (File format may be invalid.)")
def test_pg_sequence_resetting_checks(self):
"""

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db import transaction, IntegrityError, DatabaseError
from django.test import TestCase

View File

@@ -5,14 +5,15 @@ from django.db.models.fields.related import ReverseSingleRelatedObjectDescriptor
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import get_language
@python_2_unicode_compatible
class Country(models.Model):
# Table Column Fields
name = models.CharField(max_length=50)
def __unicode__(self):
def __str__(self):
return self.name
@python_2_unicode_compatible
class Person(models.Model):
# Table Column Fields
name = models.CharField(max_length=128)
@@ -26,9 +27,10 @@ class Person(models.Model):
class Meta:
ordering = ('name',)
def __unicode__(self):
def __str__(self):
return self.name
@python_2_unicode_compatible
class Group(models.Model):
# Table Column Fields
name = models.CharField(max_length=128)
@@ -38,10 +40,11 @@ class Group(models.Model):
class Meta:
ordering = ('name',)
def __unicode__(self):
def __str__(self):
return self.name
@python_2_unicode_compatible
class Membership(models.Model):
# Table Column Fields
membership_country = models.ForeignKey(Country)
@@ -51,17 +54,19 @@ class Membership(models.Model):
group_id = models.IntegerField()
# Relation Fields
person = models.ForeignObject(Person,
person = models.ForeignObject(
Person,
from_fields=['membership_country', 'person_id'],
to_fields=['person_country_id', 'id'])
group = models.ForeignObject(Group,
group = models.ForeignObject(
Group,
from_fields=['membership_country', 'group_id'],
to_fields=['group_country', 'id'])
class Meta:
ordering = ('date_joined', 'invite_reason')
def __unicode__(self):
def __str__(self):
return "%s is a member of %s" % (self.person.name, self.group.name)
@@ -73,17 +78,20 @@ class Friendship(models.Model):
to_friend_id = models.IntegerField()
# Relation Fields
from_friend = models.ForeignObject(Person,
from_friend = models.ForeignObject(
Person,
from_fields=['from_friend_country', 'from_friend_id'],
to_fields=['person_country_id', 'id'],
related_name='from_friend')
to_friend_country = models.ForeignObject(Country,
to_friend_country = models.ForeignObject(
Country,
from_fields=['to_friend_country_id'],
to_fields=['id'],
related_name='to_friend_country')
to_friend = models.ForeignObject(Person,
to_friend = models.ForeignObject(
Person,
from_fields=['to_friend_country_id', 'to_friend_id'],
to_fields=['person_country_id', 'id'],
related_name='to_friend')
@@ -140,6 +148,9 @@ class Article(models.Model):
except ArticleTranslation.DoesNotExist:
return '[No translation found]'
class NewsArticle(Article):
pass
class ArticleTranslation(models.Model):
article = models.ForeignKey(Article)
lang = models.CharField(max_length='2')
@@ -156,5 +167,6 @@ class ArticleTag(models.Model):
name = models.CharField(max_length=255)
class ArticleIdea(models.Model):
articles = models.ManyToManyField(Article, related_name="ideas", related_query_name="idea_things")
articles = models.ManyToManyField(Article, related_name="ideas",
related_query_name="idea_things")
name = models.CharField(max_length=255)

View File

@@ -1,12 +1,17 @@
import datetime
from operator import attrgetter
from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation, ArticleTag, ArticleIdea
from .models import (
Country, Person, Group, Membership, Friendship, Article,
ArticleTranslation, ArticleTag, ArticleIdea, NewsArticle)
from django.test import TestCase
from django.utils.translation import activate
from django.core.exceptions import FieldError
from django import forms
# Note that these tests are testing internal implementation details.
# ForeignObject is not part of public API.
class MultiColumnFKTests(TestCase):
def setUp(self):
# Creating countries
@@ -140,9 +145,9 @@ class MultiColumnFKTests(TestCase):
Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat)
with self.assertNumQueries(1):
people = [m.person for m in Membership.objects.select_related('person')]
people = [m.person for m in Membership.objects.select_related('person').order_by('pk')]
normal_people = [m.person for m in Membership.objects.all()]
normal_people = [m.person for m in Membership.objects.all().order_by('pk')]
self.assertEqual(people, normal_people)
def test_prefetch_foreignkey_forward_works(self):
@@ -150,19 +155,22 @@ class MultiColumnFKTests(TestCase):
Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat)
with self.assertNumQueries(2):
people = [m.person for m in Membership.objects.prefetch_related('person')]
people = [
m.person for m in Membership.objects.prefetch_related('person').order_by('pk')]
normal_people = [m.person for m in Membership.objects.all()]
normal_people = [m.person for m in Membership.objects.order_by('pk')]
self.assertEqual(people, normal_people)
def test_prefetch_foreignkey_reverse_works(self):
Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia)
Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat)
with self.assertNumQueries(2):
membership_sets = [list(p.membership_set.all())
for p in Person.objects.prefetch_related('membership_set')]
membership_sets = [
list(p.membership_set.all())
for p in Person.objects.prefetch_related('membership_set').order_by('pk')]
normal_membership_sets = [list(p.membership_set.all()) for p in Person.objects.all()]
normal_membership_sets = [list(p.membership_set.all())
for p in Person.objects.order_by('pk')]
self.assertEqual(membership_sets, normal_membership_sets)
def test_m2m_through_forward_returns_valid_members(self):
@@ -339,6 +347,20 @@ class MultiColumnFKTests(TestCase):
with self.assertRaises(FieldError):
Article.objects.filter(ideas__name="idea1")
def test_inheritance(self):
activate("fi")
na = NewsArticle.objects.create(pub_date=datetime.date.today())
ArticleTranslation.objects.create(
article=na, lang="fi", title="foo", body="bar")
self.assertQuerysetEqual(
NewsArticle.objects.select_related('active_translation'),
[na], lambda x: x
)
with self.assertNumQueries(1):
self.assertEqual(
NewsArticle.objects.select_related(
'active_translation')[0].active_translation.title,
"foo")
class FormsTests(TestCase):
# ForeignObjects should not have any form fields, currently the user needs

View File

@@ -34,7 +34,30 @@ class Defaults(models.Model):
class ChoiceModel(models.Model):
"""For ModelChoiceField and ModelMultipleChoiceField tests."""
CHOICES = [
('', 'No Preference'),
('f', 'Foo'),
('b', 'Bar'),
]
INTEGER_CHOICES = [
(None, 'No Preference'),
(1, 'Foo'),
(2, 'Bar'),
]
STRING_CHOICES_WITH_NONE = [
(None, 'No Preference'),
('f', 'Foo'),
('b', 'Bar'),
]
name = models.CharField(max_length=10)
choice = models.CharField(max_length=2, blank=True, choices=CHOICES)
choice_string_w_none = models.CharField(
max_length=2, blank=True, null=True, choices=STRING_CHOICES_WITH_NONE)
choice_integer = models.IntegerField(choices=INTEGER_CHOICES, blank=True,
null=True)
@python_2_unicode_compatible

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from .test_error_messages import (FormsErrorMessagesTestCase,
ModelChoiceFieldErrorMessagesTestCase)
from .test_extra import FormsExtraTestCase, FormsExtraL10NTestCase

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import *

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime
@@ -620,6 +620,37 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin):
self.assertTrue(f.is_valid())
self.assertEqual(f.cleaned_data['username'], 'sirrobin')
def test_changing_cleaned_data_nothing_returned(self):
class UserForm(Form):
username = CharField(max_length=10)
password = CharField(widget=PasswordInput)
def clean(self):
self.cleaned_data['username'] = self.cleaned_data['username'].lower()
# don't return anything
f = UserForm({'username': 'SirRobin', 'password': 'blue'})
self.assertTrue(f.is_valid())
self.assertEqual(f.cleaned_data['username'], 'sirrobin')
def test_changing_cleaned_data_in_clean(self):
class UserForm(Form):
username = CharField(max_length=10)
password = CharField(widget=PasswordInput)
def clean(self):
data = self.cleaned_data
# Return a different dict. We have not changed self.cleaned_data.
return {
'username': data['username'].lower(),
'password': 'this_is_not_a_secret',
}
f = UserForm({'username': 'SirRobin', 'password': 'blue'})
self.assertTrue(f.is_valid())
self.assertEqual(f.cleaned_data['username'], 'sirrobin')
def test_overriding_errorlist(self):
@python_2_unicode_compatible
class DivErrorList(ErrorList):

View File

@@ -4,6 +4,7 @@ from __future__ import unicode_literals
import datetime
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.validators import RegexValidator
from django.forms import *
from django.http import QueryDict
from django.template import Template, Context
@@ -1792,6 +1793,75 @@ class FormsTestCase(TestCase):
self.assertTrue(form.is_valid())
self.assertEqual(form.cleaned_data, {'name' : 'fname lname'})
def test_multivalue_optional_subfields(self):
class PhoneField(MultiValueField):
def __init__(self, *args, **kwargs):
fields = (
CharField(label='Country Code', validators=[
RegexValidator(r'^\+\d{1,2}$', message='Enter a valid country code.')]),
CharField(label='Phone Number'),
CharField(label='Extension', error_messages={'incomplete': 'Enter an extension.'}),
CharField(label='Label', required=False, help_text='E.g. home, work.'),
)
super(PhoneField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
if data_list:
return '%s.%s ext. %s (label: %s)' % tuple(data_list)
return None
# An empty value for any field will raise a `required` error on a
# required `MultiValueField`.
f = PhoneField()
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, [])
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, ['+61'])
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, ['+61', '287654321', '123'])
self.assertEqual('+61.287654321 ext. 123 (label: Home)', f.clean(['+61', '287654321', '123', 'Home']))
self.assertRaisesMessage(ValidationError,
"'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home'])
# Empty values for fields will NOT raise a `required` error on an
# optional `MultiValueField`
f = PhoneField(required=False)
self.assertEqual(None, f.clean(''))
self.assertEqual(None, f.clean(None))
self.assertEqual(None, f.clean([]))
self.assertEqual('+61. ext. (label: )', f.clean(['+61']))
self.assertEqual('+61.287654321 ext. 123 (label: )', f.clean(['+61', '287654321', '123']))
self.assertEqual('+61.287654321 ext. 123 (label: Home)', f.clean(['+61', '287654321', '123', 'Home']))
self.assertRaisesMessage(ValidationError,
"'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home'])
# For a required `MultiValueField` with `require_all_fields=False`, a
# `required` error will only be raised if all fields are empty. Fields
# can individually be required or optional. An empty value for any
# required field will raise an `incomplete` error.
f = PhoneField(require_all_fields=False)
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, [])
self.assertRaisesMessage(ValidationError, "'Enter a complete value.'", f.clean, ['+61'])
self.assertEqual('+61.287654321 ext. 123 (label: )', f.clean(['+61', '287654321', '123']))
six.assertRaisesRegex(self, ValidationError,
"'Enter a complete value\.', u?'Enter an extension\.'", f.clean, ['', '', '', 'Home'])
self.assertRaisesMessage(ValidationError,
"'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home'])
# For an optional `MultiValueField` with `require_all_fields=False`, we
# don't get any `required` error but we still get `incomplete` errors.
f = PhoneField(required=False, require_all_fields=False)
self.assertEqual(None, f.clean(''))
self.assertEqual(None, f.clean(None))
self.assertEqual(None, f.clean([]))
self.assertRaisesMessage(ValidationError, "'Enter a complete value.'", f.clean, ['+61'])
self.assertEqual('+61.287654321 ext. 123 (label: )', f.clean(['+61', '287654321', '123']))
six.assertRaisesRegex(self, ValidationError,
"'Enter a complete value\.', u?'Enter an extension\.'", f.clean, ['', '', '', 'Home'])
self.assertRaisesMessage(ValidationError,
"'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home'])
def test_custom_empty_values(self):
"""
Test that form fields can customize what is considered as an empty value
@@ -1824,7 +1894,7 @@ class FormsTestCase(TestCase):
# passing just one argument: overrides the field's label
(('custom',), {}, '<label for="id_field">custom:</label>'),
# the overriden label is escaped
# the overridden label is escaped
(('custom&',), {}, '<label for="id_field">custom&amp;:</label>'),
((mark_safe('custom&'),), {}, '<label for="id_field">custom&:</label>'),
@@ -1870,3 +1940,13 @@ class FormsTestCase(TestCase):
boundfield = SomeForm()['field']
self.assertHTMLEqual(boundfield.label_tag(), '<label for="id_field"></label>')
def test_label_tag_override(self):
"""
BoundField label_suffix (if provided) overrides Form label_suffix
"""
class SomeForm(Form):
field = CharField()
boundfield = SomeForm(label_suffix='!')['field']
self.assertHTMLEqual(boundfield.label_tag(label_suffix='$'), '<label for="id_field">Field$</label>')

View File

@@ -849,6 +849,14 @@ beatle J R Ringo False""")
w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'})
self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />')
# Test needs_multipart_form=True if any widget needs it
w = MyMultiWidget(widgets=(TextInput(), FileInput()))
self.assertTrue(w.needs_multipart_form)
# Test needs_multipart_form=False if no widget needs it
w = MyMultiWidget(widgets=(TextInput(), TextInput()))
self.assertFalse(w.needs_multipart_form)
def test_splitdatetime(self):
w = SplitDateTimeWidget()
self.assertHTMLEqual(w.render('date', ''), '<input type="text" name="date_0" /><input type="text" name="date_1" />')

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
import datetime
@@ -10,8 +10,8 @@ from django.forms.models import ModelFormMetaclass
from django.test import TestCase
from django.utils import six
from ..models import (ChoiceOptionModel, ChoiceFieldModel, FileModel, Group,
BoundaryModel, Defaults, OptionalMultiChoiceModel)
from ..models import (ChoiceModel, ChoiceOptionModel, ChoiceFieldModel,
FileModel, Group, BoundaryModel, Defaults, OptionalMultiChoiceModel)
class ChoiceFieldForm(ModelForm):
@@ -34,6 +34,24 @@ class ChoiceFieldExclusionForm(ModelForm):
model = ChoiceFieldModel
class EmptyCharLabelChoiceForm(ModelForm):
class Meta:
model = ChoiceModel
fields = ['name', 'choice']
class EmptyIntegerLabelChoiceForm(ModelForm):
class Meta:
model = ChoiceModel
fields = ['name', 'choice_integer']
class EmptyCharLabelNoneChoiceForm(ModelForm):
class Meta:
model = ChoiceModel
fields = ['name', 'choice_string_w_none']
class FileForm(Form):
file1 = FileField()
@@ -259,3 +277,78 @@ class ManyToManyExclusionTestCase(TestCase):
self.assertEqual(form.instance.choice_int.pk, data['choice_int'])
self.assertEqual(list(form.instance.multi_choice.all()), [opt2, opt3])
self.assertEqual([obj.pk for obj in form.instance.multi_choice_int.all()], data['multi_choice_int'])
class EmptyLabelTestCase(TestCase):
def test_empty_field_char(self):
f = EmptyCharLabelChoiceForm()
self.assertHTMLEqual(f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p>
<p><label for="id_choice">Choice:</label> <select id="id_choice" name="choice">
<option value="" selected="selected">No Preference</option>
<option value="f">Foo</option>
<option value="b">Bar</option>
</select></p>""")
def test_empty_field_char_none(self):
f = EmptyCharLabelNoneChoiceForm()
self.assertHTMLEqual(f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p>
<p><label for="id_choice_string_w_none">Choice string w none:</label> <select id="id_choice_string_w_none" name="choice_string_w_none">
<option value="" selected="selected">No Preference</option>
<option value="f">Foo</option>
<option value="b">Bar</option>
</select></p>""")
def test_save_empty_label_forms(self):
# Test that saving a form with a blank choice results in the expected
# value being stored in the database.
tests = [
(EmptyCharLabelNoneChoiceForm, 'choice_string_w_none', None),
(EmptyIntegerLabelChoiceForm, 'choice_integer', None),
(EmptyCharLabelChoiceForm, 'choice', ''),
]
for form, key, expected in tests:
f = form({'name': 'some-key', key: ''})
self.assertTrue(f.is_valid())
m = f.save()
self.assertEqual(expected, getattr(m, key))
self.assertEqual('No Preference',
getattr(m, 'get_{0}_display'.format(key))())
def test_empty_field_integer(self):
f = EmptyIntegerLabelChoiceForm()
self.assertHTMLEqual(f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p>
<p><label for="id_choice_integer">Choice integer:</label> <select id="id_choice_integer" name="choice_integer">
<option value="" selected="selected">No Preference</option>
<option value="1">Foo</option>
<option value="2">Bar</option>
</select></p>""")
def test_get_display_value_on_none(self):
m = ChoiceModel.objects.create(name='test', choice='', choice_integer=None)
self.assertEqual(None, m.choice_integer)
self.assertEqual('No Preference', m.get_choice_integer_display())
def test_html_rendering_of_prepopulated_models(self):
none_model = ChoiceModel(name='none-test', choice_integer=None)
f = EmptyIntegerLabelChoiceForm(instance=none_model)
self.assertHTMLEqual(f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" value="none-test"/></p>
<p><label for="id_choice_integer">Choice integer:</label> <select id="id_choice_integer" name="choice_integer">
<option value="" selected="selected">No Preference</option>
<option value="1">Foo</option>
<option value="2">Bar</option>
</select></p>""")
foo_model = ChoiceModel(name='foo-test', choice_integer=1)
f = EmptyIntegerLabelChoiceForm(instance=foo_model)
self.assertHTMLEqual(f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" value="foo-test"/></p>
<p><label for="id_choice_integer">Choice integer:</label> <select id="id_choice_integer" name="choice_integer">
<option value="">No Preference</option>
<option value="1" selected="selected">Foo</option>
<option value="2">Bar</option>
</select></p>""")

View File

@@ -1,5 +1,3 @@
from __future__ import absolute_import
from django.contrib import admin
from django.contrib.contenttypes import generic

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from __future__ import unicode_literals
from django.conf import settings
from django.contrib import admin
@@ -325,3 +325,23 @@ class GenericInlineModelAdminTest(TestCase):
self.assertEqual(
list(list(ma.get_formsets(request))[0]().forms[0].fields),
['description', 'keywords', 'id', 'DELETE'])
def test_get_fieldsets(self):
# Test that get_fieldsets is called when figuring out form fields.
# Refs #18681.
class MediaForm(ModelForm):
class Meta:
model = Media
fields = '__all__'
class MediaInline(GenericTabularInline):
form = MediaForm
model = Media
can_delete = False
def get_fieldsets(self, request, obj=None):
return [(None, {'fields': ['url', 'description']})]
ma = MediaInline(Media, self.site)
form = ma.get_formset(None).form
self.assertEqual(form._meta.fields, ['url', 'description'])

Some files were not shown because too many files have changed in this diff Show More