Code

Ticket #3688: recursive-related-fields-4692.diff

File recursive-related-fields-4692.diff, 6.3 KB (added by Ben Slavin <benjamin.slavin@…>, 7 years ago)

Patch described above.

Line 
1Index: django/db/models/fields/related.py
2===================================================================
3--- django/db/models/fields/related.py  (revision 4692)
4+++ django/db/models/fields/related.py  (working copy)
5@@ -6,6 +6,7 @@
6 from django.utils.translation import gettext_lazy, string_concat, ngettext
7 from django.utils.functional import curry
8 from django.core import validators
9+from django.core.exceptions import ImproperlyConfigured
10 from django import oldforms
11 from django import newforms as forms
12 from django.dispatch import dispatcher
13@@ -23,11 +24,21 @@
14 
15 def add_lookup(rel_cls, field):
16     name = field.rel.to
17-    module = rel_cls.__module__
18-    key = (module, name)
19+    # name should be either the name of a model in the same application or the
20+    # name of a model in another application referenced with standard Django
21+    # dotted notation ('appname.modelname')
22+    if name.find('.') is -1:
23+        app_name = rel_cls._meta.app_label
24+        model_name = name
25+    else:
26+        try:
27+            app_name, model_name = name.split('.')
28+        except ValueError:
29+            raise ImproperlyConfigured, "Invalid definition for field %s (%s). Please use app_name.model_name notation." % (field.name, name)
30+    key = (app_name, model_name)
31     # Has the model already been loaded?
32     # If so, resolve the string reference right away
33-    model = get_model(rel_cls._meta.app_label, field.rel.to, False)
34+    model = get_model(app_name, model_name, False)
35     if model:
36         field.rel.to = model
37         field.do_related_class(model, rel_cls)
38@@ -37,7 +48,7 @@
39 
40 def do_pending_lookups(sender):
41     other_cls = sender
42-    key = (other_cls.__module__, other_cls.__name__)
43+    key = (other_cls._meta.app_label, other_cls.__name__)
44     for rel_cls, field in pending_lookups.setdefault(key, []):
45         field.rel.to = other_cls
46         field.do_related_class(other_cls, rel_cls)
47Index: docs/model-api.txt
48===================================================================
49--- docs/model-api.txt  (revision 4692)
50+++ docs/model-api.txt  (working copy)
51@@ -682,23 +682,8 @@
52         manufacturer = models.ForeignKey(Manufacturer)
53         # ...
54 
55-To create a recursive relationship -- an object that has a many-to-one
56-relationship with itself -- use ``models.ForeignKey('self')``.
57+For information on `recursive relationships`_, see below.
58 
59-If you need to create a relationship on a model that has not yet been defined,
60-you can use the name of the model, rather than the model object itself::
61-
62-    class Car(models.Model):
63-        manufacturer = models.ForeignKey('Manufacturer')
64-        # ...
65-
66-    class Manufacturer(models.Model):
67-        # ...
68-
69-Note, however, that you can only use strings to refer to models in the same
70-models.py file -- you cannot use a string to reference a model in a different
71-application, or to reference a model that has been imported from elsewhere.
72-
73 Behind the scenes, Django appends ``"_id"`` to the field name to create its
74 database column name. In the above example, the database table for the ``Car``
75 model will have a ``manufacturer_id`` column. (You can change this explicitly
76@@ -817,12 +802,7 @@
77         # ...
78         toppings = models.ManyToManyField(Topping)
79 
80-As with ``ForeignKey``, a relationship to self can be defined by using the
81-string ``'self'`` instead of the model name, and you can refer to as-yet
82-undefined models by using a string containing the model name. However, you
83-can only use strings to refer to models in the same models.py file -- you
84-cannot use a string to reference a model in a different application, or to
85-reference a model that has been imported from elsewhere.
86+For information on `recursive relationships`_, see below.
87 
88 It's suggested, but not required, that the name of a ``ManyToManyField``
89 (``toppings`` in the example above) be a plural describing the set of related
90@@ -910,9 +890,7 @@
91 could make ``Restaurant`` have a ``OneToOneField`` to ``Place`` (because a
92 restaurant "is-a" place).
93 
94-As with ``ForeignKey``, a relationship to self can be defined by using the
95-string ``"self"`` instead of the model name; references to as-yet undefined
96-models can be made by using a string containing the model name.
97+For information on `recursive relationships`_, see below.
98 
99 This ``OneToOneField`` will actually replace the primary key ``id`` field
100 (since one-to-one relations share the same primary key), and will be displayed
101@@ -922,6 +900,57 @@
102 
103 .. _One-to-one relationship model example: http://www.djangoproject.com/documentation/models/one_to_one/
104 
105+Recursive relationships
106+~~~~~~~~~~~~~~~~~~~~~~~
107+To create a recursive relationship -- an object that has a many-to-one
108+relationship with itself -- use ``models.ForeignKey('self')``.
109+
110+If you need to create a relationship on a model that has not yet been defined,
111+you can use the name of the model, rather than the model object itself::
112+
113+    class Car(models.Model):
114+        manufacturer = models.ForeignKey('Manufacturer')
115+        # ...
116+
117+    class Manufacturer(models.Model):
118+        # ...
119+
120+Note, however, that you can only use strings to refer to models in the same
121+models.py file -- you cannot use a string to reference a model in a different
122+application, or to reference a model that has been imported from elsewhere.
123+
124+**New in Django development version:** String-based references have been
125+expanded to allow reference to models in other models.py files.  This usage
126+provides a mechanism for enabling recursive relationships between
127+tightly-coupled objects that logically reside in different applications.
128+
129+For example, consider a user class and a series of articles:
130+
131+       class User(models.Model):
132+               saved_articles = models.ManyToManyField('Article')
133+               # ...
134+
135+       class Article(models.Model):
136+               author = models.ForeignKey(User)
137+               # ...
138+
139+It doesn't make much sense for users and articles to be stored in the same
140+models.py file.  By using dotted-notation (``app_name.model_name``), it is
141+possible to reference across models.py files when recursive ``import``
142+statements would otherwise cause problems.
143+
144+my_project/accounts/models.py:
145+
146+       class User(models.Model):
147+               saved_articles = models.ManyToManyField('articles.Article')
148+               # ...
149+
150+my_project/articles/models.py:
151+
152+       class Article(models.Model):
153+               author = models.ForeignKey('accounts.User')
154+               # ...
155+
156 Meta options
157 ============
158