2007-08-20 10:28:40 +08:00
from django . db import connection , transaction
2006-06-23 12:37:00 +08:00
from django . db . models import signals , get_model
2008-07-19 07:54:34 +08:00
from django . db . models . fields import AutoField , Field , IntegerField , PositiveIntegerField , PositiveSmallIntegerField , FieldDoesNotExist
2006-05-02 09:31:56 +08:00
from django . db . models . related import RelatedObject
2008-07-21 23:41:21 +08:00
from django . db . models . query import QuerySet
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
from django . db . models . query_utils import QueryWrapper
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
from django . utils . translation import ugettext_lazy , string_concat , ungettext , ugettext as _
2006-05-02 09:31:56 +08:00
from django . utils . functional import curry
from django . core import validators
2006-12-16 02:00:50 +08:00
from django import oldforms
2008-07-19 09:22:26 +08:00
from django import forms
2006-05-02 09:31:56 +08:00
2007-06-23 11:18:22 +08:00
try :
set
except NameError :
from sets import Set as set # Python 2.3 fallback
2006-05-02 09:31:56 +08:00
# Values for Relation.edit_inline.
TABULAR , STACKED = 1 , 2
RECURSIVE_RELATIONSHIP_CONSTANT = ' self '
pending_lookups = { }
2008-07-29 20:41:08 +08:00
def add_lazy_relation ( cls , field , relation , operation ) :
2008-02-27 05:13:16 +08:00
"""
Adds a lookup on ` ` cls ` ` when a related field is defined using a string ,
i . e . : :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
class MyModel ( Model ) :
fk = ForeignKey ( " AnotherModel " )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
This string can be :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
* RECURSIVE_RELATIONSHIP_CONSTANT ( i . e . " self " ) to indicate a recursive
relation .
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
* The name of a model ( i . e " AnotherModel " ) to indicate another model in
the same app .
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
* An app - label and model name ( i . e . " someapp.AnotherModel " ) to indicate
another model in a different app .
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
If the other model hasn ' t yet been loaded -- almost a given if you ' re using
lazy relationships - - then the relation won ' t be set up until the
class_prepared signal fires at the end of model initialization .
2008-07-29 20:41:08 +08:00
operation is the work that must be performed once the relation can be resolved .
2008-02-27 05:13:16 +08:00
"""
# Check for recursive relations
if relation == RECURSIVE_RELATIONSHIP_CONSTANT :
app_label = cls . _meta . app_label
model_name = cls . __name__
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
else :
# Look for an "app.Model" relation
try :
app_label , model_name = relation . split ( " . " )
except ValueError :
# If we can't split, assume a model in current app
app_label = cls . _meta . app_label
model_name = relation
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-02-27 05:13:16 +08:00
# Try to look up the related model, and if it's already loaded resolve the
# string right away. If get_model returns None, it means that the related
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# model isn't loaded yet, so we need to pend the relation until the class
2008-02-27 05:13:16 +08:00
# is prepared.
model = get_model ( app_label , model_name , False )
2006-06-23 12:37:00 +08:00
if model :
2008-07-29 20:41:08 +08:00
operation ( field , model , cls )
2006-06-23 12:37:00 +08:00
else :
2008-02-27 05:13:16 +08:00
key = ( app_label , model_name )
2008-07-29 20:41:08 +08:00
value = ( cls , field , operation )
2008-02-27 05:13:16 +08:00
pending_lookups . setdefault ( key , [ ] ) . append ( value )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2008-08-06 23:32:46 +08:00
def do_pending_lookups ( sender , * * kwargs ) :
2008-02-27 05:13:16 +08:00
"""
Handle any pending relations to the sending model . Sent from class_prepared .
"""
key = ( sender . _meta . app_label , sender . __name__ )
2008-07-29 20:41:08 +08:00
for cls , field , operation in pending_lookups . pop ( key , [ ] ) :
operation ( field , sender , cls )
2006-05-02 09:31:56 +08:00
2008-08-06 23:32:46 +08:00
signals . class_prepared . connect ( do_pending_lookups )
2006-05-02 09:31:56 +08:00
def manipulator_valid_rel_key ( f , self , field_data , all_data ) :
" Validates that the value is a valid foreign key "
klass = f . rel . to
try :
2006-07-14 05:31:53 +08:00
klass . _default_manager . get ( * * { f . rel . field_name : field_data } )
2006-05-02 09:31:56 +08:00
except klass . DoesNotExist :
2007-02-12 07:50:35 +08:00
raise validators . ValidationError , _ ( " Please enter a valid %s . " ) % f . verbose_name
2006-05-02 09:31:56 +08:00
#HACK
class RelatedField ( object ) :
def contribute_to_class ( self , cls , name ) :
sup = super ( RelatedField , self )
# Add an accessor to allow easy determination of the related query path for this field
self . related_query_name = curry ( self . _get_related_query_name , cls . _meta )
if hasattr ( sup , ' contribute_to_class ' ) :
sup . contribute_to_class ( cls , name )
2008-06-26 15:04:18 +08:00
if not cls . _meta . abstract and self . rel . related_name :
self . rel . related_name = self . rel . related_name % { ' class ' : cls . __name__ . lower ( ) }
2006-05-02 09:31:56 +08:00
other = self . rel . to
if isinstance ( other , basestring ) :
2008-07-29 20:41:08 +08:00
def resolve_related_class ( field , model , cls ) :
field . rel . to = model
field . do_related_class ( model , cls )
add_lazy_relation ( cls , self , other , resolve_related_class )
2006-05-02 09:31:56 +08:00
else :
self . do_related_class ( other , cls )
def set_attributes_from_rel ( self ) :
self . name = self . name or ( self . rel . to . _meta . object_name . lower ( ) + ' _ ' + self . rel . to . _meta . pk . name )
2008-07-29 13:17:47 +08:00
if self . verbose_name is None :
self . verbose_name = self . rel . to . _meta . verbose_name
2006-05-02 09:31:56 +08:00
self . rel . field_name = self . rel . field_name or self . rel . to . _meta . pk . name
def do_related_class ( self , other , cls ) :
self . set_attributes_from_rel ( )
related = RelatedObject ( other , cls , self )
2008-06-26 15:04:18 +08:00
if not cls . _meta . abstract :
self . contribute_to_related_class ( other , related )
2006-05-02 09:31:56 +08:00
2006-07-01 09:14:41 +08:00
def get_db_prep_lookup ( self , lookup_type , value ) :
2006-07-14 05:31:53 +08:00
# If we are doing a lookup on a Related Field, we must be
# comparing object instances. The value should be the PK of value,
2006-07-01 09:14:41 +08:00
# not value itself.
def pk_trace ( value ) :
2006-07-14 05:31:53 +08:00
# Value may be a primary key, or an object held in a relation.
2006-07-01 09:14:41 +08:00
# If it is an object, then we need to get the primary key value for
# that object. In certain conditions (especially one-to-one relations),
# the primary key may itself be an object - so we need to keep drilling
# down until we hit a value that can be used for a comparison.
v = value
try :
while True :
v = getattr ( v , v . _meta . pk . name )
except AttributeError :
pass
2006-07-14 05:31:53 +08:00
return v
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
if hasattr ( value , ' as_sql ' ) :
sql , params = value . as_sql ( )
return QueryWrapper ( ( ' ( %s ) ' % sql ) , params )
2006-07-01 09:14:41 +08:00
if lookup_type == ' exact ' :
return [ pk_trace ( value ) ]
if lookup_type == ' in ' :
return [ pk_trace ( v ) for v in value ]
elif lookup_type == ' isnull ' :
return [ ]
raise TypeError , " Related Field has invalid lookup: %s " % lookup_type
2006-07-14 05:31:53 +08:00
2006-05-02 09:31:56 +08:00
def _get_related_query_name ( self , opts ) :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# This method defines the name that can be used to identify this
# related object in a table-spanning query. It uses the lower-cased
# object_name by default, but this can be overridden with the
# "related_name" option.
2006-05-02 09:31:56 +08:00
return self . rel . related_name or opts . object_name . lower ( )
class SingleRelatedObjectDescriptor ( object ) :
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# a single "remote" value, on the class pointed to by a related field.
# In the example "place.restaurant", the restaurant attribute is a
# SingleRelatedObjectDescriptor instance.
def __init__ ( self , related ) :
self . related = related
2008-05-29 20:17:03 +08:00
self . cache_name = ' _ %s _cache ' % related . get_accessor_name ( )
2006-05-02 09:31:56 +08:00
def __get__ ( self , instance , instance_type = None ) :
if instance is None :
raise AttributeError , " %s must be accessed via instance " % self . related . opts . object_name
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
try :
return getattr ( instance , self . cache_name )
except AttributeError :
params = { ' %s __pk ' % self . related . field . name : instance . _get_pk_val ( ) }
rel_obj = self . related . model . _default_manager . get ( * * params )
setattr ( instance , self . cache_name , rel_obj )
return rel_obj
2006-05-02 09:31:56 +08:00
def __set__ ( self , instance , value ) :
if instance is None :
raise AttributeError , " %s must be accessed via instance " % self . related . opts . object_name
2008-06-29 18:35:35 +08:00
# The similarity of the code below to the code in
2008-06-05 08:39:32 +08:00
# ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch
# of small differences that would make a common base class convoluted.
2008-06-29 18:35:35 +08:00
2008-06-05 08:39:32 +08:00
# If null=True, we can assign null here, but otherwise the value needs
# to be an instance of the related class.
if value is None and self . related . field . null == False :
raise ValueError ( ' Cannot assign None: " %s . %s " does not allow null values. ' %
( instance . _meta . object_name , self . related . get_accessor_name ( ) ) )
elif value is not None and not isinstance ( value , self . related . model ) :
raise ValueError ( ' Cannot assign " %r " : " %s . %s " must be a " %s " instance. ' %
2008-06-29 18:35:35 +08:00
( value , instance . _meta . object_name ,
2008-06-05 08:39:32 +08:00
self . related . get_accessor_name ( ) , self . related . opts . object_name ) )
2008-06-29 18:35:35 +08:00
2006-05-02 09:31:56 +08:00
# Set the value of the related field
setattr ( value , self . related . field . rel . get_related_field ( ) . attname , instance )
2008-06-05 08:39:32 +08:00
# Since we already know what the related object is, seed the related
2008-06-29 18:35:35 +08:00
# object caches now, too. This avoids another db hit if you get the
2008-06-05 08:39:32 +08:00
# object you just set.
setattr ( instance , self . cache_name , value )
setattr ( value , self . related . field . get_cache_name ( ) , instance )
2006-05-02 09:31:56 +08:00
class ReverseSingleRelatedObjectDescriptor ( object ) :
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# a single "remote" value, on the class that defines the related field.
# In the example "choice.poll", the poll attribute is a
# ReverseSingleRelatedObjectDescriptor instance.
def __init__ ( self , field_with_rel ) :
self . field = field_with_rel
def __get__ ( self , instance , instance_type = None ) :
if instance is None :
raise AttributeError , " %s must be accessed via instance " % self . field . name
cache_name = self . field . get_cache_name ( )
try :
return getattr ( instance , cache_name )
except AttributeError :
val = getattr ( instance , self . field . attname )
if val is None :
# If NULL is an allowed value, return it.
if self . field . null :
return None
raise self . field . rel . to . DoesNotExist
other_field = self . field . rel . get_related_field ( )
if other_field . rel :
params = { ' %s __pk ' % self . field . rel . field_name : val }
else :
params = { ' %s __exact ' % self . field . rel . field_name : val }
2008-08-06 00:13:28 +08:00
# If the related manager indicates that it should be used for
# related fields, respect that.
rel_mgr = self . field . rel . to . _default_manager
if getattr ( rel_mgr , ' use_for_related_fields ' , False ) :
rel_obj = rel_mgr . get ( * * params )
else :
rel_obj = QuerySet ( self . field . rel . to ) . get ( * * params )
2006-05-02 09:31:56 +08:00
setattr ( instance , cache_name , rel_obj )
return rel_obj
def __set__ ( self , instance , value ) :
if instance is None :
raise AttributeError , " %s must be accessed via instance " % self . _field . name
2008-06-29 18:35:35 +08:00
2008-06-05 08:39:32 +08:00
# If null=True, we can assign null here, but otherwise the value needs
# to be an instance of the related class.
if value is None and self . field . null == False :
raise ValueError ( ' Cannot assign None: " %s . %s " does not allow null values. ' %
( instance . _meta . object_name , self . field . name ) )
elif value is not None and not isinstance ( value , self . field . rel . to ) :
raise ValueError ( ' Cannot assign " %r " : " %s . %s " must be a " %s " instance. ' %
2008-06-29 18:35:35 +08:00
( value , instance . _meta . object_name ,
2008-06-05 08:39:32 +08:00
self . field . name , self . field . rel . to . _meta . object_name ) )
2008-06-29 18:35:35 +08:00
2006-05-02 09:31:56 +08:00
# Set the value of the related field
try :
val = getattr ( value , self . field . rel . get_related_field ( ) . attname )
except AttributeError :
val = None
setattr ( instance , self . field . attname , val )
2008-06-05 08:39:32 +08:00
# Since we already know what the related object is, seed the related
2008-06-29 18:35:35 +08:00
# object cache now, too. This avoids another db hit if you get the
2008-06-05 08:39:32 +08:00
# object you just set.
setattr ( instance , self . field . get_cache_name ( ) , value )
2006-05-02 09:31:56 +08:00
class ForeignRelatedObjectsDescriptor ( object ) :
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# multiple "remote" values and have a ForeignKey pointed at them by
# some other model. In the example "poll.choice_set", the choice_set
# attribute is a ForeignRelatedObjectsDescriptor instance.
def __init__ ( self , related ) :
self . related = related # RelatedObject instance
def __get__ ( self , instance , instance_type = None ) :
if instance is None :
raise AttributeError , " Manager must be accessed via instance "
rel_field = self . related . field
rel_model = self . related . model
# Dynamically create a class that subclasses the related
# model's default manager.
superclass = self . related . model . _default_manager . __class__
class RelatedManager ( superclass ) :
def get_query_set ( self ) :
return superclass . get_query_set ( self ) . filter ( * * ( self . core_filters ) )
def add ( self , * objs ) :
for obj in objs :
setattr ( obj , rel_field . name , instance )
obj . save ( )
add . alters_data = True
def create ( self , * * kwargs ) :
new_obj = self . model ( * * kwargs )
self . add ( new_obj )
return new_obj
create . alters_data = True
# remove() and clear() are only provided if the ForeignKey can have a value of null.
if rel_field . null :
def remove ( self , * objs ) :
val = getattr ( instance , rel_field . rel . get_related_field ( ) . attname )
for obj in objs :
# Is obj actually part of this descriptor set?
if getattr ( obj , rel_field . attname ) == val :
setattr ( obj , rel_field . name , None )
obj . save ( )
else :
2006-06-04 08:23:51 +08:00
raise rel_field . rel . to . DoesNotExist , " %r is not related to %r . " % ( obj , instance )
2006-05-02 09:31:56 +08:00
remove . alters_data = True
def clear ( self ) :
for obj in self . all ( ) :
setattr ( obj , rel_field . name , None )
obj . save ( )
clear . alters_data = True
manager = RelatedManager ( )
2008-06-29 18:35:35 +08:00
attname = rel_field . rel . get_related_field ( ) . name
manager . core_filters = { ' %s __ %s ' % ( rel_field . name , attname ) :
getattr ( instance , attname ) }
2006-05-02 09:31:56 +08:00
manager . model = self . related . model
return manager
def __set__ ( self , instance , value ) :
if instance is None :
raise AttributeError , " Manager must be accessed via instance "
manager = self . __get__ ( instance )
# If the foreign key can support nulls, then completely clear the related set.
# Otherwise, just move the named objects into the set.
if self . related . field . null :
manager . clear ( )
2006-12-20 17:59:03 +08:00
manager . add ( * value )
2006-05-02 09:31:56 +08:00
2008-07-29 20:41:08 +08:00
def create_many_related_manager ( superclass , through = False ) :
2006-05-02 09:31:56 +08:00
""" Creates a manager that subclasses ' superclass ' (which is a Manager)
and adds behavior for many - to - many related objects . """
class ManyRelatedManager ( superclass ) :
def __init__ ( self , model = None , core_filters = None , instance = None , symmetrical = None ,
join_table = None , source_col_name = None , target_col_name = None ) :
super ( ManyRelatedManager , self ) . __init__ ( )
self . core_filters = core_filters
self . model = model
self . symmetrical = symmetrical
self . instance = instance
self . join_table = join_table
self . source_col_name = source_col_name
self . target_col_name = target_col_name
2008-07-29 20:41:08 +08:00
self . through = through
2006-05-03 03:51:41 +08:00
self . _pk_val = self . instance . _get_pk_val ( )
if self . _pk_val is None :
2008-06-12 12:13:16 +08:00
raise ValueError ( " %r instance needs to have a primary key value before a many-to-many relationship can be used. " % instance . __class__ . __name__ )
2006-05-02 09:31:56 +08:00
def get_query_set ( self ) :
return superclass . get_query_set ( self ) . filter ( * * ( self . core_filters ) )
2008-07-29 20:41:08 +08:00
# If the ManyToMany relation has an intermediary model,
# the add and remove methods do not exist.
if through is None :
def add ( self , * objs ) :
self . _add_items ( self . source_col_name , self . target_col_name , * objs )
2006-05-02 09:31:56 +08:00
2008-07-29 20:41:08 +08:00
# If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table
if self . symmetrical :
self . _add_items ( self . target_col_name , self . source_col_name , * objs )
add . alters_data = True
2006-05-02 09:31:56 +08:00
2008-07-29 20:41:08 +08:00
def remove ( self , * objs ) :
self . _remove_items ( self . source_col_name , self . target_col_name , * objs )
2006-05-02 09:31:56 +08:00
2008-07-29 20:41:08 +08:00
# If this is a symmetrical m2m relation to self, remove the mirror entry in the m2m table
if self . symmetrical :
self . _remove_items ( self . target_col_name , self . source_col_name , * objs )
remove . alters_data = True
2006-05-02 09:31:56 +08:00
def clear ( self ) :
self . _clear_items ( self . source_col_name )
# If this is a symmetrical m2m relation to self, clear the mirror entry in the m2m table
if self . symmetrical :
self . _clear_items ( self . target_col_name )
clear . alters_data = True
def create ( self , * * kwargs ) :
2008-07-29 20:41:08 +08:00
# This check needs to be done here, since we can't later remove this
# from the method lookup table, as we do with add and remove.
if through is not None :
raise AttributeError , " Cannot use create() on a ManyToManyField which specifies an intermediary model. Use %s ' s Manager instead. " % through
2006-05-02 09:31:56 +08:00
new_obj = self . model ( * * kwargs )
new_obj . save ( )
self . add ( new_obj )
return new_obj
create . alters_data = True
def _add_items ( self , source_col_name , target_col_name , * objs ) :
# join_table: name of the m2m link table
# source_col_name: the PK colname in join_table for the source object
# target_col_name: the PK colname in join_table for the target object
2007-01-30 00:09:25 +08:00
# *objs - objects to add. Either object instances, or primary keys of object instances.
2006-05-02 09:31:56 +08:00
2006-12-20 21:57:17 +08:00
# If there aren't any objects, there is nothing to do.
if objs :
2006-12-20 22:06:27 +08:00
# Check that all the objects are of the right type
2007-01-30 00:09:25 +08:00
new_ids = set ( )
2006-12-20 22:06:27 +08:00
for obj in objs :
2007-01-30 00:09:25 +08:00
if isinstance ( obj , self . model ) :
new_ids . add ( obj . _get_pk_val ( ) )
else :
new_ids . add ( obj )
2006-12-20 21:57:17 +08:00
# Add the newly created or already existing objects to the join table.
# First find out which items are already added, to avoid adding them twice
cursor = connection . cursor ( )
cursor . execute ( " SELECT %s FROM %s WHERE %s = %% s AND %s IN ( %s ) " % \
( target_col_name , self . join_table , source_col_name ,
target_col_name , " , " . join ( [ ' %s ' ] * len ( new_ids ) ) ) ,
[ self . _pk_val ] + list ( new_ids ) )
2007-06-23 22:16:00 +08:00
existing_ids = set ( [ row [ 0 ] for row in cursor . fetchall ( ) ] )
2006-12-20 21:57:17 +08:00
# Add the ones that aren't there already
for obj_id in ( new_ids - existing_ids ) :
cursor . execute ( " INSERT INTO %s ( %s , %s ) VALUES ( %% s, %% s) " % \
( self . join_table , source_col_name , target_col_name ) ,
[ self . _pk_val , obj_id ] )
transaction . commit_unless_managed ( )
2006-05-02 09:31:56 +08:00
def _remove_items ( self , source_col_name , target_col_name , * objs ) :
# source_col_name: the PK colname in join_table for the source object
# target_col_name: the PK colname in join_table for the target object
# *objs - objects to remove
2006-12-20 22:06:27 +08:00
# If there aren't any objects, there is nothing to do.
if objs :
# Check that all the objects are of the right type
2007-01-30 00:09:25 +08:00
old_ids = set ( )
2006-12-20 22:06:27 +08:00
for obj in objs :
2007-01-30 00:09:25 +08:00
if isinstance ( obj , self . model ) :
old_ids . add ( obj . _get_pk_val ( ) )
else :
old_ids . add ( obj )
2006-12-20 22:06:27 +08:00
# Remove the specified objects from the join table
cursor = connection . cursor ( )
cursor . execute ( " DELETE FROM %s WHERE %s = %% s AND %s IN ( %s ) " % \
2006-12-27 13:23:21 +08:00
( self . join_table , source_col_name ,
2006-12-20 22:06:27 +08:00
target_col_name , " , " . join ( [ ' %s ' ] * len ( old_ids ) ) ) ,
[ self . _pk_val ] + list ( old_ids ) )
transaction . commit_unless_managed ( )
2006-05-02 09:31:56 +08:00
def _clear_items ( self , source_col_name ) :
# source_col_name: the PK colname in join_table for the source object
cursor = connection . cursor ( )
cursor . execute ( " DELETE FROM %s WHERE %s = %% s " % \
( self . join_table , source_col_name ) ,
[ self . _pk_val ] )
transaction . commit_unless_managed ( )
return ManyRelatedManager
class ManyRelatedObjectsDescriptor ( object ) :
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# multiple "remote" values and have a ManyToManyField pointed at them by
# some other model (rather than having a ManyToManyField themselves).
# In the example "publication.article_set", the article_set attribute is a
# ManyRelatedObjectsDescriptor instance.
def __init__ ( self , related ) :
self . related = related # RelatedObject instance
def __get__ ( self , instance , instance_type = None ) :
if instance is None :
raise AttributeError , " Manager must be accessed via instance "
# Dynamically create a class that subclasses the related
# model's default manager.
rel_model = self . related . model
superclass = rel_model . _default_manager . __class__
2008-07-29 20:41:08 +08:00
RelatedManager = create_many_related_manager ( superclass , self . related . field . rel . through )
2006-05-02 09:31:56 +08:00
2007-08-20 09:03:33 +08:00
qn = connection . ops . quote_name
2006-05-02 09:31:56 +08:00
manager = RelatedManager (
model = rel_model ,
core_filters = { ' %s __pk ' % self . related . field . name : instance . _get_pk_val ( ) } ,
instance = instance ,
symmetrical = False ,
join_table = qn ( self . related . field . m2m_db_table ( ) ) ,
source_col_name = qn ( self . related . field . m2m_reverse_name ( ) ) ,
target_col_name = qn ( self . related . field . m2m_column_name ( ) )
)
return manager
def __set__ ( self , instance , value ) :
if instance is None :
raise AttributeError , " Manager must be accessed via instance "
2008-07-29 20:41:08 +08:00
through = getattr ( self . related . field . rel , ' through ' , None )
if through is not None :
raise AttributeError , " Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s ' s Manager instead. " % through
2006-05-02 09:31:56 +08:00
manager = self . __get__ ( instance )
manager . clear ( )
2006-12-20 17:59:03 +08:00
manager . add ( * value )
2006-05-02 09:31:56 +08:00
class ReverseManyRelatedObjectsDescriptor ( object ) :
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# multiple "remote" values and have a ManyToManyField defined in their
# model (rather than having another model pointed *at* them).
# In the example "article.publications", the publications attribute is a
# ReverseManyRelatedObjectsDescriptor instance.
def __init__ ( self , m2m_field ) :
self . field = m2m_field
def __get__ ( self , instance , instance_type = None ) :
if instance is None :
raise AttributeError , " Manager must be accessed via instance "
# Dynamically create a class that subclasses the related
# model's default manager.
rel_model = self . field . rel . to
superclass = rel_model . _default_manager . __class__
2008-07-29 20:41:08 +08:00
RelatedManager = create_many_related_manager ( superclass , self . field . rel . through )
2006-05-02 09:31:56 +08:00
2007-08-20 09:03:33 +08:00
qn = connection . ops . quote_name
2006-05-02 09:31:56 +08:00
manager = RelatedManager (
model = rel_model ,
core_filters = { ' %s __pk ' % self . field . related_query_name ( ) : instance . _get_pk_val ( ) } ,
instance = instance ,
symmetrical = ( self . field . rel . symmetrical and instance . __class__ == rel_model ) ,
join_table = qn ( self . field . m2m_db_table ( ) ) ,
source_col_name = qn ( self . field . m2m_column_name ( ) ) ,
target_col_name = qn ( self . field . m2m_reverse_name ( ) )
)
return manager
def __set__ ( self , instance , value ) :
if instance is None :
raise AttributeError , " Manager must be accessed via instance "
2008-07-29 20:41:08 +08:00
through = getattr ( self . field . rel , ' through ' , None )
if through is not None :
raise AttributeError , " Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s ' s Manager instead. " % through
2006-05-02 09:31:56 +08:00
manager = self . __get__ ( instance )
manager . clear ( )
2006-12-20 17:59:03 +08:00
manager . add ( * value )
2006-05-02 09:31:56 +08:00
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
class ManyToOneRel ( object ) :
def __init__ ( self , to , field_name , num_in_admin = 3 , min_num_in_admin = None ,
max_num_in_admin = None , num_extra_on_change = 1 , edit_inline = False ,
related_name = None , limit_choices_to = None , lookup_overrides = None ,
2008-07-19 07:54:34 +08:00
parent_link = False ) :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
try :
to . _meta
except AttributeError : # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance ( to , basestring ) , " ' to ' must be either a model, a model name or the string %r " % RECURSIVE_RELATIONSHIP_CONSTANT
self . to , self . field_name = to , field_name
self . num_in_admin , self . edit_inline = num_in_admin , edit_inline
self . min_num_in_admin , self . max_num_in_admin = min_num_in_admin , max_num_in_admin
self . num_extra_on_change , self . related_name = num_extra_on_change , related_name
if limit_choices_to is None :
limit_choices_to = { }
self . limit_choices_to = limit_choices_to
self . lookup_overrides = lookup_overrides or { }
self . multiple = True
self . parent_link = parent_link
def get_related_field ( self ) :
"""
Returns the Field in the ' to ' object to which this relationship is
tied .
"""
data = self . to . _meta . get_field_by_name ( self . field_name )
if not data [ 2 ] :
raise FieldDoesNotExist ( " No related field named ' %s ' " %
self . field_name )
return data [ 0 ]
class OneToOneRel ( ManyToOneRel ) :
def __init__ ( self , to , field_name , num_in_admin = 0 , min_num_in_admin = None ,
max_num_in_admin = None , num_extra_on_change = None , edit_inline = False ,
related_name = None , limit_choices_to = None , lookup_overrides = None ,
2008-07-19 07:54:34 +08:00
parent_link = False ) :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# NOTE: *_num_in_admin and num_extra_on_change are intentionally
# ignored here. We accept them as parameters only to match the calling
# signature of ManyToOneRel.__init__().
super ( OneToOneRel , self ) . __init__ ( to , field_name , num_in_admin ,
edit_inline = edit_inline , related_name = related_name ,
limit_choices_to = limit_choices_to ,
2008-07-19 07:54:34 +08:00
lookup_overrides = lookup_overrides , parent_link = parent_link )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
self . multiple = False
class ManyToManyRel ( object ) :
def __init__ ( self , to , num_in_admin = 0 , related_name = None ,
2008-07-29 20:41:08 +08:00
limit_choices_to = None , symmetrical = True , through = None ) :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
self . to = to
self . num_in_admin = num_in_admin
self . related_name = related_name
if limit_choices_to is None :
limit_choices_to = { }
self . limit_choices_to = limit_choices_to
self . edit_inline = False
self . symmetrical = symmetrical
self . multiple = True
2008-07-29 20:41:08 +08:00
self . through = through
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2006-05-02 09:31:56 +08:00
class ForeignKey ( RelatedField , Field ) :
empty_strings_allowed = False
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
def __init__ ( self , to , to_field = None , rel_class = ManyToOneRel , * * kwargs ) :
2006-05-02 09:31:56 +08:00
try :
to_name = to . _meta . object_name . lower ( )
except AttributeError : # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
assert isinstance ( to , basestring ) , " %s ( %r ) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r " % ( self . __class__ . __name__ , to , RECURSIVE_RELATIONSHIP_CONSTANT )
2006-05-02 09:31:56 +08:00
else :
to_field = to_field or to . _meta . pk . name
2008-07-30 02:31:27 +08:00
kwargs [ ' verbose_name ' ] = kwargs . get ( ' verbose_name ' , None )
2006-05-02 09:31:56 +08:00
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
kwargs [ ' rel ' ] = rel_class ( to , to_field ,
2006-05-02 09:31:56 +08:00
num_in_admin = kwargs . pop ( ' num_in_admin ' , 3 ) ,
min_num_in_admin = kwargs . pop ( ' min_num_in_admin ' , None ) ,
max_num_in_admin = kwargs . pop ( ' max_num_in_admin ' , None ) ,
num_extra_on_change = kwargs . pop ( ' num_extra_on_change ' , 1 ) ,
edit_inline = kwargs . pop ( ' edit_inline ' , False ) ,
related_name = kwargs . pop ( ' related_name ' , None ) ,
limit_choices_to = kwargs . pop ( ' limit_choices_to ' , None ) ,
lookup_overrides = kwargs . pop ( ' lookup_overrides ' , None ) ,
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
parent_link = kwargs . pop ( ' parent_link ' , False ) )
2006-05-02 09:31:56 +08:00
Field . __init__ ( self , * * kwargs )
self . db_index = True
def get_attname ( self ) :
return ' %s _id ' % self . name
def get_validator_unique_lookup_type ( self ) :
return ' %s __ %s __exact ' % ( self . name , self . rel . get_related_field ( ) . name )
def prepare_field_objs_and_params ( self , manipulator , name_prefix ) :
params = { ' validator_list ' : self . validator_list [ : ] , ' member_name ' : name_prefix + self . attname }
2008-07-19 07:54:34 +08:00
if self . null :
field_objs = [ oldforms . NullSelectField ]
2006-05-02 09:31:56 +08:00
else :
2008-07-19 07:54:34 +08:00
field_objs = [ oldforms . SelectField ]
params [ ' choices ' ] = self . get_choices_default ( )
2006-05-02 09:31:56 +08:00
return field_objs , params
2008-03-20 14:56:23 +08:00
def get_default ( self ) :
" Here we check if the default value is an object and return the to_field if so. "
field_default = super ( ForeignKey , self ) . get_default ( )
if isinstance ( field_default , self . rel . to ) :
return getattr ( field_default , self . rel . get_related_field ( ) . attname )
return field_default
2006-05-02 09:31:56 +08:00
def get_manipulator_field_objs ( self ) :
rel_field = self . rel . get_related_field ( )
2008-07-19 07:54:34 +08:00
return [ oldforms . IntegerField ]
2006-05-02 09:31:56 +08:00
def get_db_prep_save ( self , value ) :
if value == ' ' or value == None :
return None
else :
return self . rel . get_related_field ( ) . get_db_prep_save ( value )
def flatten_data ( self , follow , obj = None ) :
if not obj :
# In required many-to-one fields with only one available choice,
# select that one available choice. Note: For SelectFields
2008-07-19 07:54:34 +08:00
# we have to check that the length of choices is *2*, not 1,
# because SelectFields always have an initial "blank" value.
if not self . blank and self . choices :
2006-05-02 09:31:56 +08:00
choice_list = self . get_choices_default ( )
2008-07-19 07:54:34 +08:00
if len ( choice_list ) == 2 :
2006-05-02 09:31:56 +08:00
return { self . attname : choice_list [ 1 ] [ 0 ] }
return Field . flatten_data ( self , follow , obj )
def contribute_to_class ( self , cls , name ) :
super ( ForeignKey , self ) . contribute_to_class ( cls , name )
setattr ( cls , self . name , ReverseSingleRelatedObjectDescriptor ( self ) )
2008-06-29 10:36:18 +08:00
if isinstance ( self . rel . to , basestring ) :
target = self . rel . to
else :
target = self . rel . to . _meta . db_table
cls . _meta . duplicate_targets [ self . column ] = ( target , " o2m " )
2006-05-02 09:31:56 +08:00
def contribute_to_related_class ( self , cls , related ) :
setattr ( cls , related . get_accessor_name ( ) , ForeignRelatedObjectsDescriptor ( related ) )
2007-01-22 14:32:14 +08:00
def formfield ( self , * * kwargs ) :
2008-07-19 07:54:34 +08:00
defaults = { ' form_class ' : forms . ModelChoiceField , ' queryset ' : self . rel . to . _default_manager . complex_filter ( self . rel . limit_choices_to ) }
2007-01-22 14:32:14 +08:00
defaults . update ( kwargs )
2007-04-28 21:55:24 +08:00
return super ( ForeignKey , self ) . formfield ( * * defaults )
2006-12-27 13:23:21 +08:00
2007-07-20 14:28:56 +08:00
def db_type ( self ) :
# The database column type of a ForeignKey is the column type
# of the field to which it points. An exception is if the ForeignKey
# points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField,
# in which case the column type is simply that of an IntegerField.
rel_field = self . rel . get_related_field ( )
if isinstance ( rel_field , ( AutoField , PositiveIntegerField , PositiveSmallIntegerField ) ) :
return IntegerField ( ) . db_type ( )
return rel_field . db_type ( )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
class OneToOneField ( ForeignKey ) :
"""
A OneToOneField is essentially the same as a ForeignKey , with the exception
that always carries a " unique " constraint with it and the reverse relation
always returns the object pointed to ( since there will only ever be one ) ,
rather than returning a list .
"""
2006-05-02 09:31:56 +08:00
def __init__ ( self , to , to_field = None , * * kwargs ) :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
kwargs [ ' unique ' ] = True
2008-07-22 09:10:06 +08:00
kwargs [ ' editable ' ] = False
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
if ' num_in_admin ' not in kwargs :
kwargs [ ' num_in_admin ' ] = 0
super ( OneToOneField , self ) . __init__ ( to , to_field , OneToOneRel , * * kwargs )
2006-05-02 09:31:56 +08:00
def contribute_to_related_class ( self , cls , related ) :
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
setattr ( cls , related . get_accessor_name ( ) ,
SingleRelatedObjectDescriptor ( related ) )
2006-05-02 09:31:56 +08:00
if not cls . _meta . one_to_one_field :
cls . _meta . one_to_one_field = self
class ManyToManyField ( RelatedField , Field ) :
def __init__ ( self , to , * * kwargs ) :
kwargs [ ' verbose_name ' ] = kwargs . get ( ' verbose_name ' , None )
2006-05-04 12:59:22 +08:00
kwargs [ ' rel ' ] = ManyToManyRel ( to ,
2006-05-02 09:31:56 +08:00
num_in_admin = kwargs . pop ( ' num_in_admin ' , 0 ) ,
related_name = kwargs . pop ( ' related_name ' , None ) ,
limit_choices_to = kwargs . pop ( ' limit_choices_to ' , None ) ,
2008-07-29 20:41:08 +08:00
symmetrical = kwargs . pop ( ' symmetrical ' , True ) ,
through = kwargs . pop ( ' through ' , None ) )
2007-01-25 21:47:55 +08:00
self . db_table = kwargs . pop ( ' db_table ' , None )
2008-07-29 20:41:08 +08:00
if kwargs [ ' rel ' ] . through is not None :
self . creates_table = False
assert self . db_table is None , " Cannot specify a db_table if an intermediary model is used. "
else :
self . creates_table = True
2006-05-02 09:31:56 +08:00
Field . __init__ ( self , * * kwargs )
2008-07-19 07:54:34 +08:00
msg = ugettext_lazy ( ' Hold down " Control " , or " Command " on a Mac, to select more than one. ' )
2006-09-26 11:46:33 +08:00
self . help_text = string_concat ( self . help_text , ' ' , msg )
2006-05-02 09:31:56 +08:00
def get_manipulator_field_objs ( self ) :
2008-07-19 07:54:34 +08:00
choices = self . get_choices_default ( )
return [ curry ( oldforms . SelectMultipleField , size = min ( max ( len ( choices ) , 5 ) , 15 ) , choices = choices ) ]
2006-05-02 09:31:56 +08:00
def get_choices_default ( self ) :
return Field . get_choices ( self , include_blank = False )
def _get_m2m_db_table ( self , opts ) :
" Function that can be curried to provide the m2m table name for this relation "
2008-07-29 20:41:08 +08:00
if self . rel . through is not None :
return self . rel . through_model . _meta . db_table
elif self . db_table :
2007-01-25 21:47:55 +08:00
return self . db_table
else :
return ' %s _ %s ' % ( opts . db_table , self . name )
2006-05-02 09:31:56 +08:00
def _get_m2m_column_name ( self , related ) :
" Function that can be curried to provide the source column name for the m2m table "
2008-07-29 20:41:08 +08:00
try :
return self . _m2m_column_name_cache
except :
if self . rel . through is not None :
for f in self . rel . through_model . _meta . fields :
if hasattr ( f , ' rel ' ) and f . rel and f . rel . to == related . model :
self . _m2m_column_name_cache = f . column
break
# If this is an m2m relation to self, avoid the inevitable name clash
elif related . model == related . parent_model :
self . _m2m_column_name_cache = ' from_ ' + related . model . _meta . object_name . lower ( ) + ' _id '
else :
self . _m2m_column_name_cache = related . model . _meta . object_name . lower ( ) + ' _id '
# Return the newly cached value
return self . _m2m_column_name_cache
2006-05-02 09:31:56 +08:00
def _get_m2m_reverse_name ( self , related ) :
" Function that can be curried to provide the related column name for the m2m table "
2008-07-29 20:41:08 +08:00
try :
return self . _m2m_reverse_name_cache
except :
if self . rel . through is not None :
found = False
for f in self . rel . through_model . _meta . fields :
if hasattr ( f , ' rel ' ) and f . rel and f . rel . to == related . parent_model :
if related . model == related . parent_model :
# If this is an m2m-intermediate to self,
# the first foreign key you find will be
# the source column. Keep searching for
# the second foreign key.
if found :
self . _m2m_reverse_name_cache = f . column
break
else :
found = True
else :
self . _m2m_reverse_name_cache = f . column
break
# If this is an m2m relation to self, avoid the inevitable name clash
elif related . model == related . parent_model :
self . _m2m_reverse_name_cache = ' to_ ' + related . parent_model . _meta . object_name . lower ( ) + ' _id '
else :
self . _m2m_reverse_name_cache = related . parent_model . _meta . object_name . lower ( ) + ' _id '
# Return the newly cached value
return self . _m2m_reverse_name_cache
2006-05-02 09:31:56 +08:00
def isValidIDList ( self , field_data , all_data ) :
" Validates that the value is a valid list of foreign keys "
mod = self . rel . to
try :
pks = map ( int , field_data . split ( ' , ' ) )
except ValueError :
# the CommaSeparatedIntegerField validator will catch this error
return
objects = mod . _default_manager . in_bulk ( pks )
if len ( objects ) != len ( pks ) :
badkeys = [ k for k in pks if k not in objects ]
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
raise validators . ValidationError , ungettext ( " Please enter valid %(self)s IDs. The value %(value)r is invalid. " ,
2006-05-02 09:31:56 +08:00
" Please enter valid %(self)s IDs. The values %(value)r are invalid. " , len ( badkeys ) ) % {
' self ' : self . verbose_name ,
' value ' : len ( badkeys ) == 1 and badkeys [ 0 ] or tuple ( badkeys ) ,
}
def flatten_data ( self , follow , obj = None ) :
new_data = { }
if obj :
instance_ids = [ instance . _get_pk_val ( ) for instance in getattr ( obj , self . name ) . all ( ) ]
2008-07-19 07:54:34 +08:00
new_data [ self . name ] = instance_ids
2006-05-02 09:31:56 +08:00
else :
# In required many-to-many fields with only one available choice,
# select that one available choice.
2008-07-19 07:54:34 +08:00
if not self . blank and not self . rel . edit_inline :
2006-05-02 09:31:56 +08:00
choices_list = self . get_choices_default ( )
if len ( choices_list ) == 1 :
new_data [ self . name ] = [ choices_list [ 0 ] [ 0 ] ]
return new_data
def contribute_to_class ( self , cls , name ) :
2008-07-29 20:41:08 +08:00
super ( ManyToManyField , self ) . contribute_to_class ( cls , name )
2006-05-02 09:31:56 +08:00
# Add the descriptor for the m2m relation
setattr ( cls , self . name , ReverseManyRelatedObjectsDescriptor ( self ) )
# Set up the accessor for the m2m table name for the relation
self . m2m_db_table = curry ( self . _get_m2m_db_table , cls . _meta )
2008-07-29 20:41:08 +08:00
# Populate some necessary rel arguments so that cross-app relations
# work correctly.
if isinstance ( self . rel . through , basestring ) :
def resolve_through_model ( field , model , cls ) :
field . rel . through_model = model
add_lazy_relation ( cls , self , self . rel . through , resolve_through_model )
elif self . rel . through :
self . rel . through_model = self . rel . through
self . rel . through = self . rel . through . _meta . object_name
2008-06-29 10:36:18 +08:00
if isinstance ( self . rel . to , basestring ) :
target = self . rel . to
else :
target = self . rel . to . _meta . db_table
cls . _meta . duplicate_targets [ self . column ] = ( target , " m2m " )
2006-05-02 09:31:56 +08:00
def contribute_to_related_class ( self , cls , related ) :
# m2m relations to self do not have a ManyRelatedObjectsDescriptor,
# as it would be redundant - unless the field is non-symmetrical.
if related . model != related . parent_model or not self . rel . symmetrical :
# Add the descriptor for the m2m relation
setattr ( cls , related . get_accessor_name ( ) , ManyRelatedObjectsDescriptor ( related ) )
# Set up the accessors for the column names on the m2m table
self . m2m_column_name = curry ( self . _get_m2m_column_name , related )
self . m2m_reverse_name = curry ( self . _get_m2m_reverse_name , related )
def set_attributes_from_rel ( self ) :
pass
2006-12-28 10:27:14 +08:00
def value_from_object ( self , obj ) :
" Returns the value of this field in the given model instance. "
return getattr ( obj , self . attname ) . all ( )
2007-08-06 21:58:56 +08:00
def save_form_data ( self , instance , data ) :
setattr ( instance , self . attname , data )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
2007-01-22 14:32:14 +08:00
def formfield ( self , * * kwargs ) :
2008-07-19 07:54:34 +08:00
defaults = { ' form_class ' : forms . ModelMultipleChoiceField , ' queryset ' : self . rel . to . _default_manager . complex_filter ( self . rel . limit_choices_to ) }
2007-04-28 21:55:24 +08:00
defaults . update ( kwargs )
2006-12-30 08:12:02 +08:00
# If initial is passed in, it's a list of related objects, but the
# MultipleChoiceField takes a list of IDs.
2007-04-28 21:55:24 +08:00
if defaults . get ( ' initial ' ) is not None :
defaults [ ' initial ' ] = [ i . _get_pk_val ( ) for i in defaults [ ' initial ' ] ]
return super ( ManyToManyField , self ) . formfield ( * * defaults )
2006-12-27 13:15:22 +08:00
2007-07-20 14:28:56 +08:00
def db_type ( self ) :
# A ManyToManyField is not represented by a single column,
# so return None.
return None