Django

Code

Changeset 7158

Show
Ignore:
Timestamp:
02/26/08 15:13:16 (4 months ago)
Author:
jacob
Message:

Beefed up support for "lazy" related objects. Now, in addition to ForeignKey?("Model") you can also say ForeignKey?("app.Model"). This means that cross-app recursive relations now work.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/fields/related.py

    r5975 r7158  
    2424pending_lookups = {} 
    2525 
    26 def add_lookup(rel_cls, field): 
    27     name = field.rel.to 
    28     module = rel_cls.__module__ 
    29     key = (module, name) 
    30     # Has the model already been loaded? 
    31     # If so, resolve the string reference right away 
    32     model = get_model(rel_cls._meta.app_label, field.rel.to, False) 
     26def add_lazy_relation(cls, field, relation): 
     27    """ 
     28    Adds a lookup on ``cls`` when a related field is defined using a string, 
     29    i.e.:: 
     30     
     31        class MyModel(Model): 
     32            fk = ForeignKey("AnotherModel") 
     33             
     34    This string can be: 
     35     
     36        * RECURSIVE_RELATIONSHIP_CONSTANT (i.e. "self") to indicate a recursive 
     37          relation. 
     38           
     39        * The name of a model (i.e "AnotherModel") to indicate another model in 
     40          the same app. 
     41           
     42        * An app-label and model name (i.e. "someapp.AnotherModel") to indicate 
     43          another model in a different app. 
     44           
     45    If the other model hasn't yet been loaded -- almost a given if you're using 
     46    lazy relationships -- then the relation won't be set up until the 
     47    class_prepared signal fires at the end of model initialization. 
     48    """ 
     49    # Check for recursive relations 
     50    if relation == RECURSIVE_RELATIONSHIP_CONSTANT: 
     51        app_label = cls._meta.app_label 
     52        model_name = cls.__name__ 
     53     
     54    else: 
     55        # Look for an "app.Model" relation 
     56        try: 
     57            app_label, model_name = relation.split(".") 
     58        except ValueError: 
     59            # If we can't split, assume a model in current app 
     60            app_label = cls._meta.app_label 
     61            model_name = relation 
     62     
     63    # Try to look up the related model, and if it's already loaded resolve the 
     64    # string right away. If get_model returns None, it means that the related 
     65    # model isn't loaded yet, so we need to pend the relation until the class  
     66    # is prepared. 
     67    model = get_model(app_label, model_name, False) 
    3368    if model: 
    3469        field.rel.to = model 
    35         field.do_related_class(model, rel_cls) 
     70        field.do_related_class(model, cls) 
    3671    else: 
    37         # Mark the related field for later lookup 
    38         pending_lookups.setdefault(key, []).append((rel_cls, field)) 
    39  
     72        key = (app_label, model_name) 
     73        value = (cls, field) 
     74        pending_lookups.setdefault(key, []).append(value) 
     75     
    4076def do_pending_lookups(sender): 
    41     other_cls = sender 
    42     key = (other_cls.__module__, other_cls.__name__) 
    43     for rel_cls, field in pending_lookups.setdefault(key, []): 
    44         field.rel.to = other_cls 
    45         field.do_related_class(other_cls, rel_cls) 
     77    """ 
     78    Handle any pending relations to the sending model. Sent from class_prepared. 
     79    """ 
     80    key = (sender._meta.app_label, sender.__name__) 
     81    for cls, field in pending_lookups.pop(key, []): 
     82        field.rel.to = sender 
     83        field.do_related_class(sender, cls) 
    4684 
    4785dispatcher.connect(do_pending_lookups, signal=signals.class_prepared) 
     
    67105        other = self.rel.to 
    68106        if isinstance(other, basestring): 
    69             if other == RECURSIVE_RELATIONSHIP_CONSTANT: 
    70                 self.rel.to = cls.__name__ 
    71             add_lookup(cls, self) 
     107            add_lazy_relation(cls, self, other) 
    72108        else: 
    73109            self.do_related_class(other, cls) 
  • django/trunk/tests/modeltests/mutually_referential/models.py

    r5876 r7158  
    2224. Mutually referential many-to-one relationships 
    33 
    4 To define a many-to-one relationship, use ``ForeignKey()``
     4Strings can be used instead of model literals to set up "lazy" relations
    55""" 
    66 
     
    99class Parent(Model): 
    1010    name = CharField(max_length=100, core=True) 
     11     
     12    # Use a simple string for forward declarations. 
    1113    bestchild = ForeignKey("Child", null=True, related_name="favoured_by") 
    1214 
    1315class Child(Model): 
    1416    name = CharField(max_length=100) 
    15     parent = ForeignKey(Parent) 
     17     
     18    # You can also explicitally specify the related app. 
     19    parent = ForeignKey("mutually_referential.Parent") 
    1620 
    1721__test__ = {'API_TESTS':"""