diff options
| author | Justin Bronn <jbronn@gmail.com> | 2007-12-15 01:38:43 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2007-12-15 01:38:43 +0000 |
| commit | 34560a01daee3c42a7c5ec462f38a485cccf4df7 (patch) | |
| tree | c332688943fb10b1503654815a06093c1d52e493 /tests | |
| parent | 5799c2e048ff829300af88ae839de20e1763ee1d (diff) | |
gis: Merged revisions 6672,6686-6688,6690,6693,6707-6708,6726,6730,6753,6755-6762,6764,6776-6777,6779,6782-6919 via svnmerge from trunk; reverted oracle backend `base.py` due to ikelly's patch in r6905.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6920 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
26 files changed, 499 insertions, 167 deletions
diff --git a/tests/modeltests/empty/models.py b/tests/modeltests/empty/models.py index 2493b53837..d57087134e 100644 --- a/tests/modeltests/empty/models.py +++ b/tests/modeltests/empty/models.py @@ -1,5 +1,5 @@ """ -39. Empty model tests +40. Empty model tests These test that things behave sensibly for the rare corner-case of a model with no fields. diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py index 1132f1ca41..fe80cce406 100644 --- a/tests/modeltests/field_defaults/models.py +++ b/tests/modeltests/field_defaults/models.py @@ -48,4 +48,9 @@ u'Default headline' >>> d = now - a.pub_date >>> d.seconds < 5 True + +# make sure that SafeUnicode fields work +>>> from django.utils.safestring import SafeUnicode +>>> a.headline = SafeUnicode(u'SafeUnicode Headline') +>>> a.save() """} diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py index ce1d824ca8..ff86823d07 100644 --- a/tests/modeltests/generic_relations/models.py +++ b/tests/modeltests/generic_relations/models.py @@ -18,42 +18,42 @@ class TaggedItem(models.Model): tag = models.SlugField() content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() - + content_object = generic.GenericForeignKey() - + class Meta: ordering = ["tag"] - + def __unicode__(self): return self.tag class Animal(models.Model): common_name = models.CharField(max_length=150) latin_name = models.CharField(max_length=150) - + tags = generic.GenericRelation(TaggedItem) def __unicode__(self): return self.common_name - + class Vegetable(models.Model): name = models.CharField(max_length=150) is_yucky = models.BooleanField(default=True) - + tags = generic.GenericRelation(TaggedItem) - + def __unicode__(self): return self.name - + class Mineral(models.Model): name = models.CharField(max_length=150) hardness = models.PositiveSmallIntegerField() - + # note the lack of an explicit GenericRelation here... - + def __unicode__(self): return self.name - + __test__ = {'API_TESTS':""" # Create the world in 7 lines of code... >>> lion = Animal(common_name="Lion", latin_name="Panthera leo") @@ -117,13 +117,13 @@ __test__ = {'API_TESTS':""" >>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] [(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)] -# If Generic Relation is not explicitly defined, any related objects +# If Generic Relation is not explicitly defined, any related objects # remain after deletion of the source object. >>> quartz.delete() >>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] [(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)] -# If you delete a tag, the objects using the tag are unaffected +# If you delete a tag, the objects using the tag are unaffected # (other than losing a tag) >>> tag = TaggedItem.objects.get(id=1) >>> tag.delete() @@ -132,4 +132,8 @@ __test__ = {'API_TESTS':""" >>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] [(u'clearish', <ContentType: mineral>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)] +>>> ctype = ContentType.objects.get_for_model(lion) +>>> Animal.objects.filter(tags__content_type=ctype) +[<Animal: Platypus>] + """} diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py index bd800317d3..d9f276b024 100644 --- a/tests/modeltests/get_object_or_404/models.py +++ b/tests/modeltests/get_object_or_404/models.py @@ -78,7 +78,7 @@ Http404: No Article matches the given query. >>> get_object_or_404(Author.objects.all()) Traceback (most recent call last): ... -AssertionError: get() returned more than one Author -- it returned ...! Lookup parameters were {} +MultipleObjectsReturned: get() returned more than one Author -- it returned ...! Lookup parameters were {} # Using an EmptyQuerySet raises a Http404 error. >>> get_object_or_404(Article.objects.none(), title__contains="Run") diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py index b746af6dba..8a480a2381 100644 --- a/tests/modeltests/invalid_models/models.py +++ b/tests/modeltests/invalid_models/models.py @@ -108,6 +108,10 @@ class Car(models.Model): colour = models.CharField(max_length=5) model = models.ForeignKey(Model) +class MissingRelations(models.Model): + rel1 = models.ForeignKey("Rel1") + rel2 = models.ManyToManyField("Rel2") + model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute. invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute. invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute. @@ -191,4 +195,6 @@ invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'. invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_3'. invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_4' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_4'. +invalid_models.missingrelations: 'rel2' has m2m relation with model Rel2, which has not been installed +invalid_models.missingrelations: 'rel1' has relation with model Rel1, which has not been installed """ diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 9b0126ff4f..17c3b3551c 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -1,25 +1,10 @@ """ -36. Generating HTML forms from models +XX. Generating HTML forms from models -Django provides shortcuts for creating Form objects from a model class and a -model instance. - -The function django.newforms.form_for_model() takes a model class and returns -a Form that is tied to the model. This Form works just like any other Form, -with one additional method: save(). The save() method creates an instance -of the model and returns that newly created instance. It saves the instance to -the database if save(commit=True), which is default. If you pass -commit=False, then you'll get the object without committing the changes to the -database. - -The function django.newforms.form_for_instance() takes a model instance and -returns a Form that is tied to the instance. This form works just like any -other Form, with one additional method: save(). The save() -method updates the model instance. It also takes a commit=True parameter. - -The function django.newforms.save_instance() takes a bound form instance and a -model instance and saves the form's cleaned_data into the instance. It also takes -a commit=True parameter. +This is mostly just a reworking of the form_for_model/form_for_instance tests +to use ModelForm. As such, the text may not make sense in all cases, and the +examples are probably a poor fit for the ModelForm syntax. In other words, +most of these tests should be rewritten. """ from django.db import models @@ -30,23 +15,6 @@ ARTICLE_STATUS = ( (3, 'Live'), ) -STEERING_TYPE = ( - ('left', 'Left steering wheel'), - ('right', 'Right steering wheel'), -) - -FUEL_TYPE = ( - ('gas', 'Gasoline'), - ('diesel', 'Diesel'), - ('other', 'Other'), -) - -TRANSMISSION_TYPE = ( - ('at', 'Automatic'), - ('mt', 'Manual'), - ('cvt', 'CVT'), -) - class Category(models.Model): name = models.CharField(max_length=20) slug = models.SlugField(max_length=20) @@ -87,20 +55,124 @@ class PhoneNumber(models.Model): def __unicode__(self): return self.phone -class Car(models.Model): - name = models.CharField(max_length=50) - steering = models.CharField(max_length=5, choices=STEERING_TYPE, default='left') - fuel = models.CharField(max_length=10, choices=FUEL_TYPE) - transmission = models.CharField(max_length=3, choices=TRANSMISSION_TYPE, blank=True, help_text='Leave empty if not applicable.') - __test__ = {'API_TESTS': """ ->>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField +>>> from django import newforms as forms +>>> from django.newforms.models import ModelForm + +The bare bones, absolutely nothing custom, basic case. + +>>> class CategoryForm(ModelForm): +... class Meta: +... model = Category +>>> CategoryForm.base_fields.keys() +['name', 'slug', 'url'] + + +Extra fields. + +>>> class CategoryForm(ModelForm): +... some_extra_field = forms.BooleanField() +... +... class Meta: +... model = Category + +>>> CategoryForm.base_fields.keys() +['name', 'slug', 'url', 'some_extra_field'] + + +Replacing a field. + +>>> class CategoryForm(ModelForm): +... url = forms.BooleanField() +... +... class Meta: +... model = Category + +>>> CategoryForm.base_fields['url'].__class__ +<class 'django.newforms.fields.BooleanField'> + + +Using 'fields'. + +>>> class CategoryForm(ModelForm): +... +... class Meta: +... model = Category +... fields = ['url'] + +>>> CategoryForm.base_fields.keys() +['url'] + + +Using 'exclude' + +>>> class CategoryForm(ModelForm): +... +... class Meta: +... model = Category +... exclude = ['url'] + +>>> CategoryForm.base_fields.keys() +['name', 'slug'] + + +Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh, +"be liberal in what you accept" and all. + +>>> class CategoryForm(ModelForm): +... +... class Meta: +... model = Category +... fields = ['name', 'url'] +... exclude = ['url'] + +>>> CategoryForm.base_fields.keys() +['name'] + +Don't allow more than one 'model' definition in the inheritance hierarchy. +Technically, it would generate a valid form, but the fact that the resulting +save method won't deal with multiple objects is likely to trip up people not +familiar with the mechanics. + +>>> class CategoryForm(ModelForm): +... class Meta: +... model = Category + +>>> class BadForm(CategoryForm): +... class Meta: +... model = Article +Traceback (most recent call last): +... +ImproperlyConfigured: BadForm defines a different model than its parent. + +>>> class ArticleForm(ModelForm): +... class Meta: +... model = Article + +>>> class BadForm(ArticleForm, CategoryForm): +... pass +Traceback (most recent call last): +... +ImproperlyConfigured: BadForm's base classes define more than one model. + +This one is OK since the subclass specifies the same model as the parent. + +>>> class SubCategoryForm(CategoryForm): +... class Meta: +... model = Category + + +# Old form_for_x tests ####################################################### + +>>> from django.newforms import ModelForm, CharField >>> import datetime >>> Category.objects.all() [] ->>> CategoryForm = form_for_model(Category) +>>> class CategoryForm(ModelForm): +... class Meta: +... model = Category >>> f = CategoryForm() >>> print f <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr> @@ -184,7 +256,9 @@ Create a couple of Writers. ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any fields with the 'choices' attribute are represented by a ChoiceField. ->>> ArticleForm = form_for_model(Article) +>>> class ArticleForm(ModelForm): +... class Meta: +... model = Article >>> f = ArticleForm(auto_id=False) >>> print f <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> @@ -214,28 +288,23 @@ model created with such a form, you need to ensure that the fields that are _not_ on the form have default values, or are allowed to have a value of None. If a field isn't specified on a form, the object created from the form can't provide a value for that field! ->>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date')) +>>> class PartialArticleForm(ModelForm): +... class Meta: +... model = Article +... fields = ('headline','pub_date') >>> f = PartialArticleForm(auto_id=False) >>> print f <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr> -You can pass a custom Form class to form_for_model. Make sure it's a -subclass of BaseForm, not Form. ->>> class CustomForm(BaseForm): -... def say_hello(self): -... print 'hello' ->>> CategoryForm = form_for_model(Category, form=CustomForm) ->>> f = CategoryForm() ->>> f.say_hello() -hello - Use form_for_instance to create a Form from a model instance. The difference between this Form and one created via form_for_model is that the object's current values are inserted as 'initial' data in each Field. >>> w = Writer.objects.get(name='Mike Royko') ->>> RoykoForm = form_for_instance(w) ->>> f = RoykoForm(auto_id=False) +>>> class RoykoForm(ModelForm): +... class Meta: +... model = Writer +>>> f = RoykoForm(auto_id=False, instance=w) >>> print f <tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr> @@ -243,8 +312,10 @@ current values are inserted as 'initial' data in each Field. >>> art.save() >>> art.id 1 ->>> TestArticleForm = form_for_instance(art) ->>> f = TestArticleForm(auto_id=False) +>>> class TestArticleForm(ModelForm): +... class Meta: +... model = Article +>>> f = TestArticleForm(auto_id=False, instance=art) >>> print f.as_ul() <li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li> @@ -266,7 +337,7 @@ current values are inserted as 'initial' data in each Field. <option value="2">It's a test</option> <option value="3">Third test</option> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> ->>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}) +>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}, instance=art) >>> f.is_valid() True >>> test_art = f.save() @@ -278,8 +349,11 @@ u'Test headline' You can create a form over a subset of the available fields by specifying a 'fields' argument to form_for_instance. ->>> PartialArticleForm = form_for_instance(art, fields=('headline', 'slug', 'pub_date')) ->>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False) +>>> class PartialArticleForm(ModelForm): +... class Meta: +... model = Article +... fields=('headline', 'slug', 'pub_date') +>>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False, instance=art) >>> print f.as_ul() <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> @@ -299,8 +373,10 @@ Add some categories and test the many-to-many form output. >>> new_art.categories.add(Category.objects.get(name='Entertainment')) >>> new_art.categories.all() [<Category: Entertainment>] ->>> TestArticleForm = form_for_instance(new_art) ->>> f = TestArticleForm(auto_id=False) +>>> class TestArticleForm(ModelForm): +... class Meta: +... model = Article +>>> f = TestArticleForm(auto_id=False, instance=new_art) >>> print f.as_ul() <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> @@ -324,7 +400,7 @@ Add some categories and test the many-to-many form output. </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', -... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}) +... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art) >>> new_art = f.save() >>> new_art.id 1 @@ -334,7 +410,7 @@ Add some categories and test the many-to-many form output. Now, submit form data with no categories. This deletes the existing categories. >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', -... 'writer': u'1', 'article': u'Hello.'}) +... 'writer': u'1', 'article': u'Hello.'}, instance=new_art) >>> new_art = f.save() >>> new_art.id 1 @@ -343,7 +419,9 @@ Now, submit form data with no categories. This deletes the existing categories. [] Create a new article, with categories, via the form. ->>> ArticleForm = form_for_model(Article) +>>> class ArticleForm(ModelForm): +... class Meta: +... model = Article >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01', ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']}) >>> new_art = f.save() @@ -354,7 +432,9 @@ Create a new article, with categories, via the form. [<Category: Entertainment>, <Category: It's a test>] Create a new article, with no categories, via the form. ->>> ArticleForm = form_for_model(Article) +>>> class ArticleForm(ModelForm): +... class Meta: +... model = Article >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01', ... 'writer': u'1', 'article': u'Test.'}) >>> new_art = f.save() @@ -366,7 +446,9 @@ Create a new article, with no categories, via the form. Create a new article, with categories, via the form, but use commit=False. The m2m data won't be saved until save_m2m() is invoked on the form. ->>> ArticleForm = form_for_model(Article) +>>> class ArticleForm(ModelForm): +... class Meta: +... model = Article >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01', ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']}) >>> new_art = f.save(commit=False) @@ -386,10 +468,10 @@ The m2m data won't be saved until save_m2m() is invoked on the form. >>> new_art.categories.order_by('name') [<Category: Entertainment>, <Category: It's a test>] -Here, we define a custom Form. Because it happens to have the same fields as -the Category model, we can use save_instance() to apply its changes to an +Here, we define a custom ModelForm. Because it happens to have the same fields as +the Category model, we can just call the form's save() to apply its changes to an existing Category instance. ->>> class ShortCategory(Form): +>>> class ShortCategory(ModelForm): ... name = CharField(max_length=5) ... slug = CharField(max_length=5) ... url = CharField(max_length=3) @@ -398,8 +480,8 @@ existing Category instance. <Category: Third test> >>> cat.id 3 ->>> sc = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}) ->>> save_instance(sc, cat) +>>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat) +>>> form.save() <Category: Third> >>> Category.objects.get(id=3) <Category: Third> @@ -407,7 +489,9 @@ existing Category instance. Here, we demonstrate that choices for a ForeignKey ChoiceField are determined at runtime, based on the data in the database when the form is displayed, not the data in the database when the form is instantiated. ->>> ArticleForm = form_for_model(Article) +>>> class ArticleForm(ModelForm): +... class Meta: +... model = Article >>> f = ArticleForm(auto_id=False) >>> print f.as_ul() <li>Headline: <input type="text" name="headline" maxlength="50" /></li> @@ -609,60 +693,12 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices # PhoneNumberField ############################################################ ->>> PhoneNumberForm = form_for_model(PhoneNumber) +>>> class PhoneNumberForm(ModelForm): +... class Meta: +... model = PhoneNumber >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'}) >>> f.is_valid() True >>> f.cleaned_data {'phone': u'312-555-1212', 'description': u'Assistance'} - -# form_for_* blank choices #################################################### - -Show the form for a new Car. Note that steering field doesn't include the blank choice, -because the field is obligatory and has an explicit default. ->>> CarForm = form_for_model(Car) ->>> f = CarForm(auto_id=False) ->>> print f -<tr><th>Name:</th><td><input type="text" name="name" maxlength="50" /></td></tr> -<tr><th>Steering:</th><td><select name="steering"> -<option value="left" selected="selected">Left steering wheel</option> -<option value="right">Right steering wheel</option> -</select></td></tr> -<tr><th>Fuel:</th><td><select name="fuel"> -<option value="" selected="selected">---------</option> -<option value="gas">Gasoline</option> -<option value="diesel">Diesel</option> -<option value="other">Other</option> -</select></td></tr> -<tr><th>Transmission:</th><td><select name="transmission"> -<option value="" selected="selected">---------</option> -<option value="at">Automatic</option> -<option value="mt">Manual</option> -<option value="cvt">CVT</option> -</select><br />Leave empty if not applicable.</td></tr> - -Create a Car, and display the form for modifying it. Note that now the fuel -selector doesn't include the blank choice as well, since the field is -obligatory and can not be changed to be blank. ->>> honda = Car(name='Honda Accord Wagon', steering='right', fuel='gas', transmission='at') ->>> honda.save() ->>> HondaForm = form_for_instance(honda) ->>> f = HondaForm(auto_id=False) ->>> print f -<tr><th>Name:</th><td><input type="text" name="name" value="Honda Accord Wagon" maxlength="50" /></td></tr> -<tr><th>Steering:</th><td><select name="steering"> -<option value="left">Left steering wheel</option> -<option value="right" selected="selected">Right steering wheel</option> -</select></td></tr> -<tr><th>Fuel:</th><td><select name="fuel"> -<option value="gas" selected="selected">Gasoline</option> -<option value="diesel">Diesel</option> -<option value="other">Other</option> -</select></td></tr> -<tr><th>Transmission:</th><td><select name="transmission"> -<option value="">---------</option> -<option value="at" selected="selected">Automatic</option> -<option value="mt">Manual</option> -<option value="cvt">CVT</option> -</select><br />Leave empty if not applicable.</td></tr> """} diff --git a/tests/modeltests/select_related/models.py b/tests/modeltests/select_related/models.py index 43efab3a7d..f0fd121665 100644 --- a/tests/modeltests/select_related/models.py +++ b/tests/modeltests/select_related/models.py @@ -1,5 +1,5 @@ """ -40. Tests for select_related() +41. Tests for select_related() ``select_related()`` follows all relationships and pre-caches any foreign key values so that complex trees can be fetched in a single query. However, this diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index a2388223f0..0ccc19f895 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -41. Serialization +42. Serialization ``django.core.serializers`` provides interfaces to converting Django querysets to and from "flat" data (i.e. strings). @@ -218,3 +218,41 @@ None 3.4 """} + +try: + import yaml + __test__['YAML'] = """ +# Create some data: + +>>> articles = Article.objects.all().order_by("id")[:2] +>>> from django.core import serializers + +# test if serial + +>>> serialized = serializers.serialize("yaml", articles) +>>> print serialized +- fields: + author: 2 + categories: [3, 1] + headline: Just kidding; I love TV poker + pub_date: 2006-06-16 11:00:00 + model: serializers.article + pk: 1 +- fields: + author: 1 + categories: [2, 3] + headline: Time to reform copyright + pub_date: 2006-06-16 13:00:11 + model: serializers.article + pk: 2 +<BLANKLINE> + +>>> obs = list(serializers.deserialize("yaml", serialized)) +>>> for i in obs: +... print i +<DeserializedObject: Just kidding; I love TV poker> +<DeserializedObject: Time to reform copyright> + +""" +except ImportError: pass + diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index c7aaaff67d..1a6e1bdc18 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -1,6 +1,6 @@ # coding: utf-8 """ -38. Testing using the Test Client +39. Testing using the Test Client The test client is a class that can act like a simple browser for testing purposes. diff --git a/tests/modeltests/user_commands/models.py b/tests/modeltests/user_commands/models.py index 5f96806dac..6db4b049df 100644 --- a/tests/modeltests/user_commands/models.py +++ b/tests/modeltests/user_commands/models.py @@ -1,5 +1,5 @@ """ -37. User-registered management commands +38. User-registered management commands The manage.py utility provides a number of useful commands for managing a Django project. If you want to add a utility command of your own, you can. @@ -27,4 +27,4 @@ Traceback (most recent call last): CommandError: Unknown command: 'explode' -"""}
\ No newline at end of file +"""} diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index e94ea33139..f050348c77 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -3,8 +3,8 @@ # Unit tests for cache framework # Uses whatever cache backend is set in the test settings file. -import time, unittest - +import time +import unittest from django.core.cache import cache from django.utils.cache import patch_vary_headers from django.http import HttpResponse @@ -27,7 +27,7 @@ class Cache(unittest.TestCase): cache.add("addkey1", "value") cache.add("addkey1", "newvalue") self.assertEqual(cache.get("addkey1"), "value") - + def test_non_existent(self): # get with non-existent keys self.assertEqual(cache.get("does_not_exist"), None) @@ -72,12 +72,20 @@ class Cache(unittest.TestCase): 'function' : f, 'class' : C, } + cache.set("stuff", stuff) + self.assertEqual(cache.get("stuff"), stuff) def test_expiration(self): - # expiration - cache.set('expire', 'very quickly', 1) - time.sleep(2) - self.assertEqual(cache.get("expire"), None) + cache.set('expire1', 'very quickly', 1) + cache.set('expire2', 'very quickly', 1) + cache.set('expire3', 'very quickly', 1) + + time.sleep(2) + self.assertEqual(cache.get("expire1"), None) + + cache.add("expire2", "newvalue") + self.assertEqual(cache.get("expire2"), "newvalue") + self.assertEqual(cache.has_key("expire3"), False) def test_unicode(self): stuff = { @@ -90,6 +98,44 @@ class Cache(unittest.TestCase): cache.set(key, value) self.assertEqual(cache.get(key), value) +import os +import md5 +import shutil +import tempfile +from django.core.cache.backends.filebased import CacheClass as FileCache + +class FileBasedCacheTests(unittest.TestCase): + """ + Specific test cases for the file-based cache. + """ + def setUp(self): + self.dirname = tempfile.mktemp() + os.mkdir(self.dirname) + self.cache = FileCache(self.dirname, {}) + + def tearDown(self): + shutil.rmtree(self.dirname) + + def test_hashing(self): + """Test that keys are hashed into subdirectories correctly""" + self.cache.set("foo", "bar") + keyhash = md5.new("foo").hexdigest() + keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) + self.assert_(os.path.exists(keypath)) + + def test_subdirectory_removal(self): + """ + Make sure that the created subdirectories are correctly removed when empty. + """ + self.cache.set("foo", "bar") + keyhash = md5.new("foo").hexdigest() + keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) + self.assert_(os.path.exists(keypath)) + + self.cache.delete("foo") + self.assert_(not os.path.exists(keypath)) + self.assert_(not os.path.exists(os.path.dirname(keypath))) + self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath)))) class CacheUtils(unittest.TestCase): """TestCase for django.utils.cache functions.""" diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index bfa03cd6e1..668ecb9d5a 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -49,6 +49,18 @@ u'\\\\ : backslashes, too' >>> capfirst(u'hello world') u'Hello world' +>>> escapejs(u'"double quotes" and \'single quotes\'') +u'\\"double quotes\\" and \\\'single quotes\\\'' + +>>> escapejs(ur'\ : backslashes, too') +u'\\\\ : backslashes, too' + +>>> escapejs(u'and lots of whitespace: \r\n\t\v\f\b') +u'and lots of whitespace: \\r\\n\\t\\v\\f\\b' + +>>> escapejs(ur'<script>and this</script>') +u'<script>and this<\\/script>' + >>> fix_ampersands(u'Jack & Jill & Jeroboam') u'Jack & Jill & Jeroboam' diff --git a/tests/regressiontests/forms/localflavor/za.py b/tests/regressiontests/forms/localflavor/za.py new file mode 100644 index 0000000000..a948964b8d --- /dev/null +++ b/tests/regressiontests/forms/localflavor/za.py @@ -0,0 +1,40 @@ +tests = r""" +# ZAIDField ################################################################# + +ZAIDField validates that the date is a valid birthdate and that the value +has a valid checksum. It allows spaces and dashes, and returns a plain +string of digits. +>>> from django.contrib.localflavor.za.forms import ZAIDField +>>> f = ZAIDField() +>>> f.clean('0002290001003') +'0002290001003' +>>> f.clean('000229 0001 003') +'0002290001003' +>>> f.clean('0102290001001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid South African ID number'] +>>> f.clean('811208') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid South African ID number'] +>>> f.clean('0002290001004') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid South African ID number'] + +# ZAPostCodeField ########################################################### +>>> from django.contrib.localflavor.za.forms import ZAPostCodeField +>>> f = ZAPostCodeField() +>>> f.clean('abcd') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid South African postal code'] +>>> f.clean('0000') +u'0000' +>>> f.clean(' 7530') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid South African postal code'] + +""" diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 6acd4f2e1d..bb0e30b874 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -22,6 +22,7 @@ from localflavor.pl import tests as localflavor_pl_tests from localflavor.sk import tests as localflavor_sk_tests from localflavor.uk import tests as localflavor_uk_tests from localflavor.us import tests as localflavor_us_tests +from localflavor.za import tests as localflavor_za_tests from regressions import tests as regression_tests from util import tests as util_tests from widgets import tests as widgets_tests @@ -50,6 +51,7 @@ __test__ = { 'localflavor_sk_tests': localflavor_sk_tests, 'localflavor_uk_tests': localflavor_uk_tests, 'localflavor_us_tests': localflavor_us_tests, + 'localflavor_za_tests': localflavor_za_tests, 'regression_tests': regression_tests, 'util_tests': util_tests, 'widgets_tests': widgets_tests, diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 2ffc62f90d..94e792cf54 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -43,7 +43,7 @@ u'django' Translating a string requiring no auto-escaping shouldn't change the "safe" status. ->>> from django.utils.safestring import mark_safe +>>> from django.utils.safestring import mark_safe, SafeString >>> s = mark_safe('Password') >>> type(s) <class 'django.utils.safestring.SafeString'> @@ -51,6 +51,19 @@ status. >>> type(ugettext(s)) <class 'django.utils.safestring.SafeUnicode'> >>> deactivate() + +>>> SafeString('a') + s +'aPassword' +>>> s + SafeString('a') +'Passworda' +>>> s + mark_safe('a') +'Passworda' +>>> mark_safe('a') + s +'aPassword' +>>> mark_safe('a') + mark_safe('s') +'as' +>>> print s +Password """ __test__ = { diff --git a/tests/regressiontests/maxlength/tests.py b/tests/regressiontests/maxlength/tests.py index 8a5f874c78..c7ed1f91c0 100644 --- a/tests/regressiontests/maxlength/tests.py +++ b/tests/regressiontests/maxlength/tests.py @@ -22,12 +22,12 @@ Don't print out the deprecation warnings during testing. >>> legacy_maxlength(10, 12) Traceback (most recent call last): ... -TypeError: field can not take both the max_length argument and the legacy maxlength argument. +TypeError: Field cannot take both the max_length argument and the legacy maxlength argument. >>> legacy_maxlength(0, 10) Traceback (most recent call last): ... -TypeError: field can not take both the max_length argument and the legacy maxlength argument. +TypeError: Field cannot take both the max_length argument and the legacy maxlength argument. >>> legacy_maxlength(0, None) 0 @@ -48,7 +48,7 @@ TypeError: field can not take both the max_length argument and the legacy maxlen >>> fields.Field(maxlength=10, max_length=15) Traceback (most recent call last): ... -TypeError: field can not take both the max_length argument and the legacy maxlength argument. +TypeError: Field cannot take both the max_length argument and the legacy maxlength argument. # Test max_length >>> new.max_length diff --git a/tests/regressiontests/middleware/__init__.py b/tests/regressiontests/middleware/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/middleware/__init__.py diff --git a/tests/regressiontests/middleware/tests.py b/tests/regressiontests/middleware/tests.py new file mode 100644 index 0000000000..cb5c29abe1 --- /dev/null +++ b/tests/regressiontests/middleware/tests.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +import unittest + +from django.test import TestCase +from django.http import HttpRequest +from django.middleware.common import CommonMiddleware +from django.conf import settings + +class CommonMiddlewareTest(TestCase): + def _get_request(self, path): + request = HttpRequest() + request.META = { + 'SERVER_NAME': 'testserver', + 'SERVER_PORT': 80, + } + request.path = "/middleware/%s" % path + return request + + def test_append_slash_have_slash(self): + """ + tests that urls with slashes go unmolested + """ + settings.APPEND_SLASH = True + request = self._get_request('slash/') + self.assertEquals(CommonMiddleware().process_request(request), None) + + def test_append_slash_slashless_resource(self): + """ + tests that matches to explicit slashless urls go unmolested + """ + settings.APPEND_SLASH = True + request = self._get_request('noslash') + self.assertEquals(CommonMiddleware().process_request(request), None) + + def test_append_slash_slashless_unknown(self): + """ + tests that APPEND_SLASH doesn't redirect to unknown resources + """ + settings.APPEND_SLASH = True + request = self._get_request('unknown') + self.assertEquals(CommonMiddleware().process_request(request), None) + + def test_append_slash_redirect(self): + """ + tests that APPEND_SLASH redirects slashless urls to a valid pattern + """ + settings.APPEND_SLASH = True + request = self._get_request('slash') + r = CommonMiddleware().process_request(request) + self.assertEquals(r.status_code, 301) + self.assertEquals(r['Location'], 'http://testserver/middleware/slash/') + + def test_append_slash_no_redirect_on_POST_in_DEBUG(self): + """ + tests that while in debug mode, an exception is raised with a warning + when a failed attempt is made to POST to an url which would normally be + redirected to a slashed version + """ + settings.APPEND_SLASH = True + settings.DEBUG = True + request = self._get_request('slash') + request.method = 'POST' + self.assertRaises( + RuntimeError, + CommonMiddleware().process_request, + request) + try: + CommonMiddleware().process_request(request) + except RuntimeError, e: + self.assertTrue('end in a slash' in str(e)) + + def test_append_slash_disabled(self): + """ + tests disabling append slash functionality + """ + settings.APPEND_SLASH = False + request = self._get_request('slash') + self.assertEquals(CommonMiddleware().process_request(request), None) + + def test_append_slash_quoted(self): + """ + tests that urls which require quoting are redirected to their slash + version ok + """ + settings.APPEND_SLASH = True + request = self._get_request('needsquoting#') + r = CommonMiddleware().process_request(request) + self.assertEquals(r.status_code, 301) + self.assertEquals( + r['Location'], + 'http://testserver/middleware/needsquoting%23/') + diff --git a/tests/regressiontests/middleware/urls.py b/tests/regressiontests/middleware/urls.py new file mode 100644 index 0000000000..88a4b37ddc --- /dev/null +++ b/tests/regressiontests/middleware/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls.defaults import patterns + +urlpatterns = patterns('', + (r'^noslash$', 'view'), + (r'^slash/$', 'view'), + (r'^needsquoting#/$', 'view'), +) diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py index 00c3bc96f0..02e73a5aa9 100644 --- a/tests/regressiontests/model_regress/models.py +++ b/tests/regressiontests/model_regress/models.py @@ -11,6 +11,7 @@ class Article(models.Model): pub_date = models.DateTimeField() status = models.IntegerField(blank=True, null=True, choices=CHOICES) misc_data = models.CharField(max_length=100, blank=True) + article_text = models.TextField() class Meta: ordering = ('pub_date','headline') @@ -41,5 +42,14 @@ Empty strings should be returned as Unicode >>> a2 = Article.objects.get(pk=a.id) >>> a2.misc_data u'' + +# TextFields can hold more than 4000 characters (this was broken in Oracle). +>>> a3 = Article(headline="Really, really big", pub_date=datetime.now()) +>>> a3.article_text = "ABCDE" * 1000 +>>> a3.save() +>>> a4 = Article.objects.get(pk=a3.id) +>>> len(a4.article_text) +5000 + """ } diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py index 12ebd0cf07..9deeb18763 100644 --- a/tests/regressiontests/string_lookup/models.py +++ b/tests/regressiontests/string_lookup/models.py @@ -18,26 +18,26 @@ class Bar(models.Model): return "Bar %s" % self.place.name class Whiz(models.Model): - name = models.CharField(max_length = 50) + name = models.CharField(max_length=50) def __unicode__(self): return "Whiz %s" % self.name class Child(models.Model): parent = models.OneToOneField('Base') - name = models.CharField(max_length = 50) + name = models.CharField(max_length=50) def __unicode__(self): return "Child %s" % self.name class Base(models.Model): - name = models.CharField(max_length = 50) + name = models.CharField(max_length=50) def __unicode__(self): return "Base %s" % self.name class Article(models.Model): - name = models.CharField(maxlength = 50) + name = models.CharField(max_length=50) text = models.TextField() def __str__(self): diff --git a/tests/regressiontests/templates/context.py b/tests/regressiontests/templates/context.py new file mode 100644 index 0000000000..d8b0f39abe --- /dev/null +++ b/tests/regressiontests/templates/context.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +context_tests = r""" +>>> from django.template import Context +>>> c = Context({'a': 1, 'b': 'xyzzy'}) +>>> c['a'] +1 +>>> c.push() +{} +>>> c['a'] = 2 +>>> c['a'] +2 +>>> c.pop() +{'a': 2} +>>> c['a'] +1 +""" + diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index cbbd88b06c..846023afc9 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -18,11 +18,13 @@ from django.utils.safestring import mark_safe from django.utils.tzinfo import LocalTimezone from unicode import unicode_tests +from context import context_tests import filters # Some other tests we would like to run __test__ = { 'unicode': unicode_tests, + 'context': context_tests, } ################################# diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py index 9e0bbb2d66..956432e7d5 100644 --- a/tests/regressiontests/views/views.py +++ b/tests/regressiontests/views/views.py @@ -1,7 +1,5 @@ from django.http import HttpResponse -from django.template import RequestContext def index_page(request): """Dummy index page""" return HttpResponse('<html><body>Dummy page</body></html>') - diff --git a/tests/runtests.py b/tests/runtests.py index 843850074b..2d3b737cec 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -107,8 +107,7 @@ def django_tests(verbosity, interactive, test_labels): 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.common.CommonMiddleware', ) - if not hasattr(settings, 'SITE_ID'): - settings.SITE_ID = 1 + settings.SITE_ID = 1 # Load all the ALWAYS_INSTALLED_APPS. # (This import statement is intentionally delayed until after we diff --git a/tests/urls.py b/tests/urls.py index d7251007c5..174e06969b 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -14,4 +14,7 @@ urlpatterns = patterns('', # django built-in views (r'^views/', include('regressiontests.views.urls')), + + # test urlconf for middleware tests + (r'^middleware/', include('regressiontests.middleware.urls')), ) |
