mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Refactored metaclass in preparation for descriptor fields
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1701 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -17,7 +17,6 @@ from django.db.models.fields.related import * | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.db.models.exceptions import FieldDoesNotExist, BadKeywordArguments | ||||
|  | ||||
|  | ||||
| # Admin stages. | ||||
| ADD, CHANGE, BOTH = 1, 2, 3 | ||||
|  | ||||
| @@ -28,7 +27,6 @@ ADD, CHANGE, BOTH = 1, 2, 3 | ||||
| #def get_app(app_label): | ||||
| #    return __import__('%s.%s' % (MODEL_PREFIX, app_label), '', '', ['']) | ||||
|  | ||||
|  | ||||
| class LazyDate: | ||||
|     """ | ||||
|     Use in limit_choices_to to compare the field to dates calculated at run time | ||||
| @@ -50,11 +48,6 @@ class LazyDate: | ||||
|     def __get_value__(self): | ||||
|         return datetime.datetime.now() + self.delta | ||||
|  | ||||
| ################ | ||||
| # MAIN CLASSES # | ||||
| ################ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| from django.db.models.manipulators import ManipulatorDescriptor, ModelAddManipulator, ModelChangeManipulator | ||||
| from django.db.models.fields import Field, DateField, FileField, ImageField, AutoField | ||||
| from django.db.models.fields.related import OneToOne, ManyToOne, ManyToMany, RECURSIVE_RELATIONSHIP_CONSTANT | ||||
| from django.db.models.fields.related import RelatedField, OneToOne, ManyToOne, ManyToMany, RECURSIVE_RELATIONSHIP_CONSTANT | ||||
| from django.db.models.related import RelatedObject | ||||
| from django.db.models.manager import Manager, ManagerDescriptor | ||||
| from django.db.models.query import orderlist2sql  | ||||
| @@ -36,22 +36,8 @@ class ModelBase(type): | ||||
|         except KeyError: | ||||
|             meta_attrs = {} | ||||
|  | ||||
|         # Gather all attributes that are Field or Manager instances. | ||||
|         fields, managers = [], [] | ||||
|         for obj_name, obj in attrs.items(): | ||||
|             if isinstance(obj, Field): | ||||
|                 obj.set_name(obj_name) | ||||
|                 fields.append(obj) | ||||
|                 del attrs[obj_name] | ||||
|             elif isinstance(obj, Manager): | ||||
|                 managers.append((obj_name, obj)) | ||||
|                 del attrs[obj_name] | ||||
|  | ||||
|         # Sort the fields and managers in the order that they were created. The | ||||
|         # "creation_counter" is needed because metaclasses don't preserve the | ||||
|         # attribute order. | ||||
|         fields.sort(lambda x, y: x.creation_counter - y.creation_counter) | ||||
|         managers.sort(lambda x, y: x[1].creation_counter - y[1].creation_counter) | ||||
|         # Create the class, because we need it to use in currying. | ||||
|         new_class = type.__new__(cls, name, bases, { '__module__' : attrs.pop('__module__') }) | ||||
|          | ||||
|         opts = Options( | ||||
|             module_name = meta_attrs.pop('module_name', get_module_name(name)), | ||||
| @@ -60,7 +46,6 @@ class ModelBase(type): | ||||
|             verbose_name = meta_attrs.pop('verbose_name', get_verbose_name(name)), | ||||
|             verbose_name_plural = meta_attrs.pop('verbose_name_plural', ''), | ||||
|             db_table = meta_attrs.pop('db_table', ''), | ||||
|             fields = fields, | ||||
|             ordering = meta_attrs.pop('ordering', None), | ||||
|             unique_together = meta_attrs.pop('unique_together', None), | ||||
|             admin = meta_attrs.pop('admin', None), | ||||
| @@ -76,12 +61,31 @@ class ModelBase(type): | ||||
|  | ||||
|         if meta_attrs != {}: | ||||
|             raise TypeError, "'class META' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()) | ||||
|         new_class.add_to_class('_meta', opts) | ||||
|          | ||||
|         # Create the DoesNotExist exception. | ||||
|         attrs['DoesNotExist'] = types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}) | ||||
|         new_class.DoesNotExist = types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}) | ||||
|          | ||||
|         # Figure out the app_label by looking one level up. | ||||
|         #FIXME: wrong for nested model modules | ||||
|         app_package = sys.modules.get(new_class.__module__) | ||||
|         app_label = app_package.__name__.replace('.models', '') | ||||
|         app_label = app_label[app_label.rfind('.')+1:] | ||||
|  | ||||
|         # Cache the app label. | ||||
|         opts.app_label = app_label | ||||
|          | ||||
|         #Add all attributes to the class | ||||
|         #fields, managers = [], [] | ||||
|         for obj_name, obj in attrs.items(): | ||||
|             new_class.add_to_class(obj_name, obj) | ||||
|          | ||||
|         if not hasattr(new_class, '_default_manager'): | ||||
|             # Create the default manager, if needed. | ||||
|             if hasattr(new_class, 'objects'): | ||||
|                 raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name | ||||
|             new_class.add_to_class('objects',  Manager()) | ||||
|  | ||||
|         # Create the class, because we need it to use in currying. | ||||
|         new_class = type.__new__(cls, name, bases, attrs) | ||||
|          | ||||
|         # Give the class a docstring -- its definition. | ||||
|         if new_class.__doc__ is None: | ||||
| @@ -90,41 +94,15 @@ class ModelBase(type): | ||||
|         if hasattr(new_class, 'get_absolute_url'): | ||||
|             new_class.get_absolute_url = curry(get_absolute_url, opts, new_class.get_absolute_url) | ||||
|              | ||||
|         # Figure out the app_label by looking one level up. | ||||
|         app_package = sys.modules.get(new_class.__module__) | ||||
|         app_label = app_package.__name__.replace('.models', '') | ||||
|         app_label = app_label[app_label.rfind('.')+1:] | ||||
|  | ||||
|         # Populate the _MODELS member on the module the class is in. | ||||
|         app_package.__dict__.setdefault('_MODELS', []).append(new_class) | ||||
|  | ||||
|         # Cache the app label. | ||||
|         opts.app_label = app_label | ||||
|         opts._prepare() | ||||
|         new_class._prepare() | ||||
|          | ||||
|         # If the db_table wasn't provided, use the app_label + module_name. | ||||
|         if not opts.db_table: | ||||
|             opts.db_table = "%s_%s" % (app_label, opts.module_name) | ||||
|         new_class._meta = opts | ||||
|          | ||||
|         for m_name, m in managers: | ||||
|             new_class.add_to_class(m_name, m) | ||||
|          | ||||
|         if not hasattr(new_class, '_default_manager'): | ||||
|             # Create the default manager, if needed. | ||||
|             if hasattr(new_class, 'objects'): | ||||
|                 raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name | ||||
|             new_class.add_to_class('objects',  Manager()) | ||||
|  | ||||
|         new_class._prepare() | ||||
|  | ||||
|         for field in fields: | ||||
|             if field.rel: | ||||
|                 other = field.rel.to | ||||
|                 if isinstance(other, basestring): | ||||
|                     print "string lookup" | ||||
|                 else: | ||||
|                     related = RelatedObject(other._meta, new_class, field) | ||||
|                     field.contribute_to_related_class(other, related) | ||||
|         # Populate the _MODELS member on the module the class is in. | ||||
|         app_package.__dict__.setdefault('_MODELS', []).append(new_class) | ||||
|          | ||||
|      | ||||
|         return new_class | ||||
| @@ -207,13 +185,6 @@ class Model(object): | ||||
|                     if not f.height_field: | ||||
|                         setattr(cls, 'get_%s_height' % f.name, curry(cls.__get_FIELD_height, field=f)) | ||||
|  | ||||
|             # If the object has a relationship to itself, as designated by | ||||
|             # RECURSIVE_RELATIONSHIP_CONSTANT, create that relationship formally. | ||||
|             if f.rel and f.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT: | ||||
|                 f.rel.to = cls | ||||
|                 f.name = f.name or (f.rel.to._meta.object_name.lower() + '_' + f.rel.to._meta.pk.name) | ||||
|                 f.verbose_name = f.verbose_name or f.rel.to._meta.verbose_name | ||||
|                 f.rel.field_name = f.rel.field_name or f.rel.to._meta.pk.name | ||||
|  | ||||
|             # Add methods for many-to-one related objects. | ||||
|             # EXAMPLES: Choice.get_poll(), Story.get_dateline() | ||||
| @@ -234,6 +205,8 @@ class Model(object): | ||||
|             cls.get_next_in_order = curry(cls.__get_next_or_previous_in_order, is_next=True) | ||||
|             cls.get_previous_in_order = curry(cls.__get_next_or_previous_in_order, is_next=False) | ||||
|          | ||||
|         RelatedField.do_pending_lookups(cls) | ||||
|  | ||||
|     _prepare = classmethod(_prepare) | ||||
|  | ||||
|     def save(self): | ||||
|   | ||||
| @@ -96,7 +96,8 @@ class Field(object): | ||||
|         unique_for_year=None, validator_list=None, choices=None, radio_admin=None, | ||||
|         help_text='', db_column=None): | ||||
|         self.name = name | ||||
|         self.verbose_name = verbose_name or (name and name.replace('_', ' ')) | ||||
|         self.verbose_name = verbose_name  | ||||
|          | ||||
|         self.primary_key = primary_key | ||||
|         self.maxlength, self.unique = maxlength, unique | ||||
|         self.blank, self.null = blank, null | ||||
| @@ -112,14 +113,21 @@ class Field(object): | ||||
|         self.db_column = db_column | ||||
|          | ||||
|         # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. | ||||
|          | ||||
|         self.db_index = db_index | ||||
|  | ||||
|         # Increase the creation counter, and save our local copy. | ||||
|         self.creation_counter = Field.creation_counter | ||||
|         Field.creation_counter += 1 | ||||
|  | ||||
|  | ||||
|     def __cmp__(self,other ): | ||||
|         #This is because bisect does not take a comparison function. grrr.  | ||||
|         return cmp(self.creation_counter, other.creation_counter) | ||||
|  | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         self.name = name | ||||
|         self.attname, self.column = self.get_attname_column() | ||||
|         self.verbose_name = self.verbose_name or (name and name.replace('_', ' ')) | ||||
|         cls._meta.add_field(self) | ||||
|  | ||||
|     def set_name(self, name): | ||||
|         self.name = name | ||||
|   | ||||
| @@ -1,21 +1,63 @@ | ||||
| from django.db.models.fields import Field, IntegerField | ||||
| from django.db.models.related import RelatedObject | ||||
| from django.utils.translation import gettext_lazy, string_concat | ||||
| from django.utils.functional import curry | ||||
| from django.core import formfields | ||||
|  | ||||
|  | ||||
| # Values for Relation.edit_inline. | ||||
| TABULAR, STACKED = 1, 2 | ||||
|  | ||||
| RECURSIVE_RELATIONSHIP_CONSTANT = 'self' | ||||
|  | ||||
| #HACK  | ||||
| class SharedMethods(object): | ||||
| class RelatedField(object): | ||||
|     pending_lookups = {} | ||||
|      | ||||
|     def add_lookup(cls, rel_cls, field): | ||||
|         name = field.rel.to | ||||
|         module = rel_cls.__module__ | ||||
|         key = (module, name) | ||||
|         cls.pending_lookups.setdefault(key,[]).append( (rel_cls, field) ) | ||||
|     add_lookup = classmethod(add_lookup) | ||||
|          | ||||
|     def do_pending_lookups(cls, other_cls): | ||||
|         key = (other_cls.__module__, other_cls.__name__) | ||||
|         for (rel_cls,field) in cls.pending_lookups.setdefault(key,[]): | ||||
|             field.rel.to = other_cls | ||||
|             field.do_related_class(other_cls, rel_cls) | ||||
|     do_pending_lookups = classmethod(do_pending_lookups) | ||||
|      | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         Field.contribute_to_class(self,cls,name) | ||||
|         other = self.rel.to | ||||
|         if isinstance(other, basestring): | ||||
|             if other == RECURSIVE_RELATIONSHIP_CONSTANT: | ||||
|                 self.rel.to = cls.__name__ | ||||
|                 self.add_lookup(cls, self) | ||||
|         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) | ||||
|         self.verbose_name = self.verbose_name or self.rel.to._meta.verbose_name | ||||
|         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._meta, cls, self) | ||||
|         self.contribute_to_related_class(other, related) | ||||
|          | ||||
|  | ||||
| #HACK | ||||
| class SharedMethods(RelatedField): | ||||
|     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) | ||||
|  | ||||
|  | ||||
| class ForeignKey(SharedMethods,Field): | ||||
|     empty_strings_allowed = False | ||||
|     def __init__(self, to, to_field=None, **kwargs): | ||||
| @@ -98,7 +140,6 @@ class ForeignKey(SharedMethods,Field): | ||||
|  | ||||
|     def contribute_to_related_class(self, cls, related): | ||||
|         rel_obj_name = related.get_method_name_part() | ||||
|  | ||||
|         # Add "get_thingie" methods for many-to-one related objects. | ||||
|         # EXAMPLE: Poll.get_choice() | ||||
|         setattr(cls, 'get_%s' % rel_obj_name, curry(cls._get_related, method_name='get_object', rel_class=related.model, rel_field=related.field)) | ||||
| @@ -147,7 +188,7 @@ class OneToOneField(SharedMethods, IntegerField): | ||||
|                       rel_class=related.model, rel_field=related.field)) | ||||
|  | ||||
|  | ||||
| class ManyToManyField(Field): | ||||
| class ManyToManyField(RelatedField,Field): | ||||
|     def __init__(self, to, **kwargs): | ||||
|         kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural) | ||||
|         kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None), | ||||
| @@ -225,6 +266,9 @@ class ManyToManyField(Field): | ||||
|             func.alters_data = True | ||||
|             setattr(cls, 'set_%s' % related.opts.module_name, func) | ||||
|  | ||||
|     def set_attributes_from_rel(self): | ||||
|         pass | ||||
|  | ||||
| class ManyToOne: | ||||
|     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, | ||||
|   | ||||
| @@ -5,19 +5,17 @@ from django.db.models.loading import get_installed_model_modules | ||||
| from django.db.models.query import orderlist2sql | ||||
| from django.db.models.exceptions import FieldDoesNotExist | ||||
|  | ||||
| from bisect import bisect | ||||
|  | ||||
| class Options: | ||||
|     def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', | ||||
|         fields=None, ordering=None, unique_together=None, admin=None, | ||||
|         ordering=None, unique_together=None, admin=None, | ||||
|         where_constraints=None, object_name=None, app_label=None, | ||||
|         exceptions=None, permissions=None, get_latest_by=None, | ||||
|         order_with_respect_to=None, module_constants=None): | ||||
|         # Move many-to-many related fields from self.fields into self.many_to_many. | ||||
|         self.fields, self.many_to_many = [], [] | ||||
|         for field in (fields or []): | ||||
|             if field.rel and isinstance(field.rel, ManyToMany): | ||||
|                 self.many_to_many.append(field) | ||||
|             else: | ||||
|                 self.fields.append(field) | ||||
|          | ||||
|         self.module_name, self.verbose_name = module_name, verbose_name | ||||
|         self.verbose_name_plural = verbose_name_plural or verbose_name + 's' | ||||
|         self.db_table = db_table | ||||
| @@ -28,13 +26,21 @@ class Options: | ||||
|         self.permissions = permissions or [] | ||||
|         self.object_name, self.app_label = object_name, app_label | ||||
|         self.get_latest_by = get_latest_by | ||||
|         if order_with_respect_to: | ||||
|         self.order_with_respect_to = order_with_respect_to | ||||
|          | ||||
|         self.module_constants = module_constants or {} | ||||
|         self.admin = admin | ||||
|      | ||||
|     def contribute_to_class(self, cls, name): | ||||
|         self.model = cls | ||||
|         cls._meta = self | ||||
|  | ||||
|     def _prepare(self): | ||||
|         if self.order_with_respect_to: | ||||
|             self.order_with_respect_to = self.get_field(order_with_respect_to) | ||||
|             self.ordering = ('_order',) | ||||
|         else: | ||||
|             self.order_with_respect_to = None | ||||
|         self.module_constants = module_constants or {} | ||||
|         self.admin = admin | ||||
|          | ||||
|         # Calculate one_to_one_field. | ||||
|         self.one_to_one_field = None | ||||
| @@ -51,7 +57,9 @@ class Options: | ||||
|         # If a primary_key field hasn't been specified, add an | ||||
|         # auto-incrementing primary-key ID field automatically. | ||||
|         if self.pk is None: | ||||
|             self.fields.insert(0, AutoField(name='id', verbose_name='ID', primary_key=True)) | ||||
|             auto = AutoField(verbose_name='ID', primary_key=True) | ||||
|             auto.creation_counter = -1 | ||||
|             self.model.add_to_class('id', auto) | ||||
|             self.pk = self.fields[0] | ||||
|         # Cache whether this has an AutoField. | ||||
|         self.has_auto_field = False | ||||
| @@ -64,6 +72,15 @@ class Options: | ||||
|         #HACK | ||||
|         self.limit_choices_to = {} | ||||
|  | ||||
|     def add_field(self, field): | ||||
|         # Insert the fields in the order that they were created. The | ||||
|         # "creation_counter" is needed because metaclasses don't preserve the | ||||
|         # attribute order. | ||||
|         if field.rel and isinstance(field.rel, ManyToMany): | ||||
|             self.many_to_many.insert(bisect(self.many_to_many, field), field) | ||||
|         else: | ||||
|             self.fields.insert(bisect(self.fields,field),field) | ||||
|              | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return '<Options for %s>' % self.module_name | ||||
|   | ||||
		Reference in New Issue
	
	Block a user