1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

newforms-admin: Merged to [4431]

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@4432 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-01-26 23:27:26 +00:00
parent f2d2c64b97
commit e7c6bd9da2
5 changed files with 130 additions and 35 deletions

View File

@ -615,6 +615,7 @@ class ManyToManyField(RelatedField, Field):
filter_interface=kwargs.pop('filter_interface', None), filter_interface=kwargs.pop('filter_interface', None),
limit_choices_to=kwargs.pop('limit_choices_to', None), limit_choices_to=kwargs.pop('limit_choices_to', None),
symmetrical=kwargs.pop('symmetrical', True)) symmetrical=kwargs.pop('symmetrical', True))
self.db_table = kwargs.pop('db_table', None)
Field.__init__(self, **kwargs) Field.__init__(self, **kwargs)
msg = gettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') msg = gettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.')
@ -629,7 +630,10 @@ class ManyToManyField(RelatedField, Field):
def _get_m2m_db_table(self, opts): def _get_m2m_db_table(self, opts):
"Function that can be curried to provide the m2m table name for this relation" "Function that can be curried to provide the m2m table name for this relation"
return '%s_%s' % (opts.db_table, self.name) if self.db_table:
return self.db_table
else:
return '%s_%s' % (opts.db_table, self.name)
def _get_m2m_column_name(self, related): def _get_m2m_column_name(self, related):
"Function that can be curried to provide the source column name for the m2m table" "Function that can be curried to provide the source column name for the m2m table"

View File

@ -1,5 +1,6 @@
from django.db import backend, connection, transaction from django.db import backend, connection, transaction
from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.fields.generic import GenericRelation
from django.db.models import signals from django.db.models import signals
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
@ -979,18 +980,26 @@ def delete_objects(seen_objs):
pk_list = [pk for pk,instance in seen_objs[cls]] pk_list = [pk for pk,instance in seen_objs[cls]]
for related in cls._meta.get_all_related_many_to_many_objects(): for related in cls._meta.get_all_related_many_to_many_objects():
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): if not isinstance(related.field, GenericRelation):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
(qn(related.field.m2m_db_table()), cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
qn(related.field.m2m_reverse_name()), (qn(related.field.m2m_db_table()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), qn(related.field.m2m_reverse_name()),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
for f in cls._meta.many_to_many: for f in cls._meta.many_to_many:
if isinstance(f, GenericRelation):
from django.contrib.contenttypes.models import ContentType
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
args_extra = [ContentType.objects.get_for_model(cls).id]
else:
query_extra = ''
args_extra = []
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(f.m2m_db_table()), qn(f.m2m_column_name()), (qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
for field in cls._meta.fields: for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs: if field.rel and field.null and field.rel.to in seen_objs:
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):

View File

@ -874,6 +874,10 @@ the relationship should work. All are optional:
force Django to add the descriptor for the reverse force Django to add the descriptor for the reverse
relationship, allowing ``ManyToMany`` relationships to be relationship, allowing ``ManyToMany`` relationships to be
non-symmetrical. non-symmetrical.
``db_table`` The name of the table to create for storing the many-to-many
data. If this is not provided, Django will assume a default
name based upon the names of the two tables being joined.
======================= ============================================================ ======================= ============================================================

View File

@ -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 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 ``db_column`` parameter. Note that you'll use the field's name, not its column
name, in API usage. 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 from django.db import models
class Person(models.Model): class Author(models.Model):
first_name = models.CharField(maxlength=30, db_column='firstname') first_name = models.CharField(maxlength=30, db_column='firstname')
last_name = models.CharField(maxlength=30, db_column='last') last_name = models.CharField(maxlength=30, db_column='last')
def __str__(self): def __str__(self):
return '%s %s' % (self.first_name, self.last_name) return '%s %s' % (self.first_name, self.last_name)
__test__ = {'API_TESTS':""" class Meta:
# Create a Person. db_table = 'my_author_table'
>>> p = Person(first_name='John', last_name='Smith') ordering = ('last_name','first_name')
>>> p.save()
>>> p.id 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 Author.
>>> a = Author(first_name='John', last_name='Smith')
>>> a.save()
>>> a.id
1 1
>>> Person.objects.all() # Create another author
[<Person: John Smith>] >>> a2 = Author(first_name='Peter', last_name='Jones')
>>> a2.save()
>>> Person.objects.filter(first_name__exact='John') # Create an article
[<Person: John Smith>] >>> art = Article(headline='Django lets you build web apps easily')
>>> art.save()
>>> art.authors = [a, a2]
>>> Person.objects.get(first_name__exact='John') # Although the table and column names on Author have been set to
<Person: John Smith> # custom values, nothing about using the Author model has changed...
>>> Person.objects.filter(firstname__exact='John') # Query the available authors
>>> Author.objects.all()
[<Author: Peter Jones>, <Author: John Smith>]
>>> 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): Traceback (most recent call last):
... ...
TypeError: Cannot resolve keyword 'firstname' into field TypeError: Cannot resolve keyword 'firstname' into field
>>> p = Person.objects.get(last_name__exact='Smith') >>> a = Author.objects.get(last_name__exact='Smith')
>>> p.first_name >>> a.first_name
'John' 'John'
>>> p.last_name >>> a.last_name
'Smith' 'Smith'
>>> p.firstname >>> a.firstname
Traceback (most recent call last): Traceback (most recent call last):
... ...
AttributeError: 'Person' object has no attribute 'firstname' AttributeError: 'Author' object has no attribute 'firstname'
>>> p.last >>> a.last
Traceback (most recent call 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>]
"""} """}

View File

@ -65,14 +65,14 @@ __test__ = {'API_TESTS':"""
# Objects with declared GenericRelations can be tagged directly -- the API # Objects with declared GenericRelations can be tagged directly -- the API
# mimics the many-to-many 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") >>> bacon.tags.create(tag="fatty")
<TaggedItem: fatty> <TaggedItem: fatty>
>>> bacon.tags.create(tag="salty") >>> bacon.tags.create(tag="salty")
<TaggedItem: salty> <TaggedItem: salty>
>>> lion.tags.create(tag="yellow")
<TaggedItem: yellow>
>>> lion.tags.create(tag="hairy")
<TaggedItem: hairy>
>>> lion.tags.all() >>> lion.tags.all()
[<TaggedItem: hairy>, <TaggedItem: yellow>] [<TaggedItem: hairy>, <TaggedItem: yellow>]
@ -105,4 +105,30 @@ __test__ = {'API_TESTS':"""
[<TaggedItem: shiny>] [<TaggedItem: shiny>]
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id) >>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
[<TaggedItem: clearish>] [<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)]
"""} """}