Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #122 -- BIG, BACKWARDS-INCOMPATIBLE CHANGE. Changed model synta…

…x to use fieldname=FieldClass() syntax. See ModelSyntaxChangeInstructions for important information on how to change your models

git-svn-id: http://code.djangoproject.com/svn/django/trunk@549 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 25264c86048d442a4885dfebae94510e2fa0c1e4 1 parent aec0a73
Adrian Holovaty authored August 25, 2005

Showing 36 changed files with 957 additions and 721 deletions. Show diff stats Hide diff stats

  1. 209  django/contrib/comments/models/comments.py
  2. 4  django/contrib/comments/templatetags/comments.py
  3. 6  django/contrib/comments/views/comments.py
  4. 8  django/contrib/comments/views/userflags.py
  5. 18  django/core/management.py
  6. 283  django/core/meta/__init__.py
  7. 98  django/core/meta/fields.py
  8. 136  django/models/auth.py
  9. 132  django/models/core.py
  10. 12  django/templatetags/log.py
  11. 20  django/views/admin/main.py
  12. 4  django/views/defaults.py
  13. 49  docs/db-api.txt
  14. 9  docs/faq.txt
  15. 16  docs/forms.txt
  16. 104  docs/model-api.txt
  17. 31  docs/overview.txt
  18. 46  docs/tutorial01.txt
  19. 12  docs/tutorial02.txt
  20. 3  tests/testapp/models/__init__.py
  21. 6  tests/testapp/models/basic.py
  22. 6  tests/testapp/models/custom_methods.py
  23. 11  tests/testapp/models/custom_pk.py
  24. 9  tests/testapp/models/get_latest.py
  25. 9  tests/testapp/models/lookup.py
  26. 30  tests/testapp/models/m2m_intermediary.py
  27. 18  tests/testapp/models/m2o_recursive.py
  28. 22  tests/testapp/models/m2o_recursive2.py
  29. 10  tests/testapp/models/many_to_many.py
  30. 54  tests/testapp/models/many_to_one.py
  31. 77  tests/testapp/models/many_to_one_null.py
  32. 27  tests/testapp/models/one_to_one.py
  33. 17  tests/testapp/models/ordering.py
  34. 8  tests/testapp/models/repr.py
  35. 6  tests/testapp/models/save_delete_hooks.py
  36. 168  tests/testapp/models/subclassing.py
209  django/contrib/comments/models/comments.py
@@ -2,59 +2,56 @@
2 2
 from django.models import auth, core
3 3
 
4 4
 class Comment(meta.Model):
5  
-    db_table = 'comments'
6  
-    fields = (
7  
-        meta.ForeignKey(auth.User, raw_id_admin=True),
8  
-        meta.ForeignKey(core.ContentType, name='content_type_id', rel_name='content_type'),
9  
-        meta.IntegerField('object_id', 'object ID'),
10  
-        meta.CharField('headline', 'headline', maxlength=255, blank=True),
11  
-        meta.TextField('comment', 'comment', maxlength=3000),
12  
-        meta.PositiveSmallIntegerField('rating1', 'rating #1', blank=True, null=True),
13  
-        meta.PositiveSmallIntegerField('rating2', 'rating #2', blank=True, null=True),
14  
-        meta.PositiveSmallIntegerField('rating3', 'rating #3', blank=True, null=True),
15  
-        meta.PositiveSmallIntegerField('rating4', 'rating #4', blank=True, null=True),
16  
-        meta.PositiveSmallIntegerField('rating5', 'rating #5', blank=True, null=True),
17  
-        meta.PositiveSmallIntegerField('rating6', 'rating #6', blank=True, null=True),
18  
-        meta.PositiveSmallIntegerField('rating7', 'rating #7', blank=True, null=True),
19  
-        meta.PositiveSmallIntegerField('rating8', 'rating #8', blank=True, null=True),
20  
-        # This field designates whether to use this row's ratings in
21  
-        # aggregate functions (summaries). We need this because people are
22  
-        # allowed to post multiple review on the same thing, but the system
23  
-        # will only use the latest one (with valid_rating=True) in tallying
24  
-        # the reviews.
25  
-        meta.BooleanField('valid_rating', 'is valid rating'),
26  
-        meta.DateTimeField('submit_date', 'date/time submitted', auto_now_add=True),
27  
-        meta.BooleanField('is_public', 'is public'),
28  
-        meta.IPAddressField('ip_address', 'IP address', blank=True, null=True),
29  
-        meta.BooleanField('is_removed', 'is removed',
30  
-            help_text='Check this box if the comment is inappropriate. A "This comment has been removed" message will be displayed instead.'),
31  
-        meta.ForeignKey(core.Site),
32  
-    )
33  
-    module_constants = {
34  
-        # min. and max. allowed dimensions for photo resizing (in pixels)
35  
-        'MIN_PHOTO_DIMENSION': 5,
36  
-        'MAX_PHOTO_DIMENSION': 1000,
37  
-
38  
-        # option codes for comment-form hidden fields
39  
-        'PHOTOS_REQUIRED': 'pr',
40  
-        'PHOTOS_OPTIONAL': 'pa',
41  
-        'RATINGS_REQUIRED': 'rr',
42  
-        'RATINGS_OPTIONAL': 'ra',
43  
-        'IS_PUBLIC': 'ip',
44  
-    }
45  
-    ordering = ('-submit_date',)
46  
-    admin = meta.Admin(
47  
-        fields = (
48  
-            (None, {'fields': ('content_type_id', 'object_id', 'site_id')}),
49  
-            ('Content', {'fields': ('user_id', 'headline', 'comment')}),
50  
-            ('Ratings', {'fields': ('rating1', 'rating2', 'rating3', 'rating4', 'rating5', 'rating6', 'rating7', 'rating8', 'valid_rating')}),
51  
-            ('Meta', {'fields': ('is_public', 'is_removed', 'ip_address')}),
52  
-        ),
53  
-        list_display = ('user_id', 'submit_date', 'content_type_id', 'get_content_object'),
54  
-        list_filter = ('submit_date',),
55  
-        date_hierarchy = 'submit_date',
56  
-        search_fields = ('comment', 'user__username'),
57  
-    )
  5
+    user = meta.ForeignKey(auth.User, raw_id_admin=True)
  6
+    content_type = meta.ForeignKey(core.ContentType)
  7
+    object_id = meta.IntegerField('object ID')
  8
+    headline = meta.CharField(maxlength=255, blank=True)
  9
+    comment = meta.TextField(maxlength=3000)
  10
+    rating1 = meta.PositiveSmallIntegerField('rating #1', blank=True, null=True)
  11
+    rating2 = meta.PositiveSmallIntegerField('rating #2', blank=True, null=True)
  12
+    rating3 = meta.PositiveSmallIntegerField('rating #3', blank=True, null=True)
  13
+    rating4 = meta.PositiveSmallIntegerField('rating #4', blank=True, null=True)
  14
+    rating5 = meta.PositiveSmallIntegerField('rating #5', blank=True, null=True)
  15
+    rating6 = meta.PositiveSmallIntegerField('rating #6', blank=True, null=True)
  16
+    rating7 = meta.PositiveSmallIntegerField('rating #7', blank=True, null=True)
  17
+    rating8 = meta.PositiveSmallIntegerField('rating #8', blank=True, null=True)
  18
+    # This field designates whether to use this row's ratings in aggregate
  19
+    # functions (summaries). We need this because people are allowed to post
  20
+    # multiple reviews on the same thing, but the system will only use the
  21
+    # latest one (with valid_rating=True) in tallying the reviews.
  22
+    valid_rating = meta.BooleanField('is valid rating')
  23
+    submit_date = meta.DateTimeField('date/time submitted', auto_now_add=True)
  24
+    is_public = meta.BooleanField()
  25
+    ip_address = meta.IPAddressField('IP address', blank=True, null=True)
  26
+    is_removed = meta.BooleanField(help_text='Check this box if the comment is inappropriate. A "This comment has been removed" message will be displayed instead.')
  27
+    site = meta.ForeignKey(core.Site)
  28
+    class META:
  29
+        db_table = 'comments'
  30
+        module_constants = {
  31
+            # min. and max. allowed dimensions for photo resizing (in pixels)
  32
+            'MIN_PHOTO_DIMENSION': 5,
  33
+            'MAX_PHOTO_DIMENSION': 1000,
  34
+
  35
+            # option codes for comment-form hidden fields
  36
+            'PHOTOS_REQUIRED': 'pr',
  37
+            'PHOTOS_OPTIONAL': 'pa',
  38
+            'RATINGS_REQUIRED': 'rr',
  39
+            'RATINGS_OPTIONAL': 'ra',
  40
+            'IS_PUBLIC': 'ip',
  41
+        }
  42
+        ordering = ('-submit_date',)
  43
+        admin = meta.Admin(
  44
+            fields = (
  45
+                (None, {'fields': ('content_type', 'object_id', 'site')}),
  46
+                ('Content', {'fields': ('user', 'headline', 'comment')}),
  47
+                ('Ratings', {'fields': ('rating1', 'rating2', 'rating3', 'rating4', 'rating5', 'rating6', 'rating7', 'rating8', 'valid_rating')}),
  48
+                ('Meta', {'fields': ('is_public', 'is_removed', 'ip_address')}),
  49
+            ),
  50
+            list_display = ('user', 'submit_date', 'content_type', 'get_content_object'),
  51
+            list_filter = ('submit_date',),
  52
+            date_hierarchy = 'submit_date',
  53
+            search_fields = ('comment', 'user__username'),
  54
+        )
58 55
 
59 56
     def __repr__(self):
60 57
         return "%s: %s..." % (self.get_user().username, self.comment[:100])
@@ -156,32 +153,31 @@ def _module_user_is_moderator(user):
156 153
         return False
157 154
 
158 155
 class FreeComment(meta.Model):
159  
-    "A FreeComment is a comment by a non-registered user"
160  
-    db_table = 'comments_free'
161  
-    fields = (
162  
-        meta.ForeignKey(core.ContentType, name='content_type_id', rel_name='content_type'),
163  
-        meta.IntegerField('object_id', 'object ID'),
164  
-        meta.TextField('comment', 'comment', maxlength=3000),
165  
-        meta.CharField('person_name', "person's name", maxlength=50),
166  
-        meta.DateTimeField('submit_date', 'date/time submitted', auto_now_add=True),
167  
-        meta.BooleanField('is_public', 'is public'),
168  
-        meta.IPAddressField('ip_address', 'IP address'),
169  
-        # TODO: Change this to is_removed, like Comment
170  
-        meta.BooleanField('approved', 'approved by staff'),
171  
-        meta.ForeignKey(core.Site),
172  
-    )
173  
-    ordering = ('-submit_date',)
174  
-    admin = meta.Admin(
175  
-        fields = (
176  
-            (None, {'fields': ('content_type_id', 'object_id', 'site_id')}),
177  
-            ('Content', {'fields': ('person_name', 'comment')}),
178  
-            ('Meta', {'fields': ('submit_date', 'is_public', 'ip_address', 'approved')}),
179  
-        ),
180  
-        list_display = ('person_name', 'submit_date', 'content_type_id', 'get_content_object'),
181  
-        list_filter = ('submit_date',),
182  
-        date_hierarchy = 'submit_date',
183  
-        search_fields = ('comment', 'person_name'),
184  
-    )
  156
+    # A FreeComment is a comment by a non-registered user.
  157
+    content_type = meta.ForeignKey(core.ContentType)
  158
+    object_id = meta.IntegerField('object ID')
  159
+    comment = meta.TextField(maxlength=3000)
  160
+    person_name = meta.CharField("person's name", maxlength=50)
  161
+    submit_date = meta.DateTimeField('date/time submitted', auto_now_add=True)
  162
+    is_public = meta.BooleanField()
  163
+    ip_address = meta.IPAddressField()
  164
+    # TODO: Change this to is_removed, like Comment
  165
+    approved = meta.BooleanField('approved by staff')
  166
+    site = meta.ForeignKey(core.Site)
  167
+    class META:
  168
+        db_table = 'comments_free'
  169
+        ordering = ('-submit_date',)
  170
+        admin = meta.Admin(
  171
+            fields = (
  172
+                (None, {'fields': ('content_type', 'object_id', 'site')}),
  173
+                ('Content', {'fields': ('person_name', 'comment')}),
  174
+                ('Meta', {'fields': ('submit_date', 'is_public', 'ip_address', 'approved')}),
  175
+            ),
  176
+            list_display = ('person_name', 'submit_date', 'content_type', 'get_content_object'),
  177
+            list_filter = ('submit_date',),
  178
+            date_hierarchy = 'submit_date',
  179
+            search_fields = ('comment', 'person_name'),
  180
+        )
185 181
 
186 182
     def __repr__(self):
187 183
         return "%s: %s..." % (self.person_name, self.comment[:100])
@@ -203,26 +199,25 @@ def get_content_object(self):
203 199
     get_content_object.short_description = 'Content object'
204 200
 
205 201
 class KarmaScore(meta.Model):
206  
-    module_name = 'karma'
207  
-    fields = (
208  
-        meta.ForeignKey(auth.User),
209  
-        meta.ForeignKey(Comment),
210  
-        meta.SmallIntegerField('score', 'score', db_index=True),
211  
-        meta.DateTimeField('scored_date', 'date scored', auto_now=True),
212  
-    )
213  
-    unique_together = (('user_id', 'comment_id'),)
214  
-    module_constants = {
215  
-        # what users get if they don't have any karma
216  
-        'DEFAULT_KARMA': 5,
217  
-        'KARMA_NEEDED_BEFORE_DISPLAYED': 3,
218  
-    }
  202
+    user = meta.ForeignKey(auth.User)
  203
+    comment = meta.ForeignKey(Comment)
  204
+    score = meta.SmallIntegerField(db_index=True)
  205
+    scored_date = meta.DateTimeField(auto_now=True)
  206
+    class META:
  207
+        module_name = 'karma'
  208
+        unique_together = (('user', 'comment'),)
  209
+        module_constants = {
  210
+            # what users get if they don't have any karma
  211
+            'DEFAULT_KARMA': 5,
  212
+            'KARMA_NEEDED_BEFORE_DISPLAYED': 3,
  213
+        }
219 214
 
220 215
     def __repr__(self):
221 216
         return "%d rating by %s" % (self.score, self.get_user())
222 217
 
223 218
     def _module_vote(user_id, comment_id, score):
224 219
         try:
225  
-            karma = get_object(comment_id__exact=comment_id, user_id__exact=user_id)
  220
+            karma = get_object(comment__id__exact=comment_id, user__id__exact=user_id)
226 221
         except KarmaScoreDoesNotExist:
227 222
             karma = KarmaScore(None, user_id, comment_id, score, datetime.datetime.now())
228 223
             karma.save()
@@ -241,13 +236,12 @@ def _module_get_pretty_score(score):
241 236
         return int(round((4.5 * score) + 5.5))
242 237
 
243 238
 class UserFlag(meta.Model):
244  
-    db_table = 'comments_user_flags'
245  
-    fields = (
246  
-        meta.ForeignKey(auth.User),
247  
-        meta.ForeignKey(Comment),
248  
-        meta.DateTimeField('flag_date', 'date flagged', auto_now_add=True),
249  
-    )
250  
-    unique_together = (('user_id', 'comment_id'),)
  239
+    user = meta.ForeignKey(auth.User)
  240
+    comment = meta.ForeignKey(Comment)
  241
+    flag_date = meta.DateTimeField(auto_now_add=True)
  242
+    class META:
  243
+        db_table = 'comments_user_flags'
  244
+        unique_together = (('user', 'comment'),)
251 245
 
252 246
     def __repr__(self):
253 247
         return "Flag by %r" % self.get_user()
@@ -261,7 +255,7 @@ def _module_flag(comment, user):
261 255
         if int(comment.user_id) == int(user.id):
262 256
             return # A user can't flag his own comment. Fail silently.
263 257
         try:
264  
-            f = get_object(user_id__exact=user.id, comment_id__exact=comment.id)
  258
+            f = get_object(user__id__exact=user.id, comment__id__exact=comment.id)
265 259
         except UserFlagDoesNotExist:
266 260
             from django.core.mail import mail_managers
267 261
             f = UserFlag(None, user.id, comment.id, None)
@@ -270,13 +264,12 @@ def _module_flag(comment, user):
270 264
             f.save()
271 265
 
272 266
 class ModeratorDeletion(meta.Model):
273  
-    db_table = 'comments_moderator_deletions'
274  
-    fields = (
275  
-        meta.ForeignKey(auth.User, verbose_name='moderator'),
276  
-        meta.ForeignKey(Comment),
277  
-        meta.DateTimeField('deletion_date', 'date deleted', auto_now_add=True),
278  
-    )
279  
-    unique_together = (('user_id', 'comment_id'),)
  267
+    user = meta.ForeignKey(auth.User, verbose_name='moderator')
  268
+    comment = meta.ForeignKey(Comment)
  269
+    deletion_date = meta.DateTimeField(auto_now_add=True)
  270
+    class META:
  271
+        db_table = 'comments_moderator_deletions'
  272
+        unique_together = (('user', 'comment'),)
280 273
 
281 274
     def __repr__(self):
282 275
         return "Moderator deletion by %r" % self.get_user()
4  django/contrib/comments/templatetags/comments.py
@@ -123,7 +123,7 @@ def render(self, context):
123 123
             self.obj_id = template.resolve_variable(self.context_var_name, context)
124 124
         comment_count = get_count_function(object_id__exact=self.obj_id,
125 125
             content_type__package__label__exact=self.package,
126  
-            content_type__python_module_name__exact=self.module, site_id__exact=SITE_ID)
  126
+            content_type__python_module_name__exact=self.module, site__id__exact=SITE_ID)
127 127
         context[self.var_name] = comment_count
128 128
         return ''
129 129
 
@@ -146,7 +146,7 @@ def render(self, context):
146 146
             'object_id__exact': self.obj_id,
147 147
             'content_type__package__label__exact': self.package,
148 148
             'content_type__python_module_name__exact': self.module,
149  
-            'site_id__exact': SITE_ID,
  149
+            'site__id__exact': SITE_ID,
150 150
             'select_related': True,
151 151
             'order_by': (self.ordering + 'submit_date',),
152 152
         }
6  django/contrib/comments/views/comments.py
@@ -86,8 +86,8 @@ def get_comment(self, new_data):
86 86
     def save(self, new_data):
87 87
         today = datetime.date.today()
88 88
         c = self.get_comment(new_data)
89  
-        for old in comments.get_list(content_type_id__exact=new_data["content_type_id"],
90  
-            object_id__exact=new_data["object_id"], user_id__exact=self.get_user_id()):
  89
+        for old in comments.get_list(content_type__id__exact=new_data["content_type_id"],
  90
+            object_id__exact=new_data["object_id"], user__id__exact=self.get_user_id()):
91 91
             # Check that this comment isn't duplicate. (Sometimes people post
92 92
             # comments twice by mistake.) If it is, fail silently by pretending
93 93
             # the comment was posted successfully.
@@ -141,7 +141,7 @@ def save(self, new_data):
141 141
         # Check that this comment isn't duplicate. (Sometimes people post
142 142
         # comments twice by mistake.) If it is, fail silently by pretending
143 143
         # the comment was posted successfully.
144  
-        for old_comment in freecomments.get_list(content_type_id__exact=new_data["content_type_id"],
  144
+        for old_comment in freecomments.get_list(content_type__id__exact=new_data["content_type_id"],
145 145
             object_id__exact=new_data["object_id"], person_name__exact=new_data["person_name"],
146 146
             submit_date__year=today.year, submit_date__month=today.month,
147 147
             submit_date__day=today.day):
8  django/contrib/comments/views/userflags.py
@@ -16,7 +16,7 @@ def flag(request, comment_id):
16 16
             the flagged `comments.comments` object
17 17
     """
18 18
     try:
19  
-        comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
  19
+        comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
20 20
     except comments.CommentDoesNotExist:
21 21
         raise Http404
22 22
     if request.POST:
@@ -31,7 +31,7 @@ def flag(request, comment_id):
31 31
 
32 32
 def flag_done(request, comment_id):
33 33
     try:
34  
-        comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
  34
+        comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
35 35
     except comments.CommentDoesNotExist:
36 36
         raise Http404
37 37
     t = template_loader.get_template('comments/flag_done')
@@ -50,7 +50,7 @@ def delete(request, comment_id):
50 50
             the flagged `comments.comments` object
51 51
     """
52 52
     try:
53  
-        comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
  53
+        comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
54 54
     except comments.CommentDoesNotExist:
55 55
         raise Http404
56 56
     if not comments.user_is_moderator(request.user):
@@ -72,7 +72,7 @@ def delete(request, comment_id):
72 72
 
73 73
 def delete_done(request, comment_id):
74 74
     try:
75  
-        comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
  75
+        comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
76 76
     except comments.CommentDoesNotExist:
77 77
         raise Http404
78 78
     t = template_loader.get_template('comments/delete_done')
18  django/core/management.py
@@ -67,7 +67,7 @@ def get_sql_create(mod):
67 67
                 data_type = f.__class__.__name__
68 68
             col_type = db.DATA_TYPES[data_type]
69 69
             if col_type is not None:
70  
-                field_output = [f.name, col_type % rel_field.__dict__]
  70
+                field_output = [f.column, col_type % rel_field.__dict__]
71 71
                 field_output.append('%sNULL' % (not f.null and 'NOT ' or ''))
72 72
                 if f.unique:
73 73
                     field_output.append('UNIQUE')
@@ -75,12 +75,12 @@ def get_sql_create(mod):
75 75
                     field_output.append('PRIMARY KEY')
76 76
                 if f.rel:
77 77
                     field_output.append('REFERENCES %s (%s)' % \
78  
-                        (f.rel.to.db_table, f.rel.to.get_field(f.rel.field_name).name))
  78
+                        (f.rel.to.db_table, f.rel.to.get_field(f.rel.field_name).column))
79 79
                 table_output.append(' '.join(field_output))
80 80
         if opts.order_with_respect_to:
81 81
             table_output.append('_order %s NULL' % db.DATA_TYPES['IntegerField'])
82 82
         for field_constraints in opts.unique_together:
83  
-            table_output.append('UNIQUE (%s)' % ", ".join(field_constraints))
  83
+            table_output.append('UNIQUE (%s)' % ", ".join([opts.get_field(f).column for f in field_constraints]))
84 84
 
85 85
         full_statement = ['CREATE TABLE %s (' % opts.db_table]
86 86
         for i, line in enumerate(table_output): # Combine and add commas.
@@ -94,9 +94,9 @@ def get_sql_create(mod):
94 94
             table_output = ['CREATE TABLE %s (' % f.get_m2m_db_table(opts)]
95 95
             table_output.append('    id %s NOT NULL PRIMARY KEY,' % db.DATA_TYPES['AutoField'])
96 96
             table_output.append('    %s_id %s NOT NULL REFERENCES %s (%s),' % \
97  
-                (opts.object_name.lower(), db.DATA_TYPES['IntegerField'], opts.db_table, opts.pk.name))
  97
+                (opts.object_name.lower(), db.DATA_TYPES['IntegerField'], opts.db_table, opts.pk.column))
98 98
             table_output.append('    %s_id %s NOT NULL REFERENCES %s (%s),' % \
99  
-                (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.name))
  99
+                (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.column))
100 100
             table_output.append('    UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower()))
101 101
             table_output.append(');')
102 102
             final_output.append('\n'.join(table_output))
@@ -186,7 +186,7 @@ def get_sql_sequence_reset(mod):
186 186
     for klass in mod._MODELS:
187 187
         for f in klass._meta.fields:
188 188
             if isinstance(f, meta.AutoField):
189  
-                output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.name, f.name, klass._meta.db_table))
  189
+                output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
190 190
     return output
191 191
 get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given app(s)."
192 192
 get_sql_sequence_reset.args = APP_ARGS
@@ -199,7 +199,7 @@ def get_sql_indexes(mod):
199 199
             if f.db_index:
200 200
                 unique = f.unique and "UNIQUE " or ""
201 201
                 output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
202  
-                    (unique, klass._meta.db_table, f.name, klass._meta.db_table, f.name))
  202
+                    (unique, klass._meta.db_table, f.column, klass._meta.db_table, f.column))
203 203
     return output
204 204
 get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given app(s)."
205 205
 get_sql_indexes.args = APP_ARGS
@@ -490,7 +490,7 @@ def __init__(self, outfile=sys.stdout):
490 490
 
491 491
     def add(self, opts, error):
492 492
         self.errors.append((opts, error))
493  
-        self.outfile.write("%s.%s: %s\n" % (opts.module_name, opts.object_name, error))
  493
+        self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
494 494
 
495 495
 def validate():
496 496
     "Validates all installed models."
@@ -524,6 +524,8 @@ def validate():
524 524
                     if field_name == '?': continue
525 525
                     if field_name.startswith('-'):
526 526
                         field_name = field_name[1:]
  527
+                    if opts.order_with_respect_to and field_name == '_order':
  528
+                        continue
527 529
                     try:
528 530
                         opts.get_field(field_name, many_to_many=False)
529 531
                     except meta.FieldDoesNotExist:
283  django/core/meta/__init__.py
@@ -50,15 +50,21 @@ def handle_legacy_orderlist(order_list):
50 50
         warnings.warn("%r ordering syntax is deprecated. Use %r instead." % (order_list, new_order_list), DeprecationWarning)
51 51
         return new_order_list
52 52
 
53  
-def orderlist2sql(order_list, prefix=''):
  53
+def orderfield2column(f, opts):
  54
+    try:
  55
+        return opts.get_field(f, False).column
  56
+    except FieldDoesNotExist:
  57
+        return f
  58
+
  59
+def orderlist2sql(order_list, opts, prefix=''):
54 60
     output = []
55 61
     for f in handle_legacy_orderlist(order_list):
56 62
         if f.startswith('-'):
57  
-            output.append('%s%s DESC' % (prefix, f[1:]))
  63
+            output.append('%s%s DESC' % (prefix, orderfield2column(f[1:], opts)))
58 64
         elif f == '?':
59 65
             output.append('RANDOM()')
60 66
         else:
61  
-            output.append('%s%s ASC' % (prefix, f))
  67
+            output.append('%s%s ASC' % (prefix, orderfield2column(f, opts)))
62 68
     return ', '.join(output)
63 69
 
64 70
 def get_module(app_label, module_name):
@@ -206,7 +212,7 @@ def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_t
206 212
         # If a primary_key field hasn't been specified, add an
207 213
         # auto-incrementing primary-key ID field automatically.
208 214
         if self.pk is None:
209  
-            self.fields.insert(0, AutoField('id', 'ID', primary_key=True))
  215
+            self.fields.insert(0, AutoField(name='id', verbose_name='ID', primary_key=True))
210 216
             self.pk = self.fields[0]
211 217
         # Cache whether this has an AutoField.
212 218
         self.has_auto_field = False
@@ -249,7 +255,7 @@ def get_order_sql(self, table_prefix=''):
249 255
         "Returns the full 'ORDER BY' clause for this object, according to self.ordering."
250 256
         if not self.ordering: return ''
251 257
         pre = table_prefix and (table_prefix + '.') or ''
252  
-        return 'ORDER BY ' + orderlist2sql(self.ordering, pre)
  258
+        return 'ORDER BY ' + orderlist2sql(self.ordering, self, pre)
253 259
 
254 260
     def get_add_permission(self):
255 261
         return 'add_%s' % self.object_name.lower()
@@ -298,7 +304,7 @@ def get_all_related_objects(self):
298 304
                 # subsequently loaded object with related links will override this
299 305
                 # relationship we're adding.
300 306
                 link_field = copy.copy(core.RelatedLink._meta.get_field('object_id'))
301  
-                link_field.rel = ManyToOne(self.get_model_module().Klass, 'related_links', 'id',
  307
+                link_field.rel = ManyToOne(self.get_model_module().Klass, 'id',
302 308
                     num_in_admin=3, min_num_in_admin=3, edit_inline=TABULAR,
303 309
                     lookup_overrides={
304 310
                         'content_type__package__label__exact': self.app_label,
@@ -386,34 +392,48 @@ def __new__(cls, name, bases, attrs):
386 392
         if not bases:
387 393
             return type.__new__(cls, name, bases, attrs)
388 394
 
389  
-        # If this model is a subclass of another Model, create an Options
  395
+        try:
  396
+            meta_attrs = attrs.pop('META').__dict__
  397
+            del meta_attrs['__module__']
  398
+            del meta_attrs['__doc__']
  399
+        except KeyError:
  400
+            meta_attrs = {}
  401
+
  402
+        # Gather all attributes that are Field instances.
  403
+        fields = []
  404
+        for obj_name, obj in attrs.items():
  405
+            if isinstance(obj, Field):
  406
+                obj.set_name(obj_name)
  407
+                fields.append(obj)
  408
+                del attrs[obj_name]
  409
+
  410
+        # Sort the fields in the order that they were created. The
  411
+        # "creation_counter" is needed because metaclasses don't preserve the
  412
+        # attribute order.
  413
+        fields.sort(lambda x, y: x.creation_counter - y.creation_counter)
  414
+
  415
+        # If this model is a subclass of another model, create an Options
390 416
         # object by first copying the base class's _meta and then updating it
391 417
         # with the overrides from this class.
392 418
         replaces_module = None
393 419
         if bases[0] != Model:
394  
-            if not attrs.has_key('fields'):
395  
-                attrs['fields'] = list(bases[0]._meta._orig_init_args['fields'][:])
396  
-            if attrs.has_key('ignore_fields'):
397  
-                ignore_fields = attrs.pop('ignore_fields')
398  
-                new_fields = []
399  
-                for i, f in enumerate(attrs['fields']):
400  
-                    if f.name not in ignore_fields:
401  
-                        new_fields.append(f)
402  
-                attrs['fields'] = new_fields
403  
-            if attrs.has_key('add_fields'):
404  
-                attrs['fields'].extend(attrs.pop('add_fields'))
405  
-            if attrs.has_key('replaces_module'):
  420
+            field_names = [f.name for f in fields]
  421
+            remove_fields = meta_attrs.pop('remove_fields', [])
  422
+            for f in bases[0]._meta._orig_init_args['fields']:
  423
+                if f.name not in field_names and f.name not in remove_fields:
  424
+                    fields.insert(0, f)
  425
+            if meta_attrs.has_key('replaces_module'):
406 426
                 # Set the replaces_module variable for now. We can't actually
407 427
                 # do anything with it yet, because the module hasn't yet been
408 428
                 # created.
409  
-                replaces_module = attrs.pop('replaces_module').split('.')
  429
+                replaces_module = meta_attrs.pop('replaces_module').split('.')
410 430
             # Pass any Options overrides to the base's Options instance, and
411 431
             # simultaneously remove them from attrs. When this is done, attrs
412 432
             # will be a dictionary of custom methods, plus __module__.
413  
-            meta_overrides = {}
414  
-            for k, v in attrs.items():
  433
+            meta_overrides = {'fields': fields}
  434
+            for k, v in meta_attrs.items():
415 435
                 if not callable(v) and k != '__module__':
416  
-                    meta_overrides[k] = attrs.pop(k)
  436
+                    meta_overrides[k] = meta_attrs.pop(k)
417 437
             opts = bases[0]._meta.copy(**meta_overrides)
418 438
             opts.object_name = name
419 439
             del meta_overrides
@@ -422,28 +442,31 @@ def __new__(cls, name, bases, attrs):
422 442
                 # If the module_name wasn't given, use the class name
423 443
                 # in lowercase, plus a trailing "s" -- a poor-man's
424 444
                 # pluralization.
425  
-                module_name = attrs.pop('module_name', name.lower() + 's'),
  445
+                module_name = meta_attrs.pop('module_name', name.lower() + 's'),
426 446
                 # If the verbose_name wasn't given, use the class name,
427 447
                 # converted from InitialCaps to "lowercase with spaces".
428  
-                verbose_name = attrs.pop('verbose_name',
  448
+                verbose_name = meta_attrs.pop('verbose_name',
429 449
                     re.sub('([A-Z])', ' \\1', name).lower().strip()),
430  
-                verbose_name_plural = attrs.pop('verbose_name_plural', ''),
431  
-                db_table = attrs.pop('db_table', ''),
432  
-                fields = attrs.pop('fields'),
433  
-                ordering = attrs.pop('ordering', None),
434  
-                unique_together = attrs.pop('unique_together', None),
435  
-                admin = attrs.pop('admin', None),
436  
-                has_related_links = attrs.pop('has_related_links', False),
437  
-                where_constraints = attrs.pop('where_constraints', None),
  450
+                verbose_name_plural = meta_attrs.pop('verbose_name_plural', ''),
  451
+                db_table = meta_attrs.pop('db_table', ''),
  452
+                fields = fields,
  453
+                ordering = meta_attrs.pop('ordering', None),
  454
+                unique_together = meta_attrs.pop('unique_together', None),
  455
+                admin = meta_attrs.pop('admin', None),
  456
+                has_related_links = meta_attrs.pop('has_related_links', False),
  457
+                where_constraints = meta_attrs.pop('where_constraints', None),
438 458
                 object_name = name,
439  
-                app_label = attrs.pop('app_label', None),
440  
-                exceptions = attrs.pop('exceptions', None),
441  
-                permissions = attrs.pop('permissions', None),
442  
-                get_latest_by = attrs.pop('get_latest_by', None),
443  
-                order_with_respect_to = attrs.pop('order_with_respect_to', None),
444  
-                module_constants = attrs.pop('module_constants', None),
  459
+                app_label = meta_attrs.pop('app_label', None),
  460
+                exceptions = meta_attrs.pop('exceptions', None),
  461
+                permissions = meta_attrs.pop('permissions', None),
  462
+                get_latest_by = meta_attrs.pop('get_latest_by', None),
  463
+                order_with_respect_to = meta_attrs.pop('order_with_respect_to', None),
  464
+                module_constants = meta_attrs.pop('module_constants', None),
445 465
             )
446 466
 
  467
+        if meta_attrs != {}:
  468
+            raise TypeError, "'class META' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
  469
+
447 470
         # Dynamically create the module that will contain this class and its
448 471
         # associated helper functions.
449 472
         if replaces_module is not None:
@@ -511,7 +534,7 @@ def __new__(cls, name, bases, attrs):
511 534
             # RECURSIVE_RELATIONSHIP_CONSTANT, create that relationship formally.
512 535
             if f.rel and f.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT:
513 536
                 f.rel.to = opts
514  
-                f.name = f.name or ((f.rel.name or f.rel.to.object_name.lower()) + '_' + f.rel.to.pk.name)
  537
+                f.name = f.name or (f.rel.to.object_name.lower() + '_' + f.rel.to.pk.name)
515 538
                 f.verbose_name = f.verbose_name or f.rel.to.verbose_name
516 539
                 f.rel.field_name = f.rel.field_name or f.rel.to.pk.name
517 540
             # Add "get_thingie" methods for many-to-one related objects.
@@ -519,14 +542,14 @@ def __new__(cls, name, bases, attrs):
519 542
             if isinstance(f.rel, ManyToOne):
520 543
                 func = curry(method_get_many_to_one, f)
521 544
                 func.__doc__ = "Returns the associated `%s.%s` object." % (f.rel.to.app_label, f.rel.to.module_name)
522  
-                attrs['get_%s' % f.rel.name] = func
  545
+                attrs['get_%s' % f.name] = func
523 546
 
524 547
         for f in opts.many_to_many:
525 548
             # Add "get_thingie" methods for many-to-many related objects.
526 549
             # EXAMPLES: Poll.get_site_list(), Story.get_byline_list()
527 550
             func = curry(method_get_many_to_many, f)
528 551
             func.__doc__ = "Returns a list of associated `%s.%s` objects." % (f.rel.to.app_label, f.rel.to.module_name)
529  
-            attrs['get_%s_list' % f.rel.name] = func
  552
+            attrs['get_%s_list' % f.rel.singular] = func
530 553
             # Add "set_thingie" methods for many-to-many related objects.
531 554
             # EXAMPLES: Poll.set_sites(), Story.set_bylines()
532 555
             func = curry(method_set_many_to_many, f)
@@ -711,14 +734,36 @@ def __repr__(self):
711 734
 def method_init(opts, self, *args, **kwargs):
712 735
     if kwargs:
713 736
         for f in opts.fields:
714  
-            setattr(self, f.name, kwargs.pop(f.name, f.get_default()))
  737
+            if isinstance(f.rel, ManyToOne):
  738
+                try:
  739
+                    # Assume object instance was passed in.
  740
+                    rel_obj = kwargs.pop(f.name)
  741
+                except KeyError:
  742
+                    try:
  743
+                        # Object instance wasn't passed in -- must be an ID.
  744
+                        val = kwargs.pop(f.column)
  745
+                    except KeyError:
  746
+                        val = f.get_default()
  747
+                else:
  748
+                    # Special case: You can pass in "None" for related objects if it's allowed.
  749
+                    if rel_obj is None and f.null:
  750
+                        val = None
  751
+                    else:
  752
+                        try:
  753
+                            val = getattr(rel_obj, f.rel.field_name)
  754
+                        except AttributeError:
  755
+                            raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj))
  756
+                setattr(self, f.column, val)
  757
+            else:
  758
+                val = kwargs.pop(f.name, f.get_default())
  759
+                setattr(self, f.name, val)
715 760
         if kwargs:
716 761
             raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
717 762
     for i, arg in enumerate(args):
718  
-        setattr(self, opts.fields[i].name, arg)
  763
+        setattr(self, opts.fields[i].column, arg)
719 764
 
720 765
 def method_eq(opts, self, other):
721  
-    return isinstance(other, self.__class__) and getattr(self, opts.pk.name) == getattr(other, opts.pk.name)
  766
+    return isinstance(other, self.__class__) and getattr(self, opts.pk.column) == getattr(other, opts.pk.column)
722 767
 
723 768
 def method_save(opts, self):
724 769
     # Run any pre-save hooks.
@@ -728,41 +773,41 @@ def method_save(opts, self):
728 773
     cursor = db.db.cursor()
729 774
 
730 775
     # First, try an UPDATE. If that doesn't update anything, do an INSERT.
731  
-    pk_val = getattr(self, opts.pk.name)
  776
+    pk_val = getattr(self, opts.pk.column)
732 777
     pk_set = bool(pk_val)
733 778
     record_exists = True
734 779
     if pk_set:
735 780
         # Determine whether a record with the primary key already exists.
736  
-        cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.name), [pk_val])
  781
+        cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.column), [pk_val])
737 782
         # If it does already exist, do an UPDATE.
738 783
         if cursor.fetchone():
739  
-            db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.name), False)) for f in non_pks]
  784
+            db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks]
740 785
             cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table,
741  
-                ','.join(['%s=%%s' % f.name for f in non_pks]), opts.pk.name),
  786
+                ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column),
742 787
                 db_values + [pk_val])
743 788
         else:
744 789
             record_exists = False
745 790
     if not pk_set or not record_exists:
746  
-        field_names = [f.name for f in opts.fields if not isinstance(f, AutoField)]
  791
+        field_names = [f.column for f in opts.fields if not isinstance(f, AutoField)]
747 792
         placeholders = ['%s'] * len(field_names)
748  
-        db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.name), True)) for f in opts.fields if not isinstance(f, AutoField)]
  793
+        db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), True)) for f in opts.fields if not isinstance(f, AutoField)]
749 794
         if opts.order_with_respect_to:
750 795
             field_names.append('_order')
751 796
             # TODO: This assumes the database supports subqueries.
752 797
             placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \
753  
-                (opts.db_table, opts.order_with_respect_to.name))
754  
-            db_values.append(getattr(self, opts.order_with_respect_to.name))
  798
+                (opts.db_table, opts.order_with_respect_to.column))
  799
+            db_values.append(getattr(self, opts.order_with_respect_to.column))
755 800
         cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % (opts.db_table,
756 801
             ','.join(field_names), ','.join(placeholders)), db_values)
757 802
         if opts.has_auto_field:
758  
-            setattr(self, opts.pk.name, db.get_last_insert_id(cursor, opts.db_table, opts.pk.name))
  803
+            setattr(self, opts.pk.column, db.get_last_insert_id(cursor, opts.db_table, opts.pk.column))
759 804
     db.db.commit()
760 805
     # Run any post-save hooks.
761 806
     if hasattr(self, '_post_save'):
762 807
         self._post_save()
763 808
 
764 809
 def method_delete(opts, self):
765  
-    assert getattr(self, opts.pk.name) is not None, "%r can't be deleted because it doesn't have an ID."
  810
+    assert getattr(self, opts.pk.column) is not None, "%r can't be deleted because it doesn't have an ID."
766 811
     # Run any pre-delete hooks.
767 812
     if hasattr(self, '_pre_delete'):
768 813
         self._pre_delete()
@@ -781,15 +826,15 @@ def method_delete(opts, self):
781 826
                 sub_obj.delete()
782 827
     for rel_opts, rel_field in opts.get_all_related_many_to_many_objects():
783 828
         cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (rel_field.get_m2m_db_table(rel_opts),
784  
-            self._meta.object_name.lower()), [getattr(self, opts.pk.name)])
  829
+            self._meta.object_name.lower()), [getattr(self, opts.pk.column)])
785 830
     for f in opts.many_to_many:
786 831
         cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (f.get_m2m_db_table(opts), self._meta.object_name.lower()),
787  
-            [getattr(self, opts.pk.name)])
788  
-    cursor.execute("DELETE FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.name), [getattr(self, opts.pk.name)])
  832
+            [getattr(self, opts.pk.column)])
  833
+    cursor.execute("DELETE FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.column), [getattr(self, opts.pk.column)])
789 834
     db.db.commit()
790  
-    setattr(self, opts.pk.name, None)
  835
+    setattr(self, opts.pk.column, None)
791 836
     for f in opts.fields:
792  
-        if isinstance(f, FileField) and getattr(self, f.name):
  837
+        if isinstance(f, FileField) and getattr(self, f.column):
793 838
             file_name = getattr(self, 'get_%s_filename' % f.name)()
794 839
             # If the file exists and no other object of this type references it,
795 840
             # delete it from the filesystem.
@@ -802,26 +847,26 @@ def method_delete(opts, self):
802 847
 def method_get_next_in_order(opts, order_field, self):
803 848
     if not hasattr(self, '_next_in_order_cache'):
804 849
         self._next_in_order_cache = opts.get_model_module().get_object(order_by=('_order',),
805  
-            where=['_order > (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.name),
806  
-                '%s=%%s' % order_field.name], limit=1,
807  
-            params=[getattr(self, opts.pk.name), getattr(self, order_field.name)])
  850
+            where=['_order > (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.column),
  851
+                '%s=%%s' % order_field.column], limit=1,
  852
+            params=[getattr(self, opts.pk.column), getattr(self, order_field.name)])
808 853
     return self._next_in_order_cache
809 854
 
810 855
 def method_get_previous_in_order(opts, order_field, self):
811 856
     if not hasattr(self, '_previous_in_order_cache'):
812 857
         self._previous_in_order_cache = opts.get_model_module().get_object(order_by=('-_order',),
813  
-            where=['_order < (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.name),
814  
-                '%s=%%s' % order_field.name], limit=1,
815  
-            params=[getattr(self, opts.pk.name), getattr(self, order_field.name)])
  858
+            where=['_order < (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.column),
  859
+                '%s=%%s' % order_field.column], limit=1,
  860
+            params=[getattr(self, opts.pk.column), getattr(self, order_field.name)])
816 861
     return self._previous_in_order_cache
817 862
 
818 863
 # RELATIONSHIP METHODS #####################
819 864
 
820 865
 # Example: Story.get_dateline()
821 866
 def method_get_many_to_one(field_with_rel, self):
822  
-    cache_var = field_with_rel.rel.get_cache_name()
  867
+    cache_var = field_with_rel.get_cache_name()
823 868
     if not hasattr(self, cache_var):
824  
-        val = getattr(self, field_with_rel.name)
  869
+        val = getattr(self, field_with_rel.column)
825 870
         mod = field_with_rel.rel.to.get_model_module()
826 871
         if val is None:
827 872
             raise getattr(mod, '%sDoesNotExist' % field_with_rel.rel.to.object_name)
@@ -837,11 +882,11 @@ def method_get_many_to_many(field_with_rel, self):
837 882
     if not hasattr(self, cache_var):
838 883
         mod = rel.get_model_module()
839 884
         sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s_id AND b.%s_id = %%s %s" % \
840  
-            (','.join(['a.%s' % f.name for f in rel.fields]), rel.db_table,
841  
-            field_with_rel.get_m2m_db_table(self._meta), rel.pk.name,
  885
+            (','.join(['a.%s' % f.column for f in rel.fields]), rel.db_table,
  886
+            field_with_rel.get_m2m_db_table(self._meta), rel.pk.column,
842 887
             rel.object_name.lower(), self._meta.object_name.lower(), rel.get_order_sql('a'))
843 888
         cursor = db.db.cursor()
844  
-        cursor.execute(sql, [getattr(self, self._meta.pk.name)])
  889
+        cursor.execute(sql, [getattr(self, self._meta.pk.column)])
845 890
         setattr(self, cache_var, [getattr(mod, rel.object_name)(*row) for row in cursor.fetchall()])
846 891
     return getattr(self, cache_var)
847 892
 
@@ -863,7 +908,7 @@ def method_set_many_to_many(rel_field, self, id_list):
863 908
     rel = rel_field.rel.to
864 909
     m2m_table = rel_field.get_m2m_db_table(self._meta)
865 910
     cursor = db.db.cursor()
866  
-    this_id = getattr(self, self._meta.pk.name)
  911
+    this_id = getattr(self, self._meta.pk.column)
867 912
     if ids_to_delete:
868 913
         sql = "DELETE FROM %s WHERE %s_id = %%s AND %s_id IN (%s)" % (m2m_table, self._meta.object_name.lower(), rel.object_name.lower(), ','.join(map(str, ids_to_delete)))
869 914
         cursor.execute(sql, [this_id])
@@ -880,7 +925,7 @@ def method_set_many_to_many(rel_field, self, id_list):
880 925
 # Handles related-object retrieval.
881 926
 # Examples: Poll.get_choice(), Poll.get_choice_list(), Poll.get_choice_count()
882 927
 def method_get_related(method_name, rel_mod, rel_field, self, **kwargs):
883  
-    kwargs['%s__exact' % rel_field.name] = getattr(self, rel_field.rel.field_name)
  928
+    kwargs['%s__%s__exact' % (rel_field.name, rel_field.rel.to.pk.name)] = getattr(self, rel_field.rel.field_name)
884 929
     kwargs.update(rel_field.rel.lookup_overrides)
885 930
     return getattr(rel_mod, method_name)(**kwargs)
886 931
 
@@ -892,7 +937,7 @@ def method_add_related(rel_obj, rel_mod, rel_field, self, *args, **kwargs):
892 937
     for f in rel_obj.fields:
893 938
         if isinstance(f, AutoField):
894 939
             init_kwargs[f.name] = None
895  
-    init_kwargs[rel_field.name] = getattr(self, rel_field.rel.field_name)
  940
+    init_kwargs[rel_field.name] = self
896 941
     obj = rel_mod.Klass(**init_kwargs)
897 942
     obj.save()
898 943
     return obj
@@ -909,7 +954,7 @@ def method_set_related_many_to_many(rel_opts, rel_field, self, id_list):
909 954
     id_list = map(int, id_list) # normalize to integers
910 955
     rel = rel_field.rel.to
911 956
     m2m_table = rel_field.get_m2m_db_table(rel_opts)
912  
-    this_id = getattr(self, self._meta.pk.name)
  957
+    this_id = getattr(self, self._meta.pk.column)
913 958
     cursor = db.db.cursor()
914 959
     cursor.execute("DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower()), [this_id])
915 960
     sql = "INSERT INTO %s (%s_id, %s_id) VALUES (%%s, %%s)" % (m2m_table, rel.object_name.lower(), rel_opts.object_name.lower())
@@ -921,7 +966,7 @@ def method_set_related_many_to_many(rel_opts, rel_field, self, id_list):
921 966
 def method_set_order(ordered_obj, self, id_list):
922 967
     cursor = db.db.cursor()
923 968
     # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
924  
-    sql = "UPDATE %s SET _order = %%s WHERE %s = %%s AND %s = %%s" % (ordered_obj.db_table, ordered_obj.order_with_respect_to.name, ordered_obj.pk.name)
  969
+    sql = "UPDATE %s SET _order = %%s WHERE %s = %%s AND %s = %%s" % (ordered_obj.db_table, ordered_obj.order_with_respect_to.column, ordered_obj.pk.column)
925 970
     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
926 971
     cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
927 972
     db.db.commit()
@@ -929,7 +974,7 @@ def method_set_order(ordered_obj, self, id_list):
929 974
 def method_get_order(ordered_obj, self):
930 975
     cursor = db.db.cursor()
931 976
     # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
932  
-    sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY _order" % (ordered_obj.pk.name, ordered_obj.db_table, ordered_obj.order_with_respect_to.name)
  977
+    sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY _order" % (ordered_obj.pk.column, ordered_obj.db_table, ordered_obj.order_with_respect_to.column)
933 978
     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
934 979
     cursor.execute(sql, [rel_val])
935 980
     return [r[0] for r in cursor.fetchall()]
@@ -937,7 +982,7 @@ def method_get_order(ordered_obj, self):
937 982
 # DATE-RELATED METHODS #####################
938 983
 
939 984
 def method_get_next_or_previous(get_object_func, field, is_next, self, **kwargs):
940  
-    kwargs.setdefault('where', []).append('%s %s %%s' % (field.name, (is_next and '>' or '<')))
  985
+    kwargs.setdefault('where', []).append('%s %s %%s' % (field.column, (is_next and '>' or '<')))
941 986
     kwargs.setdefault('params', []).append(str(getattr(self, field.name)))
942 987
     kwargs['order_by'] = [(not is_next and '-' or '') + field.name]
943 988
     kwargs['limit'] = 1
@@ -1045,7 +1090,7 @@ def _get_cached_row(opts, row, index_start):
1045 1090
     for f in opts.fields:
1046 1091
         if f.rel and not f.null:
1047 1092
             rel_obj, index_end = _get_cached_row(f.rel.to, row, index_end)
1048  
-            setattr(obj, f.rel.get_cache_name(), rel_obj)
  1093
+            setattr(obj, f.get_cache_name(), rel_obj)
1049 1094
     return obj, index_end
1050 1095
 
1051 1096
 def function_get_iterator(opts, klass, **kwargs):
@@ -1091,9 +1136,9 @@ def function_get_values_iterator(opts, klass, **kwargs):
1091 1136
 
1092 1137
     # 'fields' is a list of field names to fetch.
1093 1138
     try:
1094  
-        fields = kwargs.pop('fields')
  1139
+        fields = [opts.get_field(f).column for f in kwargs.pop('fields')]
1095 1140
     except KeyError: # Default to all fields.
1096  
-        fields = [f.name for f in opts.fields]
  1141
+        fields = [f.column for f in opts.fields]
1097 1142
 
1098 1143
     cursor = db.db.cursor()
1099 1144
     _, sql, params = function_get_sql_clause(opts, **kwargs)
@@ -1124,8 +1169,8 @@ def _fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen
1124 1169
                 tables.append('%s %s' % (db_table, new_prefix))
1125 1170
                 db_table = new_prefix
1126 1171
             cache_tables_seen.append(db_table)
1127  
-            where.append('%s.%s = %s.%s' % (old_prefix, f.name, db_table, f.rel.field_name))
1128  
-            select.extend(['%s.%s' % (db_table, f2.name) for f2 in f.rel.to.fields])
  1172
+            where.append('%s.%s = %s.%s' % (old_prefix, f.column, db_table, f.rel.get_related_field().column))
  1173
+            select.extend(['%s.%s' % (db_table, f2.column) for f2 in f.rel.to.fields])
1129 1174
             _fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
1130 1175
 
1131 1176
 def _throw_bad_kwarg_error(kwarg):
@@ -1158,7 +1203,10 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
1158 1203
         lookup_list = kwarg.split(LOOKUP_SEPARATOR)
1159 1204
         # pk="value" is shorthand for (primary key)__exact="value"
1160 1205
         if lookup_list[-1] == 'pk':
1161  
-            lookup_list = lookup_list[:-1] + [opts.pk.name, 'exact']
  1206
+            if opts.pk.rel:
  1207
+                lookup_list = lookup_list[:-1] + [opts.pk.name, opts.pk.rel.field_name, 'exact']
  1208
+            else:
  1209
+                lookup_list = lookup_list[:-1] + [opts.pk.name, 'exact']
1162 1210
         if len(lookup_list) == 1:
1163 1211
             _throw_bad_kwarg_error(kwarg)
1164 1212
         lookup_type = lookup_list.pop()
@@ -1184,7 +1232,7 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
1184 1232
                         rel_table_alias = 't%s' % table_count
1185 1233
                         table_count += 1
1186 1234
                         tables.append('%s %s' % (f.get_m2m_db_table(current_opts), rel_table_alias))
1187  
-                        join_where.append('%s.%s = %s.%s_id' % (current_table_alias, current_opts.pk.name,
  1235
+                        join_where.append('%s.%s = %s.%s_id' % (current_table_alias, current_opts.pk.column,
1188 1236
                             rel_table_alias, current_opts.object_name.lower()))
1189 1237
                         # Optimization: In the case of primary-key lookups, we
1190 1238
                         # don't have to do an extra join.
@@ -1198,32 +1246,39 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
1198 1246
                             new_table_alias = 't%s' % table_count
1199 1247
                             tables.append('%s %s' % (f.rel.to.db_table, new_table_alias))
1200 1248
                             join_where.append('%s.%s_id = %s.%s' % (rel_table_alias, f.rel.to.object_name.lower(),
1201  
-                                new_table_alias, f.rel.to.pk.name))
  1249
+                                new_table_alias, f.rel.to.pk.column))
1202 1250
                             current_table_alias = new_table_alias
1203 1251
                             param_required = True
1204 1252
                         current_opts = f.rel.to
1205 1253
                         raise StopIteration
1206 1254
                 for f in current_opts.fields:
1207 1255
                     # Try many-to-one relationships...
1208  
-                    if f.rel and f.rel.name == current:
  1256
+                    if f.rel and f.name == current:
1209 1257
                         # Optimization: In the case of primary-key lookups, we
1210 1258
                         # don't have to do an extra join.
1211 1259
                         if lookup_list and lookup_list[0] == f.rel.to.pk.name and lookup_type == 'exact':
1212  
-                            where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.name, kwarg_value))
  1260
+                            where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.column, kwarg_value))
1213 1261
                             params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
1214 1262
                             lookup_list.pop()
1215 1263
                             param_required = False
  1264
+                        # 'isnull' lookups in many-to-one relationships are a special case,
  1265
+                        # because we don't want to do a join. We just want to find out
  1266
+                        # whether the foreign key field is NULL.
  1267
+                        elif lookup_type == 'isnull' and not lookup_list:
  1268
+                            where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.column, kwarg_value))
  1269
+                            params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
1216 1270
                         else:
1217 1271
                             new_table_alias = 't%s' % table_count
1218 1272
                             tables.append('%s %s' % (f.rel.to.db_table, new_table_alias))
1219  
-                            join_where.append('%s.%s = %s.%s' % (current_table_alias, f.name, new_table_alias, f.rel.to.pk.name))
  1273
+                            join_where.append('%s.%s = %s.%s' % (current_table_alias, f.column, \
  1274
+                                new_table_alias, f.rel.to.pk.column))
1220 1275
                             current_table_alias = new_table_alias
1221 1276
                             param_required = True
1222 1277
                         current_opts = f.rel.to
1223 1278
                         raise StopIteration
1224 1279
                     # Try direct field-name lookups...
1225 1280
                     if f.name == current:
1226  
-                        where.append(_get_where_clause(lookup_type, current_table_alias+'.', current, kwarg_value))
  1281
+                        where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.column, kwarg_value))
1227 1282
                         params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
1228 1283
                         param_required = False
1229 1284
                         raise StopIteration
@@ -1235,7 +1290,7 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
1235 1290
     return tables, join_where, where, params, table_count
1236 1291
 
1237 1292
 def function_get_sql_clause(opts, **kwargs):
1238  
-    select = ["%s.%s" % (opts.db_table, f.name) for f in opts.fields]
  1293
+    select = ["%s.%s" % (opts.db_table, f.column) for f in opts.fields]
1239 1294
     tables = [opts.db_table] + (kwargs.get('tables') and kwargs['tables'][:] or [])
1240 1295
     where = kwargs.get('where') and kwargs['where'][:] or []
1241 1296
     params = kwargs.get('params') and kwargs['params'][:] or []
@@ -1270,9 +1325,9 @@ def function_get_sql_clause(opts, **kwargs):
1270 1325
             else:
1271 1326
                 table_prefix = ''
1272 1327
             if f.startswith('-'):
1273  
-                order_by.append('%s%s DESC' % (table_prefix, f[1:]))
  1328
+                order_by.append('%s%s DESC' % (table_prefix, orderfield2column(f[1:], opts)))
1274 1329
             else:
1275  
-                order_by.append('%s%s ASC' % (table_prefix, f))
  1330
+                order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts)))
1276 1331
     order_by = ", ".join(order_by)
1277 1332
 
1278 1333
     # LIMIT and OFFSET clauses
@@ -1307,9 +1362,9 @@ def function_get_date_list(opts, field, *args, **kwargs):
1307 1362
     assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'"
1308 1363
     kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise.
1309 1364
     if field.null:
1310  
-        kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.name))
  1365
+        kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.column))
1311 1366
     select, sql, params = function_get_sql_clause(opts, **kwargs)
1312  
-    sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.name)), sql)
  1367
+    sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.column)), sql)
1313 1368
     cursor = db.db.cursor()
1314 1369
     cursor.execute(sql, params)
1315 1370
     # We have to manually run typecast_timestamp(str()) on the results, because
@@ -1359,8 +1414,8 @@ def manipulator_init(opts, add, change, self, obj_key=None):
1359 1414
                 lookup_kwargs = opts.one_to_one_field.rel.limit_choices_to
1360 1415
                 lookup_kwargs['%s__exact' % opts.one_to_one_field.rel.field_name] = obj_key
1361 1416
                 _ = opts.one_to_one_field.rel.to.get_model_module().get_object(**lookup_kwargs)
1362  
-                params = dict([(f.name, f.get_default()) for f in opts.fields])
1363  
-                params[opts.pk.name] = obj_key
  1417
+                params = dict([(f.column, f.get_default()) for f in opts.fields])
  1418
+                params[opts.pk.column] = obj_key
1364 1419
                 self.original_object = opts.get_model_module().Klass(**params)
1365 1420
             else:
1366 1421
                 raise
@@ -1396,9 +1451,9 @@ def manipulator_save(opts, klass, add, change, self, new_data):
1396 1451
         # Fields with auto_now_add are another special case; they should keep
1397 1452
         # their original value in the change stage.
1398 1453
         if change and getattr(f, 'auto_now_add', False):
1399  
-            params[f.name] = getattr(self.original_object, f.name)
  1454
+            params[f.column] = getattr(self.original_object, f.name)
1400 1455
         else:
1401  
-            params[f.name] = f.get_manipulator_new_data(new_data)
  1456
+            params[f.column] = f.get_manipulator_new_data(new_data)
1402 1457
 
1403 1458
     if change:
1404 1459
         params[opts.pk.name] = self.obj_key
@@ -1416,7 +1471,7 @@ def manipulator_save(opts, klass, add, change, self, new_data):
1416 1471
     if change:
1417 1472
         self.fields_added, self.fields_changed, self.fields_deleted = [], [], []
1418 1473
         for f in opts.fields:
1419  
-            if not f.primary_key and str(getattr(self.original_object, f.name)) != str(getattr(new_object, f.name)):
  1474
+            if not f.primary_key and str(getattr(self.original_object, f.column)) != str(getattr(new_object, f.column)):
1420 1475
                 self.fields_changed.append(f.verbose_name)
1421 1476
 
1422 1477
     # Save many-to-many objects. Example: Poll.set_sites()
@@ -1467,15 +1522,15 @@ def manipulator_save(opts, klass, add, change, self, new_data):
1467 1522
                 # case, because they'll be dealt with later.
1468 1523
                 if change and (isinstance(f, FileField) or not f.editable):
1469 1524
                     if rel_new_data.get(rel_opts.pk.name, False) and rel_new_data[rel_opts.pk.name][0]:
1470  
-                        params[f.name] = getattr(old_rel_obj, f.name)
  1525
+                        params[f.column] = getattr(old_rel_obj, f.column)
1471 1526
                     else:
1472  
-                        params[f.name] = f.get_default()
  1527
+                        params[f.column] = f.get_default()
1473 1528
                 elif f == rel_field:
1474  
-                    params[f.name] = getattr(new_object, rel_field.rel.field_name)
  1529
+                    params[f.column] = getattr(new_object, rel_field.rel.field_name)
1475 1530
                 elif add and isinstance(f, AutoField):
1476  
-                    params[f.name] = None
  1531
+                    params[f.column] = None
1477 1532
                 else:
1478  
-                    params[f.name] = f.get_manipulator_new_data(rel_new_data, rel=True)
  1533
+                    params[f.column] = f.get_manipulator_new_data(rel_new_data, rel=True)
1479 1534
                 # Related links are a special case, because we have to
1480 1535
                 # manually set the "content_type_id" field.
1481 1536
                 if opts.has_related_links and rel_opts.module_name == 'relatedlinks':
@@ -1501,7 +1556,7 @@ def manipulator_save(opts, klass, add, change, self, new_data):
1501 1556
                         self.fields_added.append('%s "%r"' % (rel_opts.verbose_name, new_rel_obj))
1502 1557
                     else:
1503 1558
                         for f in rel_opts.fields:
1504  
-                            if not f.primary_key and f != rel_field and str(getattr(old_rel_obj, f.name)) != str(getattr(new_rel_obj, f.name)):
  1559
+                            if not f.primary_key and f != rel_field and str(getattr(old_rel_obj, f.column)) != str(getattr(new_rel_obj, f.column)):
1505 1560
                                 self.fields_changed.append('%s for %s "%r"' % (f.verbose_name, rel_opts.verbose_name, new_rel_obj))
1506 1561
 
1507 1562
                 # Save many-to-many objects.
@@ -1527,20 +1582,26 @@ def manipulator_save(opts, klass, add, change, self, new_data):
1527 1582
 def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
1528 1583
     from django.utils.text import get_text_list
1529 1584
     field_list = [opts.get_field(field_name) for field_name in field_name_list]
1530  
-    kwargs = {'%s__iexact' % field_name_list[0]: field_data}
  1585
+    if isinstance(field_list[0].rel, ManyToOne):
  1586
+        kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data}
  1587
+    else:
  1588
+        kwargs = {'%s__iexact' % field_name_list[0]: field_data}
1531 1589
     for f in field_list[1:]:
1532  
-        field_val = all_data.get(f.name, None)
  1590
+        field_val = all_data.get(f.column, None)
1533 1591
         if field_val is None:
1534 1592
             # This will be caught by another validator, assuming the field
1535 1593
             # doesn't have blank=True.
1536 1594
             return
1537  
-        kwargs['%s__iexact' % f.name] = field_val
  1595
+        if isinstance(f.rel, ManyToOne):
  1596
+            kwargs['%s__pk' % f.name] = field_val
  1597
+        else:
  1598
+            kwargs['%s__iexact' % f.name] = field_val
1538 1599
     mod = opts.get_model_module()
1539 1600
     try:
1540 1601
         old_obj = mod.get_object(**kwargs)
1541 1602
     except ObjectDoesNotExist:
1542 1603
         return
1543  
-    if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
  1604
+    if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.column) == getattr(old_obj, opts.pk.column):
1544 1605
         pass
1545 1606
     else:
1546 1607
         raise validators.ValidationError, "%s with this %s already exists for the given %s." % \
@@ -1562,7 +1623,7 @@ def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_t
1562 1623
     except ObjectDoesNotExist:
1563 1624
         return
1564 1625
     else:
1565  
-        if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
  1626
+        if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.column) == getattr(old_obj, opts.pk.column):
1566 1627
             pass
1567 1628
         else:
1568 1629
             format_string = (lookup_type == 'date') and '%B %d, %Y' or '%B %Y'
98  django/core/meta/fields.py
@@ -40,7 +40,7 @@ def manipulator_validator_unique(f, opts, self, field_data, all_data):
40 40
         old_obj = opts.get_model_module().get_object(**{'%s__exact' % f.name: field_data})
41 41
     except ObjectDoesNotExist:
42 42
         return
43  
-    if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
  43
+    if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.column) == getattr(old_obj, opts.pk.column):
44 44
         return
45 45
     raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name)
46 46
 
@@ -50,14 +50,17 @@ class Field(object):
50 50
     # database level.
51 51
     empty_strings_allowed = True
52 52
 
53  
-    def __init__(self, name, verbose_name=None, primary_key=False,
  53
+    # Tracks each time a Field instance is created. Used to retain order.