summaryrefslogtreecommitdiff
path: root/tests/modeltests
diff options
context:
space:
mode:
authorRobin Munn <robin.munn@gmail.com>2007-01-31 23:43:09 +0000
committerRobin Munn <robin.munn@gmail.com>2007-01-31 23:43:09 +0000
commitfe361e678a46dc4c717c79c2f12b3ba32293b81a (patch)
tree8f42488e7d95244bab3db7b2bf934e006940521a /tests/modeltests
parent122426e7453ed638a0c5be7e8b925adcddea3889 (diff)
Merged revisions 4186 to 4454 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/sqlalchemy@4455 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests/modeltests')
-rw-r--r--tests/modeltests/basic/models.py15
-rw-r--r--tests/modeltests/custom_columns/models.py92
-rw-r--r--tests/modeltests/generic_relations/models.py34
-rw-r--r--tests/modeltests/get_object_or_404/__init__.py0
-rw-r--r--tests/modeltests/get_object_or_404/models.py86
-rw-r--r--tests/modeltests/lookup/models.py15
-rw-r--r--tests/modeltests/many_to_many/models.py26
-rw-r--r--tests/modeltests/model_forms/__init__.py0
-rw-r--r--tests/modeltests/model_forms/models.py284
-rw-r--r--tests/modeltests/or_lookups/models.py15
-rw-r--r--tests/modeltests/serializers/models.py21
-rw-r--r--tests/modeltests/test_client/views.py2
12 files changed, 558 insertions, 32 deletions
diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py
index 5638865f31..1663068892 100644
--- a/tests/modeltests/basic/models.py
+++ b/tests/modeltests/basic/models.py
@@ -10,6 +10,9 @@ class Article(models.Model):
headline = models.CharField(maxlength=100, default='Default headline')
pub_date = models.DateTimeField()
+ class Meta:
+ ordering = ('pub_date','headline')
+
def __str__(self):
return self.headline
@@ -245,7 +248,7 @@ datetime.datetime(2005, 7, 28, 0, 0)
# Slices (without step) are lazy:
>>> Article.objects.all()[0:5].filter()
-[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>]
+[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>]
# Slicing again works:
>>> Article.objects.all()[0:5][0:2]
@@ -253,17 +256,17 @@ datetime.datetime(2005, 7, 28, 0, 0)
>>> Article.objects.all()[0:5][:2]
[<Article: Area woman programs in Python>, <Article: Second article>]
>>> Article.objects.all()[0:5][4:]
-[<Article: Article 6>]
+[<Article: Default headline>]
>>> Article.objects.all()[0:5][5:]
[]
# Some more tests!
>>> Article.objects.all()[2:][0:2]
-[<Article: Third article>, <Article: Fourth article>]
+[<Article: Third article>, <Article: Article 6>]
>>> Article.objects.all()[2:][:2]
-[<Article: Third article>, <Article: Fourth article>]
+[<Article: Third article>, <Article: Article 6>]
>>> Article.objects.all()[2:][2:3]
-[<Article: Article 6>]
+[<Article: Default headline>]
# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work:
>>> Article.objects.all()[2:]
@@ -312,7 +315,7 @@ AttributeError: Manager isn't accessible via Article instances
# Bulk delete test: How many objects before and after the delete?
>>> Article.objects.all()
-[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
+[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>, <Article: Fourth article>, <Article: Article 7>, <Article: Updated article 8>]
>>> Article.objects.filter(id__lte=4).delete()
>>> Article.objects.all()
[<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py
index e88fa80da2..c09ca05557 100644
--- a/tests/modeltests/custom_columns/models.py
+++ b/tests/modeltests/custom_columns/models.py
@@ -1,53 +1,105 @@
"""
-17. Custom column names
+17. Custom column/table names
If your database column name is different than your model attribute, use the
``db_column`` parameter. Note that you'll use the field's name, not its column
name, in API usage.
+
+If your database table name is different than your model name, use the
+``db_table`` Meta attribute. This has no effect on the API used to
+query the database.
+
+If you need to use a table name for a many-to-many relationship that differs
+from the default generated name, use the ``db_table`` parameter on the
+ManyToMany field. This has no effect on the API for querying the database.
+
"""
from django.db import models
-class Person(models.Model):
+class Author(models.Model):
first_name = models.CharField(maxlength=30, db_column='firstname')
last_name = models.CharField(maxlength=30, db_column='last')
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
+ class Meta:
+ db_table = 'my_author_table'
+ ordering = ('last_name','first_name')
+
+class Article(models.Model):
+ headline = models.CharField(maxlength=100)
+ authors = models.ManyToManyField(Author, db_table='my_m2m_table')
+
+ def __str__(self):
+ return self.headline
+
+ class Meta:
+ ordering = ('headline',)
+
__test__ = {'API_TESTS':"""
-# Create a Person.
->>> p = Person(first_name='John', last_name='Smith')
->>> p.save()
+# Create a Author.
+>>> a = Author(first_name='John', last_name='Smith')
+>>> a.save()
->>> p.id
+>>> a.id
1
->>> Person.objects.all()
-[<Person: John Smith>]
+# Create another author
+>>> a2 = Author(first_name='Peter', last_name='Jones')
+>>> a2.save()
+
+# Create an article
+>>> art = Article(headline='Django lets you build web apps easily')
+>>> art.save()
+>>> art.authors = [a, a2]
->>> Person.objects.filter(first_name__exact='John')
-[<Person: John Smith>]
+# Although the table and column names on Author have been set to
+# custom values, nothing about using the Author model has changed...
->>> Person.objects.get(first_name__exact='John')
-<Person: John Smith>
+# Query the available authors
+>>> Author.objects.all()
+[<Author: Peter Jones>, <Author: John Smith>]
->>> Person.objects.filter(firstname__exact='John')
+>>> Author.objects.filter(first_name__exact='John')
+[<Author: John Smith>]
+
+>>> Author.objects.get(first_name__exact='John')
+<Author: John Smith>
+
+>>> Author.objects.filter(firstname__exact='John')
Traceback (most recent call last):
...
TypeError: Cannot resolve keyword 'firstname' into field
->>> p = Person.objects.get(last_name__exact='Smith')
->>> p.first_name
+>>> a = Author.objects.get(last_name__exact='Smith')
+>>> a.first_name
'John'
->>> p.last_name
+>>> a.last_name
'Smith'
->>> p.firstname
+>>> a.firstname
Traceback (most recent call last):
...
-AttributeError: 'Person' object has no attribute 'firstname'
->>> p.last
+AttributeError: 'Author' object has no attribute 'firstname'
+>>> a.last
Traceback (most recent call last):
...
-AttributeError: 'Person' object has no attribute 'last'
+AttributeError: 'Author' object has no attribute 'last'
+
+# Although the Article table uses a custom m2m table,
+# nothing about using the m2m relationship has changed...
+
+# Get all the authors for an article
+>>> art.authors.all()
+[<Author: Peter Jones>, <Author: John Smith>]
+
+# Get the articles for an author
+>>> a.article_set.all()
+[<Article: Django lets you build web apps easily>]
+
+# Query the authors across the m2m relation
+>>> art.authors.filter(last_name='Jones')
+[<Author: Peter Jones>]
+
"""}
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
index eb64d7ec3d..2bfb55e618 100644
--- a/tests/modeltests/generic_relations/models.py
+++ b/tests/modeltests/generic_relations/models.py
@@ -65,14 +65,14 @@ __test__ = {'API_TESTS':"""
# Objects with declared GenericRelations can be tagged directly -- the API
# mimics the many-to-many API.
->>> lion.tags.create(tag="yellow")
-<TaggedItem: yellow>
->>> lion.tags.create(tag="hairy")
-<TaggedItem: hairy>
>>> bacon.tags.create(tag="fatty")
<TaggedItem: fatty>
>>> bacon.tags.create(tag="salty")
<TaggedItem: salty>
+>>> lion.tags.create(tag="yellow")
+<TaggedItem: yellow>
+>>> lion.tags.create(tag="hairy")
+<TaggedItem: hairy>
>>> lion.tags.all()
[<TaggedItem: hairy>, <TaggedItem: yellow>]
@@ -105,4 +105,30 @@ __test__ = {'API_TESTS':"""
[<TaggedItem: shiny>]
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
[<TaggedItem: clearish>]
+
+# If you delete an object with an explicit Generic relation, the related
+# objects are deleted when the source object is deleted.
+# Original list of tags:
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
+
+>>> lion.delete()
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+
+# 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()]
+[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+
+# 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()
+>>> bacon.tags.all()
+[<TaggedItem: salty>]
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+
"""}
diff --git a/tests/modeltests/get_object_or_404/__init__.py b/tests/modeltests/get_object_or_404/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/modeltests/get_object_or_404/__init__.py
diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py
new file mode 100644
index 0000000000..79bcc6f52c
--- /dev/null
+++ b/tests/modeltests/get_object_or_404/models.py
@@ -0,0 +1,86 @@
+"""
+34. DB-API Shortcuts
+
+get_object_or_404 is a shortcut function to be used in view functions for
+performing a get() lookup and raising a Http404 exception if a DoesNotExist
+exception was rasied during the get() call.
+
+get_list_or_404 is a shortcut function to be used in view functions for
+performing a filter() lookup and raising a Http404 exception if a DoesNotExist
+exception was rasied during the filter() call.
+"""
+
+from django.db import models
+from django.http import Http404
+from django.shortcuts import get_object_or_404, get_list_or_404
+
+class Author(models.Model):
+ name = models.CharField(maxlength=50)
+
+ def __str__(self):
+ return self.name
+
+class ArticleManager(models.Manager):
+ def get_query_set(self):
+ return super(ArticleManager, self).get_query_set().filter(authors__name__icontains='sir')
+
+class Article(models.Model):
+ authors = models.ManyToManyField(Author)
+ title = models.CharField(maxlength=50)
+ objects = models.Manager()
+ by_a_sir = ArticleManager()
+
+ def __str__(self):
+ return self.title
+
+__test__ = {'API_TESTS':"""
+# Create some Authors.
+>>> a = Author.objects.create(name="Brave Sir Robin")
+>>> a.save()
+>>> a2 = Author.objects.create(name="Patsy")
+>>> a2.save()
+
+# No Articles yet, so we should get a Http404 error.
+>>> get_object_or_404(Article, title="Foo")
+Traceback (most recent call last):
+...
+Http404
+
+# Create an Article.
+>>> article = Article.objects.create(title="Run away!")
+>>> article.authors = [a, a2]
+>>> article.save()
+
+# get_object_or_404 can be passed a Model to query.
+>>> get_object_or_404(Article, title__contains="Run")
+<Article: Run away!>
+
+# We can also use the the Article manager through an Author object.
+>>> get_object_or_404(a.article_set, title__contains="Run")
+<Article: Run away!>
+
+# No articles containing "Camelot". This should raise a Http404 error.
+>>> get_object_or_404(a.article_set, title__contains="Camelot")
+Traceback (most recent call last):
+...
+Http404
+
+# Custom managers can be used too.
+>>> get_object_or_404(Article.by_a_sir, title="Run away!")
+<Article: Run away!>
+
+# get_list_or_404 can be used to get lists of objects
+>>> get_list_or_404(a.article_set, title__icontains='Run')
+[<Article: Run away!>]
+
+# Http404 is returned if the list is empty
+>>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
+Traceback (most recent call last):
+...
+Http404
+
+# Custom managers can be used too.
+>>> get_list_or_404(Article.by_a_sir, title__icontains="Run")
+[<Article: Run away!>]
+
+"""}
diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py
index 09c3aa7aa8..aa903d1a64 100644
--- a/tests/modeltests/lookup/models.py
+++ b/tests/modeltests/lookup/models.py
@@ -191,4 +191,19 @@ DoesNotExist: Article matching query does not exist.
>>> Article.objects.filter(headline__contains='\\')
[<Article: Article with \ backslash>]
+# none() returns an EmptyQuerySet that behaves like any other QuerySet object
+>>> Article.objects.none()
+[]
+>>> Article.objects.none().filter(headline__startswith='Article')
+[]
+>>> Article.objects.none().count()
+0
+
+# using __in with an empty list should return an empty query set
+>>> Article.objects.filter(id__in=[])
+[]
+
+>>> Article.objects.exclude(id__in=[])
+[<Article: Article with \ backslash>, <Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
+
"""}
diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py
index 357f3ca629..5e46ad428d 100644
--- a/tests/modeltests/many_to_many/models.py
+++ b/tests/modeltests/many_to_many/models.py
@@ -203,7 +203,19 @@ __test__ = {'API_TESTS':"""
>>> p2.article_set.all()
[<Article: Oxygen-free diet works wonders>]
-# Recreate the article and Publication we just deleted.
+# Relation sets can also be set using primary key values
+>>> p2.article_set = [a4.id, a5.id]
+>>> p2.article_set.all()
+[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
+>>> a4.publications.all()
+[<Publication: Science News>]
+>>> a4.publications = [p3.id]
+>>> p2.article_set.all()
+[<Article: Oxygen-free diet works wonders>]
+>>> a4.publications.all()
+[<Publication: Science Weekly>]
+
+# Recreate the article and Publication we have deleted.
>>> p1 = Publication(id=None, title='The Python Journal')
>>> p1.save()
>>> a2 = Article(id=None, headline='NASA uses Python')
@@ -231,4 +243,16 @@ __test__ = {'API_TESTS':"""
>>> p1.article_set.all()
[<Article: NASA uses Python>]
+# An alternate to calling clear() is to assign the empty set
+>>> p1.article_set = []
+>>> p1.article_set.all()
+[]
+
+>>> a2.publications = [p1, new_publication]
+>>> a2.publications.all()
+[<Publication: Highlights for Children>, <Publication: The Python Journal>]
+>>> a2.publications = []
+>>> a2.publications.all()
+[]
+
"""}
diff --git a/tests/modeltests/model_forms/__init__.py b/tests/modeltests/model_forms/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/modeltests/model_forms/__init__.py
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
new file mode 100644
index 0000000000..657d506b33
--- /dev/null
+++ b/tests/modeltests/model_forms/models.py
@@ -0,0 +1,284 @@
+"""
+34. 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 clean_data into the instance. It also takes
+a commit=True parameter.
+"""
+
+from django.db import models
+
+class Category(models.Model):
+ name = models.CharField(maxlength=20)
+ url = models.CharField('The URL', maxlength=40)
+
+ def __str__(self):
+ return self.name
+
+class Writer(models.Model):
+ name = models.CharField(maxlength=50, help_text='Use both first and last names.')
+
+ def __str__(self):
+ return self.name
+
+class Article(models.Model):
+ headline = models.CharField(maxlength=50)
+ pub_date = models.DateField()
+ writer = models.ForeignKey(Writer)
+ article = models.TextField()
+ categories = models.ManyToManyField(Category, blank=True)
+
+ def __str__(self):
+ return self.headline
+
+__test__ = {'API_TESTS': """
+>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
+>>> import datetime
+
+>>> Category.objects.all()
+[]
+
+>>> CategoryForm = form_for_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>
+<tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
+>>> print f.as_ul()
+<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li>
+<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>
+>>> print f['name']
+<input id="id_name" type="text" name="name" maxlength="20" />
+
+>>> f = CategoryForm(auto_id=False)
+>>> print f.as_ul()
+<li>Name: <input type="text" name="name" maxlength="20" /></li>
+<li>The URL: <input type="text" name="url" maxlength="40" /></li>
+
+>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
+>>> f.is_valid()
+True
+>>> f.clean_data
+{'url': u'entertainment', 'name': u'Entertainment'}
+>>> obj = f.save()
+>>> obj
+<Category: Entertainment>
+>>> Category.objects.all()
+[<Category: Entertainment>]
+
+>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
+>>> f.is_valid()
+True
+>>> f.clean_data
+{'url': u'test', 'name': u"It's a test"}
+>>> obj = f.save()
+>>> obj
+<Category: It's a test>
+>>> Category.objects.all()
+[<Category: Entertainment>, <Category: It's a test>]
+
+If you call save() with commit=False, then it will return an object that
+hasn't yet been saved to the database. In this case, it's up to you to call
+save() on the resulting model instance.
+>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
+>>> f.is_valid()
+True
+>>> f.clean_data
+{'url': u'third', 'name': u'Third test'}
+>>> obj = f.save(commit=False)
+>>> obj
+<Category: Third test>
+>>> Category.objects.all()
+[<Category: Entertainment>, <Category: It's a test>]
+>>> obj.save()
+>>> Category.objects.all()
+[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
+
+If you call save() with invalid data, you'll get a ValueError.
+>>> f = CategoryForm({'name': '', 'url': 'foo'})
+>>> f.errors
+{'name': [u'This field is required.']}
+>>> f.clean_data
+Traceback (most recent call last):
+...
+AttributeError: 'CategoryForm' object has no attribute 'clean_data'
+>>> f.save()
+Traceback (most recent call last):
+...
+ValueError: The Category could not be created because the data didn't validate.
+>>> f = CategoryForm({'name': '', 'url': 'foo'})
+>>> f.save()
+Traceback (most recent call last):
+...
+ValueError: The Category could not be created because the data didn't validate.
+
+Create a couple of Writers.
+>>> w = Writer(name='Mike Royko')
+>>> w.save()
+>>> w = Writer(name='Bob Woodward')
+>>> w.save()
+
+ManyToManyFields are represented by a MultipleChoiceField, and ForeignKeys are
+represented by a ChoiceField.
+>>> ArticleForm = form_for_model(Article)
+>>> f = ArticleForm(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>
+<tr><th>Writer:</th><td><select name="writer">
+<option value="" selected="selected">---------</option>
+<option value="1">Mike Royko</option>
+<option value="2">Bob Woodward</option>
+</select></td></tr>
+<tr><th>Article:</th><td><textarea name="article"></textarea></td></tr>
+<tr><th>Categories:</th><td><select multiple="multiple" name="categories">
+<option value="1">Entertainment</option>
+<option value="2">It&#39;s a test</option>
+<option value="3">Third test</option>
+</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</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)
+>>> 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>
+
+>>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
+>>> art.save()
+>>> art.id
+1
+>>> TestArticleForm = form_for_instance(art)
+>>> f = TestArticleForm(auto_id=False)
+>>> print f.as_ul()
+<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
+<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
+<li>Writer: <select name="writer">
+<option value="">---------</option>
+<option value="1" selected="selected">Mike Royko</option>
+<option value="2">Bob Woodward</option>
+</select></li>
+<li>Article: <textarea name="article">Hello.</textarea></li>
+<li>Categories: <select multiple="multiple" name="categories">
+<option value="1">Entertainment</option>
+<option value="2">It&#39;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'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'})
+>>> f.is_valid()
+True
+>>> new_art = f.save()
+>>> new_art.id
+1
+>>> new_art = Article.objects.get(id=1)
+>>> new_art.headline
+'New headline'
+
+Add some categories and test the many-to-many form output.
+>>> new_art.categories.all()
+[]
+>>> 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)
+>>> print f.as_ul()
+<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
+<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
+<li>Writer: <select name="writer">
+<option value="">---------</option>
+<option value="1" selected="selected">Mike Royko</option>
+<option value="2">Bob Woodward</option>
+</select></li>
+<li>Article: <textarea name="article">Hello.</textarea></li>
+<li>Categories: <select multiple="multiple" name="categories">
+<option value="1" selected="selected">Entertainment</option>
+<option value="2">It&#39;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'New headline', 'pub_date': u'1988-01-04',
+... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
+>>> new_art = f.save()
+>>> new_art.id
+1
+>>> new_art = Article.objects.get(id=1)
+>>> new_art.categories.all()
+[<Category: Entertainment>, <Category: It's a test>]
+
+Now, submit form data with no categories. This deletes the existing categories.
+>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04',
+... 'writer': u'1', 'article': u'Hello.'})
+>>> new_art = f.save()
+>>> new_art.id
+1
+>>> new_art = Article.objects.get(id=1)
+>>> new_art.categories.all()
+[]
+
+Create a new article, with categories, via the form.
+>>> ArticleForm = form_for_model(Article)
+>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
+... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
+>>> new_art = f.save()
+>>> new_art.id
+2
+>>> new_art = Article.objects.get(id=2)
+>>> new_art.categories.all()
+[<Category: Entertainment>, <Category: It's a test>]
+
+Create a new article, with no categories, via the form.
+>>> ArticleForm = form_for_model(Article)
+>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
+... 'writer': u'1', 'article': u'Test.'})
+>>> new_art = f.save()
+>>> new_art.id
+3
+>>> new_art = Article.objects.get(id=3)
+>>> new_art.categories.all()
+[]
+
+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
+existing Category instance.
+>>> class ShortCategory(Form):
+... name = CharField(max_length=5)
+... url = CharField(max_length=3)
+>>> cat = Category.objects.get(name='Third test')
+>>> cat
+<Category: Third test>
+>>> cat.id
+3
+>>> sc = ShortCategory({'name': 'Third', 'url': '3rd'})
+>>> save_instance(sc, cat)
+<Category: Third>
+>>> Category.objects.get(id=3)
+<Category: Third>
+"""}
diff --git a/tests/modeltests/or_lookups/models.py b/tests/modeltests/or_lookups/models.py
index 2de18edc1f..9f926a7373 100644
--- a/tests/modeltests/or_lookups/models.py
+++ b/tests/modeltests/or_lookups/models.py
@@ -69,6 +69,21 @@ __test__ = {'API_TESTS':"""
>>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
+# You could also use "in" to accomplish the same as above.
+>>> Article.objects.filter(pk__in=[1,2,3])
+[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
+
+>>> Article.objects.filter(pk__in=[1,2,3,4])
+[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
+
+# Passing "in" an empty list returns no results ...
+>>> Article.objects.filter(pk__in=[])
+[]
+
+# ... but can return results if we OR it with another query.
+>>> Article.objects.filter(Q(pk__in=[]) | Q(headline__icontains='goodbye'))
+[<Article: Goodbye>, <Article: Hello and goodbye>]
+
# Q arg objects are ANDed
>>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
[<Article: Hello and goodbye>]
diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py
index d1d10b43c0..e24ff537c1 100644
--- a/tests/modeltests/serializers/models.py
+++ b/tests/modeltests/serializers/models.py
@@ -37,6 +37,13 @@ class Article(models.Model):
def __str__(self):
return self.headline
+class AuthorProfile(models.Model):
+ author = models.OneToOneField(Author)
+ date_of_birth = models.DateField()
+
+ def __str__(self):
+ return "Profile of %s" % self.author
+
__test__ = {'API_TESTS':"""
# Create some data:
>>> from datetime import datetime
@@ -118,4 +125,18 @@ __test__ = {'API_TESTS':"""
>>> Article.objects.all()
[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
+# If you use your own primary key field (such as a OneToOneField),
+# it doesn't appear in the serialized field list - it replaces the
+# pk identifier.
+>>> profile = AuthorProfile(author=joe, date_of_birth=datetime(1970,1,1))
+>>> profile.save()
+
+>>> json = serializers.serialize("json", AuthorProfile.objects.all())
+>>> json
+'[{"pk": "1", "model": "serializers.authorprofile", "fields": {"date_of_birth": "1970-01-01"}}]'
+
+>>> for obj in serializers.deserialize("json", json):
+... print obj
+<DeserializedObject: Profile of Joe>
+
"""}
diff --git a/tests/modeltests/test_client/views.py b/tests/modeltests/test_client/views.py
index bf131032eb..7acfc2db60 100644
--- a/tests/modeltests/test_client/views.py
+++ b/tests/modeltests/test_client/views.py
@@ -26,10 +26,10 @@ def redirect_view(request):
"A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client/get_view/')
-@login_required
def login_protected_view(request):
"A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
c = Context({'user': request.user})
return HttpResponse(t.render(c))
+login_protected_view = login_required(login_protected_view) \ No newline at end of file