diff options
| author | Justin Bronn <jbronn@gmail.com> | 2008-07-04 20:16:22 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2008-07-04 20:16:22 +0000 |
| commit | bc3d6b49089ef0ea7d174eb383b1019b6a4a061a (patch) | |
| tree | b95728880e8a968ae6a921a21cec501f6a278d08 /tests/regressiontests | |
| parent | aef8a8305d2190b00386b8b1603deb03a2949f5b (diff) | |
gis: Merged revisions 7772-7808,7811-7814,7816-7823,7826-7829,7831-7833,7835 via svnmerge from trunk. Modified `GeoWhereNode` accordingly for changes in r7835.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7836 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests/regressiontests')
28 files changed, 790 insertions, 129 deletions
diff --git a/tests/regressiontests/bug639/tests.py b/tests/regressiontests/bug639/tests.py index f9596d06cb..2726dec897 100644 --- a/tests/regressiontests/bug639/tests.py +++ b/tests/regressiontests/bug639/tests.py @@ -9,6 +9,7 @@ import unittest from regressiontests.bug639.models import Photo from django.http import QueryDict from django.utils.datastructures import MultiValueDict +from django.core.files.uploadedfile import SimpleUploadedFile class Bug639Test(unittest.TestCase): @@ -21,12 +22,8 @@ class Bug639Test(unittest.TestCase): # Fake a request query dict with the file qd = QueryDict("title=Testing&image=", mutable=True) - qd["image_file"] = { - "filename" : "test.jpg", - "content-type" : "image/jpeg", - "content" : img - } - + qd["image_file"] = SimpleUploadedFile('test.jpg', img, 'image/jpeg') + manip = Photo.AddManipulator() manip.do_html2python(qd) p = manip.save(qd) @@ -39,4 +36,4 @@ class Bug639Test(unittest.TestCase): Make sure to delete the "uploaded" file to avoid clogging /tmp. """ p = Photo.objects.get() - os.unlink(p.get_image_filename())
\ No newline at end of file + os.unlink(p.get_image_filename()) diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py index d6141b09ce..62c57bc019 100644 --- a/tests/regressiontests/datastructures/tests.py +++ b/tests/regressiontests/datastructures/tests.py @@ -117,14 +117,25 @@ Init from sequence of tuples >>> d['person']['2']['firstname'] ['Adrian'] -### FileDict ################################################################ - ->>> d = FileDict({'content': 'once upon a time...'}) ->>> repr(d) -"{'content': '<omitted>'}" ->>> d = FileDict({'other-key': 'once upon a time...'}) +### ImmutableList ################################################################ +>>> d = ImmutableList(range(10)) +>>> d.sort() +Traceback (most recent call last): + File "<stdin>", line 1, in <module> + File "/var/lib/python-support/python2.5/django/utils/datastructures.py", line 359, in complain + raise AttributeError, self.warning +AttributeError: ImmutableList object is immutable. >>> repr(d) -"{'other-key': 'once upon a time...'}" +'(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)' +>>> d = ImmutableList(range(10), warning="Object is immutable!") +>>> d[1] +1 +>>> d[1] = 'test' +Traceback (most recent call last): + File "<stdin>", line 1, in <module> + File "/var/lib/python-support/python2.5/django/utils/datastructures.py", line 359, in complain + raise AttributeError, self.warning +AttributeError: Object is immutable! ### DictWrapper ############################################################# diff --git a/tests/regressiontests/extra_regress/__init__.py b/tests/regressiontests/extra_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/extra_regress/__init__.py diff --git a/tests/regressiontests/extra_regress/models.py b/tests/regressiontests/extra_regress/models.py new file mode 100644 index 0000000000..e6665222bf --- /dev/null +++ b/tests/regressiontests/extra_regress/models.py @@ -0,0 +1,55 @@ +import copy + +from django.db import models +from django.db.models.query import Q + + +class RevisionableModel(models.Model): + base = models.ForeignKey('self', null=True) + title = models.CharField(blank=True, max_length=255) + + def __unicode__(self): + return u"%s (%s, %s)" % (self.title, self.id, self.base.id) + + def save(self): + super(RevisionableModel, self).save() + if not self.base: + self.base = self + super(RevisionableModel, self).save() + + def new_revision(self): + new_revision = copy.copy(self) + new_revision.pk = None + return new_revision + +__test__ = {"API_TESTS": """ +### Regression tests for #7314 and #7372 + +>>> rm = RevisionableModel.objects.create(title='First Revision') +>>> rm.pk, rm.base.pk +(1, 1) + +>>> rm2 = rm.new_revision() +>>> rm2.title = "Second Revision" +>>> rm2.save() +>>> print u"%s of %s" % (rm2.title, rm2.base.title) +Second Revision of First Revision + +>>> rm2.pk, rm2.base.pk +(2, 1) + +Queryset to match most recent revision: +>>> qs = RevisionableModel.objects.extra(where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {'table': RevisionableModel._meta.db_table,}],) +>>> qs +[<RevisionableModel: Second Revision (2, 1)>] + +Queryset to search for string in title: +>>> qs2 = RevisionableModel.objects.filter(title__contains="Revision") +>>> qs2 +[<RevisionableModel: First Revision (1, 1)>, <RevisionableModel: Second Revision (2, 1)>] + +Following queryset should return the most recent revision: +>>> qs & qs2 +[<RevisionableModel: Second Revision (2, 1)>] + +"""} diff --git a/tests/regressiontests/file_uploads/__init__.py b/tests/regressiontests/file_uploads/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/file_uploads/__init__.py diff --git a/tests/regressiontests/file_uploads/models.py b/tests/regressiontests/file_uploads/models.py new file mode 100644 index 0000000000..2d5607b2a7 --- /dev/null +++ b/tests/regressiontests/file_uploads/models.py @@ -0,0 +1,2 @@ +# This file unintentionally left blank. +# Oops.
\ No newline at end of file diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py new file mode 100644 index 0000000000..8992298470 --- /dev/null +++ b/tests/regressiontests/file_uploads/tests.py @@ -0,0 +1,158 @@ +import os +import sha +import tempfile +from django.test import TestCase, client +from django.utils import simplejson + +class FileUploadTests(TestCase): + def test_simple_upload(self): + post_data = { + 'name': 'Ringo', + 'file_field': open(__file__), + } + response = self.client.post('/file_uploads/upload/', post_data) + self.assertEqual(response.status_code, 200) + + def test_large_upload(self): + tdir = tempfile.gettempdir() + + file1 = tempfile.NamedTemporaryFile(suffix=".file1", dir=tdir) + file1.write('a' * (2 ** 21)) + file1.seek(0) + + file2 = tempfile.NamedTemporaryFile(suffix=".file2", dir=tdir) + file2.write('a' * (10 * 2 ** 20)) + file2.seek(0) + + # This file contains chinese symbols for a name. + file3 = open(os.path.join(tdir, u'test_中文_Orl\u00e9ans.jpg'), 'w+b') + file3.write('b' * (2 ** 10)) + file3.seek(0) + + post_data = { + 'name': 'Ringo', + 'file_field1': open(file1.name), + 'file_field2': open(file2.name), + 'file_unicode': file3, + } + + for key in post_data.keys(): + try: + post_data[key + '_hash'] = sha.new(post_data[key].read()).hexdigest() + post_data[key].seek(0) + except AttributeError: + post_data[key + '_hash'] = sha.new(post_data[key]).hexdigest() + + response = self.client.post('/file_uploads/verify/', post_data) + + try: + os.unlink(file3.name) + except: + pass + + self.assertEqual(response.status_code, 200) + + def test_dangerous_file_names(self): + """Uploaded file names should be sanitized before ever reaching the view.""" + # This test simulates possible directory traversal attacks by a + # malicious uploader We have to do some monkeybusiness here to construct + # a malicious payload with an invalid file name (containing os.sep or + # os.pardir). This similar to what an attacker would need to do when + # trying such an attack. + scary_file_names = [ + "/tmp/hax0rd.txt", # Absolute path, *nix-style. + "C:\\Windows\\hax0rd.txt", # Absolute path, win-syle. + "C:/Windows/hax0rd.txt", # Absolute path, broken-style. + "\\tmp\\hax0rd.txt", # Absolute path, broken in a different way. + "/tmp\\hax0rd.txt", # Absolute path, broken by mixing. + "subdir/hax0rd.txt", # Descendant path, *nix-style. + "subdir\\hax0rd.txt", # Descendant path, win-style. + "sub/dir\\hax0rd.txt", # Descendant path, mixed. + "../../hax0rd.txt", # Relative path, *nix-style. + "..\\..\\hax0rd.txt", # Relative path, win-style. + "../..\\hax0rd.txt" # Relative path, mixed. + ] + + payload = [] + for i, name in enumerate(scary_file_names): + payload.extend([ + '--' + client.BOUNDARY, + 'Content-Disposition: form-data; name="file%s"; filename="%s"' % (i, name), + 'Content-Type: application/octet-stream', + '', + 'You got pwnd.' + ]) + payload.extend([ + '--' + client.BOUNDARY + '--', + '', + ]) + + payload = "\r\n".join(payload) + r = { + 'CONTENT_LENGTH': len(payload), + 'CONTENT_TYPE': client.MULTIPART_CONTENT, + 'PATH_INFO': "/file_uploads/echo/", + 'REQUEST_METHOD': 'POST', + 'wsgi.input': client.FakePayload(payload), + } + response = self.client.request(**r) + + # The filenames should have been sanitized by the time it got to the view. + recieved = simplejson.loads(response.content) + for i, name in enumerate(scary_file_names): + got = recieved["file%s" % i] + self.assertEqual(got, "hax0rd.txt") + + def test_filename_overflow(self): + """File names over 256 characters (dangerous on some platforms) get fixed up.""" + name = "%s.txt" % ("f"*500) + payload = "\r\n".join([ + '--' + client.BOUNDARY, + 'Content-Disposition: form-data; name="file"; filename="%s"' % name, + 'Content-Type: application/octet-stream', + '', + 'Oops.' + '--' + client.BOUNDARY + '--', + '', + ]) + r = { + 'CONTENT_LENGTH': len(payload), + 'CONTENT_TYPE': client.MULTIPART_CONTENT, + 'PATH_INFO': "/file_uploads/echo/", + 'REQUEST_METHOD': 'POST', + 'wsgi.input': client.FakePayload(payload), + } + got = simplejson.loads(self.client.request(**r).content) + self.assert_(len(got['file']) < 256, "Got a long file name (%s characters)." % len(got['file'])) + + def test_custom_upload_handler(self): + # A small file (under the 5M quota) + smallfile = tempfile.NamedTemporaryFile() + smallfile.write('a' * (2 ** 21)) + + # A big file (over the quota) + bigfile = tempfile.NamedTemporaryFile() + bigfile.write('a' * (10 * 2 ** 20)) + + # Small file posting should work. + response = self.client.post('/file_uploads/quota/', {'f': open(smallfile.name)}) + got = simplejson.loads(response.content) + self.assert_('f' in got) + + # Large files don't go through. + response = self.client.post("/file_uploads/quota/", {'f': open(bigfile.name)}) + got = simplejson.loads(response.content) + self.assert_('f' not in got) + + def test_broken_custom_upload_handler(self): + f = tempfile.NamedTemporaryFile() + f.write('a' * (2 ** 21)) + + # AttributeError: You cannot alter upload handlers after the upload has been processed. + self.assertRaises( + AttributeError, + self.client.post, + '/file_uploads/quota/broken/', + {'f': open(f.name)} + ) +
\ No newline at end of file diff --git a/tests/regressiontests/file_uploads/uploadhandler.py b/tests/regressiontests/file_uploads/uploadhandler.py new file mode 100644 index 0000000000..54f82f626c --- /dev/null +++ b/tests/regressiontests/file_uploads/uploadhandler.py @@ -0,0 +1,26 @@ +""" +Upload handlers to test the upload API. +""" + +from django.core.files.uploadhandler import FileUploadHandler, StopUpload + +class QuotaUploadHandler(FileUploadHandler): + """ + This test upload handler terminates the connection if more than a quota + (5MB) is uploaded. + """ + + QUOTA = 5 * 2**20 # 5 MB + + def __init__(self, request=None): + super(QuotaUploadHandler, self).__init__(request) + self.total_upload = 0 + + def receive_data_chunk(self, raw_data, start): + self.total_upload += len(raw_data) + if self.total_upload >= self.QUOTA: + raise StopUpload(connection_reset=True) + return raw_data + + def file_complete(self, file_size): + return None
\ No newline at end of file diff --git a/tests/regressiontests/file_uploads/urls.py b/tests/regressiontests/file_uploads/urls.py new file mode 100644 index 0000000000..529bee312d --- /dev/null +++ b/tests/regressiontests/file_uploads/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls.defaults import * +import views + +urlpatterns = patterns('', + (r'^upload/$', views.file_upload_view), + (r'^verify/$', views.file_upload_view_verify), + (r'^echo/$', views.file_upload_echo), + (r'^quota/$', views.file_upload_quota), + (r'^quota/broken/$', views.file_upload_quota_broken), +) diff --git a/tests/regressiontests/file_uploads/views.py b/tests/regressiontests/file_uploads/views.py new file mode 100644 index 0000000000..833cf90531 --- /dev/null +++ b/tests/regressiontests/file_uploads/views.py @@ -0,0 +1,70 @@ +import os +import sha +from django.core.files.uploadedfile import UploadedFile +from django.http import HttpResponse, HttpResponseServerError +from django.utils import simplejson +from uploadhandler import QuotaUploadHandler + +def file_upload_view(request): + """ + Check that a file upload can be updated into the POST dictionary without + going pear-shaped. + """ + form_data = request.POST.copy() + form_data.update(request.FILES) + if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], unicode): + # If a file is posted, the dummy client should only post the file name, + # not the full path. + if os.path.dirname(form_data['file_field'].file_name) != '': + return HttpResponseServerError() + return HttpResponse('') + else: + return HttpResponseServerError() + +def file_upload_view_verify(request): + """ + Use the sha digest hash to verify the uploaded contents. + """ + form_data = request.POST.copy() + form_data.update(request.FILES) + + # Check to see if unicode names worked out. + if not request.FILES['file_unicode'].file_name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'): + return HttpResponseServerError() + + for key, value in form_data.items(): + if key.endswith('_hash'): + continue + if key + '_hash' not in form_data: + continue + submitted_hash = form_data[key + '_hash'] + if isinstance(value, UploadedFile): + new_hash = sha.new(value.read()).hexdigest() + else: + new_hash = sha.new(value).hexdigest() + if new_hash != submitted_hash: + return HttpResponseServerError() + + return HttpResponse('') + +def file_upload_echo(request): + """ + Simple view to echo back info about uploaded files for tests. + """ + r = dict([(k, f.file_name) for k, f in request.FILES.items()]) + return HttpResponse(simplejson.dumps(r)) + +def file_upload_quota(request): + """ + Dynamically add in an upload handler. + """ + request.upload_handlers.insert(0, QuotaUploadHandler()) + return file_upload_echo(request) + +def file_upload_quota_broken(request): + """ + You can't change handlers after reading FILES; this view shouldn't work. + """ + response = file_upload_echo(request) + request.upload_handlers.insert(0, QuotaUploadHandler()) + return response
\ No newline at end of file diff --git a/tests/regressiontests/fixtures_regress/fixtures/big-fixture.json b/tests/regressiontests/fixtures_regress/fixtures/big-fixture.json new file mode 100644 index 0000000000..e655fbbf3f --- /dev/null +++ b/tests/regressiontests/fixtures_regress/fixtures/big-fixture.json @@ -0,0 +1,83 @@ +[ + { + "pk": 6, + "model": "fixtures_regress.channel", + "fields": { + "name": "Business" + } + }, + + { + "pk": 1, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 1", + "channels": [6] + } + }, + { + "pk": 2, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 2", + "channels": [6] + } + }, + { + "pk": 3, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 3", + "channels": [6] + } + }, + { + "pk": 4, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 4", + "channels": [6] + } + }, + + { + "pk": 5, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 5", + "channels": [6] + } + }, + { + "pk": 6, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 6", + "channels": [6] + } + }, + { + "pk": 7, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 7", + "channels": [6] + } + }, + { + "pk": 8, + "model": "fixtures_regress.article", + "fields": { + "title": "Article Title 8", + "channels": [6] + } + }, + { + "pk": 9, + "model": "fixtures_regress.article", + "fields": { + "title": "Yet Another Article", + "channels": [6] + } + } +]
\ No newline at end of file diff --git a/tests/regressiontests/fixtures_regress/fixtures/model-inheritance.json b/tests/regressiontests/fixtures_regress/fixtures/model-inheritance.json new file mode 100644 index 0000000000..00c482b3dd --- /dev/null +++ b/tests/regressiontests/fixtures_regress/fixtures/model-inheritance.json @@ -0,0 +1,4 @@ +[ + {"pk": 1, "model": "fixtures_regress.parent", "fields": {"name": "fred"}}, + {"pk": 1, "model": "fixtures_regress.child", "fields": {"data": "apple"}} +] diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py index 59fc167d50..2c048dc0b8 100644 --- a/tests/regressiontests/fixtures_regress/models.py +++ b/tests/regressiontests/fixtures_regress/models.py @@ -20,7 +20,7 @@ class Plant(models.Model): class Stuff(models.Model): name = models.CharField(max_length=20, null=True) owner = models.ForeignKey(User, null=True) - + def __unicode__(self): # Oracle doesn't distinguish between None and the empty string. # This hack makes the test case pass using Oracle. @@ -38,13 +38,29 @@ class Absolute(models.Model): super(Absolute, self).__init__(*args, **kwargs) Absolute.load_count += 1 +class Parent(models.Model): + name = models.CharField(max_length=10) + +class Child(Parent): + data = models.CharField(max_length=10) + +# Models to regresison check #7572 +class Channel(models.Model): + name = models.CharField(max_length=255) + +class Article(models.Model): + title = models.CharField(max_length=255) + channels = models.ManyToManyField(Channel) + + class Meta: + ordering = ('id',) __test__ = {'API_TESTS':""" >>> from django.core import management # Load a fixture that uses PK=1 >>> management.call_command('loaddata', 'sequence', verbosity=0) - + # Create a new animal. Without a sequence reset, this new object # will take a PK of 1 (on Postgres), and the save will fail. # This is a regression test for ticket #3790. @@ -61,9 +77,9 @@ __test__ = {'API_TESTS':""" [<Stuff: None is owned by None>] ############################################### -# Regression test for ticket #6436 -- +# Regression test for ticket #6436 -- # os.path.join will throw away the initial parts of a path if it encounters -# an absolute path. This means that if a fixture is specified as an absolute path, +# an absolute path. This means that if a fixture is specified as an absolute path, # we need to make sure we don't discover the absolute path in every fixture directory. >>> load_absolute_path = os.path.join(os.path.dirname(__file__), 'fixtures', 'absolute.json') @@ -94,4 +110,28 @@ No fixture data found for 'bad_fixture2'. (File format may be invalid.) >>> sys.stderr = savestderr +############################################### +# Test for ticket #7565 -- PostgreSQL sequence resetting checks shouldn't +# ascend to parent models when inheritance is used (since they are treated +# individually). + +>>> management.call_command('loaddata', 'model-inheritance.json', verbosity=0) + +############################################### +# Test for ticket #7572 -- MySQL has a problem if the same connection is +# used to create tables, load data, and then query over that data. +# To compensate, we close the connection after running loaddata. +# This ensures that a new connection is opened when test queries are issued. + +>>> management.call_command('loaddata', 'big-fixture.json', verbosity=0) + +>>> articles = Article.objects.exclude(id=9) +>>> articles.values_list('id', flat=True) +[1, 2, 3, 4, 5, 6, 7, 8] + +# Just for good measure, run the same query again. Under the influence of +# ticket #7572, this will give a different result to the previous call. +>>> articles.values_list('id', flat=True) +[1, 2, 3, 4, 5, 6, 7, 8] + """} diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py index 9f972f5b90..580326f894 100644 --- a/tests/regressiontests/forms/error_messages.py +++ b/tests/regressiontests/forms/error_messages.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- tests = r""" >>> from django.newforms import * +>>> from django.core.files.uploadedfile import SimpleUploadedFile # CharField ################################################################### @@ -214,11 +215,11 @@ ValidationError: [u'REQUIRED'] Traceback (most recent call last): ... ValidationError: [u'INVALID'] ->>> f.clean({}) +>>> f.clean(SimpleUploadedFile('name', None)) Traceback (most recent call last): ... -ValidationError: [u'MISSING'] ->>> f.clean({'filename': 'name', 'content':''}) +ValidationError: [u'EMPTY FILE'] +>>> f.clean(SimpleUploadedFile('name', '')) Traceback (most recent call last): ... ValidationError: [u'EMPTY FILE'] @@ -237,7 +238,7 @@ ValidationError: [u'REQUIRED'] Traceback (most recent call last): ... ValidationError: [u'INVALID'] ->>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') +>>> f.clean('http://www.broken.djangoproject.com') Traceback (most recent call last): ... ValidationError: [u'INVALID LINK'] diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py index f3b6a96a1e..4725c3ecf3 100644 --- a/tests/regressiontests/forms/fields.py +++ b/tests/regressiontests/forms/fields.py @@ -2,6 +2,7 @@ tests = r""" >>> from django.newforms import * >>> from django.newforms.widgets import RadioFieldRenderer +>>> from django.core.files.uploadedfile import SimpleUploadedFile >>> import datetime >>> import time >>> import re @@ -770,17 +771,17 @@ ValidationError: [u'This field is required.'] >>> f.clean(None, 'files/test2.pdf') 'files/test2.pdf' ->>> f.clean({}) +>>> f.clean(SimpleUploadedFile('', '')) Traceback (most recent call last): ... -ValidationError: [u'No file was submitted.'] +ValidationError: [u'No file was submitted. Check the encoding type on the form.'] ->>> f.clean({}, '') +>>> f.clean(SimpleUploadedFile('', ''), '') Traceback (most recent call last): ... -ValidationError: [u'No file was submitted.'] +ValidationError: [u'No file was submitted. Check the encoding type on the form.'] ->>> f.clean({}, 'files/test3.pdf') +>>> f.clean(None, 'files/test3.pdf') 'files/test3.pdf' >>> f.clean('some content that is not a file') @@ -788,20 +789,20 @@ Traceback (most recent call last): ... ValidationError: [u'No file was submitted. Check the encoding type on the form.'] ->>> f.clean({'filename': 'name', 'content': None}) +>>> f.clean(SimpleUploadedFile('name', None)) Traceback (most recent call last): ... ValidationError: [u'The submitted file is empty.'] ->>> f.clean({'filename': 'name', 'content': ''}) +>>> f.clean(SimpleUploadedFile('name', '')) Traceback (most recent call last): ... ValidationError: [u'The submitted file is empty.'] ->>> type(f.clean({'filename': 'name', 'content': 'Some File Content'})) +>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'))) <class 'django.newforms.fields.UploadedFile'> ->>> type(f.clean({'filename': 'name', 'content': 'Some File Content'}, 'files/test4.pdf')) +>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf')) <class 'django.newforms.fields.UploadedFile'> # URLField ################################################################## @@ -887,7 +888,7 @@ u'http://www.google.com' Traceback (most recent call last): ... ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') # bad domain +>>> f.clean('http://www.broken.djangoproject.com') # bad domain Traceback (most recent call last): ... ValidationError: [u'This URL appears to be a broken link.'] @@ -937,18 +938,24 @@ ValidationError: [u'This field is required.'] >>> f.clean(True) True >>> f.clean(False) -False +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] >>> f.clean(1) True >>> f.clean(0) -False +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] >>> f.clean('Django rocks') True >>> f.clean('True') True >>> f.clean('False') -False +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] >>> f = BooleanField(required=False) >>> f.clean('') diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py index 7fc206de4c..041fa4054c 100644 --- a/tests/regressiontests/forms/forms.py +++ b/tests/regressiontests/forms/forms.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- tests = r""" >>> from django.newforms import * +>>> from django.core.files.uploadedfile import SimpleUploadedFile >>> import datetime >>> import time >>> import re @@ -1465,7 +1466,7 @@ not request.POST. >>> print f <tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> ->>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False) +>>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', '')}, auto_id=False) >>> print f <tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> @@ -1473,7 +1474,7 @@ not request.POST. >>> print f <tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr> ->>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False) +>>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', 'some content')}, auto_id=False) >>> print f <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> >>> f.is_valid() diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py index 4e49df1555..429bdd7558 100644 --- a/tests/regressiontests/many_to_one_regress/models.py +++ b/tests/regressiontests/many_to_one_regress/models.py @@ -28,6 +28,24 @@ class Child(models.Model): parent = models.ForeignKey(Parent) +# Multiple paths to the same model (#7110, #7125) +class Category(models.Model): + name = models.CharField(max_length=20) + + def __unicode__(self): + return self.name + +class Record(models.Model): + category = models.ForeignKey(Category) + +class Relation(models.Model): + left = models.ForeignKey(Record, related_name='left_set') + right = models.ForeignKey(Record, related_name='right_set') + + def __unicode__(self): + return u"%s - %s" % (self.left.category.name, self.right.category.name) + + __test__ = {'API_TESTS':""" >>> Third.objects.create(id='3', name='An example') <Third: Third object> @@ -73,4 +91,26 @@ Traceback (most recent call last): ... ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance. +# Test of multiple ForeignKeys to the same model (bug #7125) + +>>> c1 = Category.objects.create(name='First') +>>> c2 = Category.objects.create(name='Second') +>>> c3 = Category.objects.create(name='Third') +>>> r1 = Record.objects.create(category=c1) +>>> r2 = Record.objects.create(category=c1) +>>> r3 = Record.objects.create(category=c2) +>>> r4 = Record.objects.create(category=c2) +>>> r5 = Record.objects.create(category=c3) +>>> r = Relation.objects.create(left=r1, right=r2) +>>> r = Relation.objects.create(left=r3, right=r4) +>>> r = Relation.objects.create(left=r1, right=r3) +>>> r = Relation.objects.create(left=r5, right=r2) +>>> r = Relation.objects.create(left=r3, right=r2) + +>>> Relation.objects.filter(left__category__name__in=['First'], right__category__name__in=['Second']) +[<Relation: First - Second>] + +>>> Category.objects.filter(record__left_set__right__category__name='Second').order_by('name') +[<Category: First>, <Category: Second>] + """} diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index e279a0669f..c2ba9ee008 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -15,4 +15,21 @@ Decimal("3.14") Traceback (most recent call last): ... ValidationError: [u'This value must be a decimal number.'] + +>>> f = DecimalField(max_digits=5, decimal_places=1) +>>> x = f.to_python(2) +>>> y = f.to_python('2.6') + +>>> f.get_db_prep_save(x) +u'2.0' +>>> f.get_db_prep_save(y) +u'2.6' +>>> f.get_db_prep_save(None) +>>> f.get_db_prep_lookup('exact', x) +[u'2.0'] +>>> f.get_db_prep_lookup('exact', y) +[u'2.6'] +>>> f.get_db_prep_lookup('exact', None) +[None] + """ diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py index 33e2e0e4f6..24d6186150 100644 --- a/tests/regressiontests/model_inheritance_regress/models.py +++ b/tests/regressiontests/model_inheritance_regress/models.py @@ -131,4 +131,26 @@ __test__ = {'API_TESTS':""" >>> Child.objects.dates('created', 'month') [datetime.datetime(2008, 6, 1, 0, 0)] +# Regression test for #7276: calling delete() on a model with multi-table +# inheritance should delete the associated rows from any ancestor tables, as +# well as any descendent objects. + +>>> ident = ItalianRestaurant.objects.all()[0].id +>>> Place.objects.get(pk=ident) +<Place: Guido's All New House of Pasta the place> +>>> xx = Restaurant.objects.create(name='a', address='xx', serves_hot_dogs=True, serves_pizza=False) + +# This should delete both Restuarants, plus the related places, plus the ItalianRestaurant. +>>> Restaurant.objects.all().delete() + +>>> Place.objects.get(pk=ident) +Traceback (most recent call last): +... +DoesNotExist: Place matching query does not exist. + +>>> ItalianRestaurant.objects.get(pk=ident) +Traceback (most recent call last): +... +DoesNotExist: ItalianRestaurant matching query does not exist. + """} diff --git a/tests/regressiontests/model_inheritance_select_related/__init__.py b/tests/regressiontests/model_inheritance_select_related/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/model_inheritance_select_related/__init__.py diff --git a/tests/regressiontests/model_inheritance_select_related/models.py b/tests/regressiontests/model_inheritance_select_related/models.py new file mode 100644 index 0000000000..a97ecaadea --- /dev/null +++ b/tests/regressiontests/model_inheritance_select_related/models.py @@ -0,0 +1,47 @@ +""" +Regression tests for the interaction between model inheritance and +select_related(). +""" + +from django.db import models + +class Place(models.Model): + name = models.CharField(max_length=50) + + class Meta: + ordering = ('name',) + + def __unicode__(self): + return u"%s the place" % self.name + +class Restaurant(Place): + serves_sushi = models.BooleanField() + serves_steak = models.BooleanField() + + def __unicode__(self): + return u"%s the restaurant" % self.name + +class Person(models.Model): + name = models.CharField(max_length=50) + favorite_restaurant = models.ForeignKey(Restaurant) + + def __unicode__(self): + return self.name + +__test__ = {'API_TESTS':""" +Regression test for #7246 + +>>> r1 = Restaurant.objects.create(name="Nobu", serves_sushi=True, serves_steak=False) +>>> r2 = Restaurant.objects.create(name="Craft", serves_sushi=False, serves_steak=True) +>>> p1 = Person.objects.create(name="John", favorite_restaurant=r1) +>>> p2 = Person.objects.create(name="Jane", favorite_restaurant=r2) + +>>> Person.objects.order_by('name').select_related() +[<Person: Jane>, <Person: John>] + +>>> jane = Person.objects.order_by('name').select_related('favorite_restaurant')[0] +>>> jane.favorite_restaurant.name +u'Craft' + +"""} + diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index c02ad73998..566411e513 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -3,13 +3,15 @@ Various complex queries that have been problematic in the past. """ import datetime +import pickle from django.db import models from django.db.models.query import Q class Tag(models.Model): name = models.CharField(max_length=10) - parent = models.ForeignKey('self', blank=True, null=True) + parent = models.ForeignKey('self', blank=True, null=True, + related_name='children') def __unicode__(self): return self.name @@ -24,6 +26,14 @@ class Note(models.Model): def __unicode__(self): return self.note +class Annotation(models.Model): + name = models.CharField(max_length=10) + tag = models.ForeignKey(Tag) + notes = models.ManyToManyField(Note) + + def __unicode__(self): + return self.name + class ExtraInfo(models.Model): info = models.CharField(max_length=100) note = models.ForeignKey(Note) @@ -162,85 +172,67 @@ class Child(models.Model): person = models.OneToOneField(Member, primary_key=True) parent = models.ForeignKey(Member, related_name="children") +# Custom primary keys interfered with ordering in the past. +class CustomPk(models.Model): + name = models.CharField(max_length=10, primary_key=True) + extra = models.CharField(max_length=10) + + class Meta: + ordering = ['name', 'extra'] + +class Related(models.Model): + custom = models.ForeignKey(CustomPk) + __test__ = {'API_TESTS':""" ->>> t1 = Tag(name='t1') ->>> t1.save() ->>> t2 = Tag(name='t2', parent=t1) ->>> t2.save() ->>> t3 = Tag(name='t3', parent=t1) ->>> t3.save() ->>> t4 = Tag(name='t4', parent=t3) ->>> t4.save() ->>> t5 = Tag(name='t5', parent=t3) ->>> t5.save() +>>> t1 = Tag.objects.create(name='t1') +>>> t2 = Tag.objects.create(name='t2', parent=t1) +>>> t3 = Tag.objects.create(name='t3', parent=t1) +>>> t4 = Tag.objects.create(name='t4', parent=t3) +>>> t5 = Tag.objects.create(name='t5', parent=t3) ->>> n1 = Note(note='n1', misc='foo') ->>> n1.save() ->>> n2 = Note(note='n2', misc='bar') ->>> n2.save() ->>> n3 = Note(note='n3', misc='foo') ->>> n3.save() +>>> n1 = Note.objects.create(note='n1', misc='foo') +>>> n2 = Note.objects.create(note='n2', misc='bar') +>>> n3 = Note.objects.create(note='n3', misc='foo') Create these out of order so that sorting by 'id' will be different to sorting by 'info'. Helps detect some problems later. ->>> e2 = ExtraInfo(info='e2', note=n2) ->>> e2.save() ->>> e1 = ExtraInfo(info='e1', note=n1) ->>> e1.save() +>>> e2 = ExtraInfo.objects.create(info='e2', note=n2) +>>> e1 = ExtraInfo.objects.create(info='e1', note=n1) ->>> a1 = Author(name='a1', num=1001, extra=e1) ->>> a1.save() ->>> a2 = Author(name='a2', num=2002, extra=e1) ->>> a2.save() ->>> a3 = Author(name='a3', num=3003, extra=e2) ->>> a3.save() ->>> a4 = Author(name='a4', num=4004, extra=e2) ->>> a4.save() +>>> a1 = Author.objects.create(name='a1', num=1001, extra=e1) +>>> a2 = Author.objects.create(name='a2', num=2002, extra=e1) +>>> a3 = Author.objects.create(name='a3', num=3003, extra=e2) +>>> a4 = Author.objects.create(name='a4', num=4004, extra=e2) >>> time1 = datetime.datetime(2007, 12, 19, 22, 25, 0) >>> time2 = datetime.datetime(2007, 12, 19, 21, 0, 0) >>> time3 = datetime.datetime(2007, 12, 20, 22, 25, 0) >>> time4 = datetime.datetime(2007, 12, 20, 21, 0, 0) ->>> i1 = Item(name='one', created=time1, modified=time1, creator=a1, note=n3) ->>> i1.save() +>>> i1 = Item.objects.create(name='one', created=time1, modified=time1, creator=a1, note=n3) >>> i1.tags = [t1, t2] ->>> i2 = Item(name='two', created=time2, creator=a2, note=n2) ->>> i2.save() +>>> i2 = Item.objects.create(name='two', created=time2, creator=a2, note=n2) >>> i2.tags = [t1, t3] ->>> i3 = Item(name='three', created=time3, creator=a2, note=n3) ->>> i3.save() ->>> i4 = Item(name='four', created=time4, creator=a4, note=n3) ->>> i4.save() +>>> i3 = Item.objects.create(name='three', created=time3, creator=a2, note=n3) +>>> i4 = Item.objects.create(name='four', created=time4, creator=a4, note=n3) >>> i4.tags = [t4] ->>> r1 = Report(name='r1', creator=a1) ->>> r1.save() ->>> r2 = Report(name='r2', creator=a3) ->>> r2.save() ->>> r3 = Report(name='r3') ->>> r3.save() +>>> r1 = Report.objects.create(name='r1', creator=a1) +>>> r2 = Report.objects.create(name='r2', creator=a3) +>>> r3 = Report.objects.create(name='r3') Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering will be rank3, rank2, rank1. ->>> rank1 = Ranking(rank=2, author=a2) ->>> rank1.save() ->>> rank2 = Ranking(rank=1, author=a3) ->>> rank2.save() ->>> rank3 = Ranking(rank=3, author=a1) ->>> rank3.save() +>>> rank1 = Ranking.objects.create(rank=2, author=a2) +>>> rank2 = Ranking.objects.create(rank=1, author=a3) +>>> rank3 = Ranking.objects.create(rank=3, author=a1) ->>> c1 = Cover(title="first", item=i4) ->>> c1.save() ->>> c2 = Cover(title="second", item=i2) ->>> c2.save() +>>> c1 = Cover.objects.create(title="first", item=i4) +>>> c2 = Cover.objects.create(title="second", item=i2) ->>> n1 = Number(num=4) ->>> n1.save() ->>> n2 = Number(num=8) ->>> n2.save() ->>> n3 = Number(num=12) ->>> n3.save() +>>> num1 = Number.objects.create(num=4) +>>> num2 = Number.objects.create(num=8) +>>> num3 = Number.objects.create(num=12) Bug #1050 >>> Item.objects.filter(tags__isnull=True) @@ -346,6 +338,10 @@ Bug #1878, #2939 4 >>> xx.delete() +Bug #7323 +>>> Item.objects.values('creator', 'name').count() +4 + Bug #2253 >>> q1 = Item.objects.order_by('name') >>> q2 = Item.objects.filter(id=i1.id) @@ -387,6 +383,10 @@ Bug #4510 >>> Author.objects.filter(report__name='r1') [<Author: a1>] +Bug #7378 +>>> a1.report_set.all() +[<Report: r1>] + Bug #5324, #6704 >>> Item.objects.filter(tags__name='t4') [<Item: four>] @@ -791,5 +791,19 @@ Empty querysets can be merged with others. >>> Note.objects.all() & Note.objects.none() [] +Bug #7204, #7506 -- make sure querysets with related fields can be pickled. If +this doesn't crash, it's a Good Thing. +>>> out = pickle.dumps(Item.objects.all()) + +Bug #7277 +>>> ann1 = Annotation.objects.create(name='a1', tag=t1) +>>> ann1.notes.add(n1) +>>> n1.annotation_set.filter(Q(tag=t5) | Q(tag__children=t5) | Q(tag__children__children=t5)) +[<Annotation: a1>] + +Bug #7371 +>>> Related.objects.order_by('custom') +[] + """} diff --git a/tests/regressiontests/select_related_regress/__init__.py b/tests/regressiontests/select_related_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/select_related_regress/__init__.py diff --git a/tests/regressiontests/select_related_regress/models.py b/tests/regressiontests/select_related_regress/models.py new file mode 100644 index 0000000000..05b851829d --- /dev/null +++ b/tests/regressiontests/select_related_regress/models.py @@ -0,0 +1,60 @@ +from django.db import models + +class Building(models.Model): + name = models.CharField(max_length=10) + + def __unicode__(self): + return u"Building: %s" % self.name + +class Device(models.Model): + building = models.ForeignKey('Building') + name = models.CharField(max_length=10) + + def __unicode__(self): + return u"device '%s' in building %s" % (self.name, self.building) + +class Port(models.Model): + device = models.ForeignKey('Device') + port_number = models.CharField(max_length=10) + + def __unicode__(self): + return u"%s/%s" % (self.device.name, self.port_number) + +class Connection(models.Model): + start = models.ForeignKey(Port, related_name='connection_start', + unique=True) + end = models.ForeignKey(Port, related_name='connection_end', unique=True) + + def __unicode__(self): + return u"%s to %s" % (self.start, self.end) + +__test__ = {'API_TESTS': """ +Regression test for bug #7110. When using select_related(), we must query the +Device and Building tables using two different aliases (each) in order to +differentiate the start and end Connection fields. The net result is that both +the "connections = ..." queries here should give the same results. + +>>> b=Building.objects.create(name='101') +>>> dev1=Device.objects.create(name="router", building=b) +>>> dev2=Device.objects.create(name="switch", building=b) +>>> dev3=Device.objects.create(name="server", building=b) +>>> port1=Port.objects.create(port_number='4',device=dev1) +>>> port2=Port.objects.create(port_number='7',device=dev2) +>>> port3=Port.objects.create(port_number='1',device=dev3) +>>> c1=Connection.objects.create(start=port1, end=port2) +>>> c2=Connection.objects.create(start=port2, end=port3) + +>>> connections=Connection.objects.filter(start__device__building=b, end__device__building=b).order_by('id') +>>> [(c.id, unicode(c.start), unicode(c.end)) for c in connections] +[(1, u'router/4', u'switch/7'), (2, u'switch/7', u'server/1')] + +>>> connections=Connection.objects.filter(start__device__building=b, end__device__building=b).select_related().order_by('id') +>>> [(c.id, unicode(c.start), unicode(c.end)) for c in connections] +[(1, u'router/4', u'switch/7'), (2, u'switch/7', u'server/1')] + +# This final query should only join seven tables (port, device and building +# twice each, plus connection once). +>>> connections.query.count_active_tables() +7 + +"""} diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py index 1bdb2d4452..39e7955592 100644 --- a/tests/regressiontests/string_lookup/models.py +++ b/tests/regressiontests/string_lookup/models.py @@ -97,6 +97,12 @@ __test__ = {'API_TESTS': ur""" >>> Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.') <Article: Article Test> +# Regression tests for #2170: test case sensitiveness +>>> Article.objects.filter(text__exact='tHe qUick bRown fOx jUmps over tHe lazy dog.') +[] +>>> Article.objects.filter(text__iexact='tHe qUick bRown fOx jUmps over tHe lazy dog.') +[<Article: Article Test>] + >>> Article.objects.get(text__contains='quick brown fox') <Article: Article Test> diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index a204ec3e72..1eb55e312e 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -6,6 +6,7 @@ from django.test import Client, TestCase from django.core.urlresolvers import reverse from django.core.exceptions import SuspiciousOperation import os +import sha class AssertContainsTests(TestCase): def test_contains(self): @@ -240,16 +241,6 @@ class AssertFormErrorTests(TestCase): except AssertionError, e: self.assertEqual(str(e), "The form 'form' in context 0 does not contain the non-field error 'Some error.' (actual errors: )") -class FileUploadTests(TestCase): - def test_simple_upload(self): - fd = open(os.path.join(os.path.dirname(__file__), "views.py")) - post_data = { - 'name': 'Ringo', - 'file_field': fd, - } - response = self.client.post('/test_client_regress/file_upload/', post_data) - self.assertEqual(response.status_code, 200) - class LoginTests(TestCase): fixtures = ['testdata'] @@ -269,7 +260,6 @@ class LoginTests(TestCase): # default client. self.assertRedirects(response, "http://testserver/test_client_regress/get_view/") - class URLEscapingTests(TestCase): def test_simple_argument_get(self): "Get a view that has a simple string argument" @@ -318,3 +308,22 @@ class ExceptionTests(TestCase): self.client.get("/test_client_regress/staff_only/") except SuspiciousOperation: self.fail("Staff should be able to visit this page") + +# We need two different tests to check URLconf subsitution - one to check +# it was changed, and another one (without self.urls) to check it was reverted on +# teardown. This pair of tests relies upon the alphabetical ordering of test execution. +class UrlconfSubstitutionTests(TestCase): + urls = 'regressiontests.test_client_regress.urls' + + def test_urlconf_was_changed(self): + "TestCase can enforce a custom URLConf on a per-test basis" + url = reverse('arg_view', args=['somename']) + self.assertEquals(url, '/arg_view/somename/') + +# This test needs to run *after* UrlconfSubstitutionTests; the zz prefix in the +# name is to ensure alphabetical ordering. +class zzUrlconfSubstitutionTests(TestCase): + def test_urlconf_was_reverted(self): + "URLconf is reverted to original value after modification in a TestCase" + url = reverse('arg_view', args=['somename']) + self.assertEquals(url, '/test_client_regress/arg_view/somename/') diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py index dc26d1260a..12f6afacf3 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -3,7 +3,6 @@ import views urlpatterns = patterns('', (r'^no_template_view/$', views.no_template_view), - (r'^file_upload/$', views.file_upload_view), (r'^staff_only/$', views.staff_only_view), (r'^get_view/$', views.get_view), url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'), diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 9632c17284..d703c82124 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -1,36 +1,18 @@ -import os - from django.contrib.auth.decorators import login_required -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError +from django.http import HttpResponse, HttpResponseRedirect from django.core.exceptions import SuspiciousOperation def no_template_view(request): "A simple view that expects a GET request, and returns a rendered template" return HttpResponse("No template used. Sample content: twice once twice. Content ends.") -def file_upload_view(request): - """ - Check that a file upload can be updated into the POST dictionary without - going pear-shaped. - """ - form_data = request.POST.copy() - form_data.update(request.FILES) - if isinstance(form_data['file_field'], dict) and isinstance(form_data['name'], unicode): - # If a file is posted, the dummy client should only post the file name, - # not the full path. - if os.path.dirname(form_data['file_field']['filename']) != '': - return HttpResponseServerError() - return HttpResponse('') - else: - return HttpResponseServerError() - def staff_only_view(request): "A view that can only be visited by staff. Non staff members get an exception" if request.user.is_staff: return HttpResponse('') else: raise SuspiciousOperation() - + def get_view(request): "A simple login protected view" return HttpResponse("Hello world") @@ -51,4 +33,4 @@ def view_with_argument(request, name): def login_protected_redirect_view(request): "A view that redirects all requests to the GET view" return HttpResponseRedirect('/test_client_regress/get_view/') -login_protected_redirect_view = login_required(login_protected_redirect_view)
\ No newline at end of file +login_protected_redirect_view = login_required(login_protected_redirect_view) |
