Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
@adrianholovaty adrianholovaty authored
Showing with 957 additions and 721 deletions.
  1. +101 −108 django/contrib/comments/models/comments.py
  2. +2 −2 django/contrib/comments/templatetags/comments.py
  3. +3 −3 django/contrib/comments/views/comments.py
  4. +4 −4 django/contrib/comments/views/userflags.py
  5. +10 −8 django/core/management.py
  6. +172 −111 django/core/meta/__init__.py
  7. +57 −41 django/core/meta/fields.py
  8. +64 −72 django/models/auth.py
  9. +62 −70 django/models/core.py
  10. +6 −6 django/templatetags/log.py
  11. +10 −10 django/views/admin/main.py
  12. +2 −2 django/views/defaults.py
  13. +18 −31 docs/db-api.txt
  14. +2 −7 docs/faq.txt
  15. +8 −8 docs/forms.txt
  16. +37 −67 docs/model-api.txt
  17. +13 −18 docs/overview.txt
  18. +22 −24 docs/tutorial01.txt
  19. +5 −7 docs/tutorial02.txt
  20. +2 −1  tests/testapp/models/__init__.py
  21. +2 −4 tests/testapp/models/basic.py
  22. +2 −4 tests/testapp/models/custom_methods.py
  23. +5 −6 tests/testapp/models/custom_pk.py
  24. +4 −5 tests/testapp/models/get_latest.py
  25. +4 −5 tests/testapp/models/lookup.py
  26. +12 −18 tests/testapp/models/m2m_intermediary.py
  27. +7 −11 tests/testapp/models/m2o_recursive.py
  28. +7 −15 tests/testapp/models/m2o_recursive2.py
  29. +3 −7 tests/testapp/models/many_to_many.py
  30. +39 −15 tests/testapp/models/many_to_one.py
  31. +77 −0 tests/testapp/models/many_to_one_null.py
  32. +14 −13 tests/testapp/models/one_to_one.py
  33. +8 −9 tests/testapp/models/ordering.py
  34. +3 −5 tests/testapp/models/repr.py
  35. +2 −4 tests/testapp/models/save_delete_hooks.py
  36. +168 −0 tests/testapp/models/subclassing.py
View
209 django/contrib/comments/models/comments.py
@@ -2,59 +2,56 @@
from django.models import auth, core
class Comment(meta.Model):
- db_table = 'comments'
- fields = (
- meta.ForeignKey(auth.User, raw_id_admin=True),
- meta.ForeignKey(core.ContentType, name='content_type_id', rel_name='content_type'),
- meta.IntegerField('object_id', 'object ID'),
- meta.CharField('headline', 'headline', maxlength=255, blank=True),
- meta.TextField('comment', 'comment', maxlength=3000),
- meta.PositiveSmallIntegerField('rating1', 'rating #1', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating2', 'rating #2', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating3', 'rating #3', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating4', 'rating #4', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating5', 'rating #5', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating6', 'rating #6', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating7', 'rating #7', blank=True, null=True),
- meta.PositiveSmallIntegerField('rating8', 'rating #8', blank=True, null=True),
- # This field designates whether to use this row's ratings in
- # aggregate functions (summaries). We need this because people are
- # allowed to post multiple review on the same thing, but the system
- # will only use the latest one (with valid_rating=True) in tallying
- # the reviews.
- meta.BooleanField('valid_rating', 'is valid rating'),
- meta.DateTimeField('submit_date', 'date/time submitted', auto_now_add=True),
- meta.BooleanField('is_public', 'is public'),
- meta.IPAddressField('ip_address', 'IP address', blank=True, null=True),
- meta.BooleanField('is_removed', 'is removed',
- help_text='Check this box if the comment is inappropriate. A "This comment has been removed" message will be displayed instead.'),
- meta.ForeignKey(core.Site),
- )
- module_constants = {
- # min. and max. allowed dimensions for photo resizing (in pixels)
- 'MIN_PHOTO_DIMENSION': 5,
- 'MAX_PHOTO_DIMENSION': 1000,
-
- # option codes for comment-form hidden fields
- 'PHOTOS_REQUIRED': 'pr',
- 'PHOTOS_OPTIONAL': 'pa',
- 'RATINGS_REQUIRED': 'rr',
- 'RATINGS_OPTIONAL': 'ra',
- 'IS_PUBLIC': 'ip',
- }
- ordering = ('-submit_date',)
- admin = meta.Admin(
- fields = (
- (None, {'fields': ('content_type_id', 'object_id', 'site_id')}),
- ('Content', {'fields': ('user_id', 'headline', 'comment')}),
- ('Ratings', {'fields': ('rating1', 'rating2', 'rating3', 'rating4', 'rating5', 'rating6', 'rating7', 'rating8', 'valid_rating')}),
- ('Meta', {'fields': ('is_public', 'is_removed', 'ip_address')}),
- ),
- list_display = ('user_id', 'submit_date', 'content_type_id', 'get_content_object'),
- list_filter = ('submit_date',),
- date_hierarchy = 'submit_date',
- search_fields = ('comment', 'user__username'),
- )
+ user = meta.ForeignKey(auth.User, raw_id_admin=True)
+ content_type = meta.ForeignKey(core.ContentType)
+ object_id = meta.IntegerField('object ID')
+ headline = meta.CharField(maxlength=255, blank=True)
+ comment = meta.TextField(maxlength=3000)
+ rating1 = meta.PositiveSmallIntegerField('rating #1', blank=True, null=True)
+ rating2 = meta.PositiveSmallIntegerField('rating #2', blank=True, null=True)
+ rating3 = meta.PositiveSmallIntegerField('rating #3', blank=True, null=True)
+ rating4 = meta.PositiveSmallIntegerField('rating #4', blank=True, null=True)
+ rating5 = meta.PositiveSmallIntegerField('rating #5', blank=True, null=True)
+ rating6 = meta.PositiveSmallIntegerField('rating #6', blank=True, null=True)
+ rating7 = meta.PositiveSmallIntegerField('rating #7', blank=True, null=True)
+ rating8 = meta.PositiveSmallIntegerField('rating #8', blank=True, null=True)
+ # This field designates whether to use this row's ratings in aggregate
+ # functions (summaries). We need this because people are allowed to post
+ # multiple reviews on the same thing, but the system will only use the
+ # latest one (with valid_rating=True) in tallying the reviews.
+ valid_rating = meta.BooleanField('is valid rating')
+ submit_date = meta.DateTimeField('date/time submitted', auto_now_add=True)
+ is_public = meta.BooleanField()
+ ip_address = meta.IPAddressField('IP address', blank=True, null=True)
+ 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.')
+ site = meta.ForeignKey(core.Site)
+ class META:
+ db_table = 'comments'
+ module_constants = {
+ # min. and max. allowed dimensions for photo resizing (in pixels)
+ 'MIN_PHOTO_DIMENSION': 5,
+ 'MAX_PHOTO_DIMENSION': 1000,
+
+ # option codes for comment-form hidden fields
+ 'PHOTOS_REQUIRED': 'pr',
+ 'PHOTOS_OPTIONAL': 'pa',
+ 'RATINGS_REQUIRED': 'rr',
+ 'RATINGS_OPTIONAL': 'ra',
+ 'IS_PUBLIC': 'ip',
+ }
+ ordering = ('-submit_date',)
+ admin = meta.Admin(
+ fields = (
+ (None, {'fields': ('content_type', 'object_id', 'site')}),
+ ('Content', {'fields': ('user', 'headline', 'comment')}),
+ ('Ratings', {'fields': ('rating1', 'rating2', 'rating3', 'rating4', 'rating5', 'rating6', 'rating7', 'rating8', 'valid_rating')}),
+ ('Meta', {'fields': ('is_public', 'is_removed', 'ip_address')}),
+ ),
+ list_display = ('user', 'submit_date', 'content_type', 'get_content_object'),
+ list_filter = ('submit_date',),
+ date_hierarchy = 'submit_date',
+ search_fields = ('comment', 'user__username'),
+ )
def __repr__(self):
return "%s: %s..." % (self.get_user().username, self.comment[:100])
@@ -156,32 +153,31 @@ def _module_user_is_moderator(user):
return False
class FreeComment(meta.Model):
- "A FreeComment is a comment by a non-registered user"
- db_table = 'comments_free'
- fields = (
- meta.ForeignKey(core.ContentType, name='content_type_id', rel_name='content_type'),
- meta.IntegerField('object_id', 'object ID'),
- meta.TextField('comment', 'comment', maxlength=3000),
- meta.CharField('person_name', "person's name", maxlength=50),
- meta.DateTimeField('submit_date', 'date/time submitted', auto_now_add=True),
- meta.BooleanField('is_public', 'is public'),
- meta.IPAddressField('ip_address', 'IP address'),
- # TODO: Change this to is_removed, like Comment
- meta.BooleanField('approved', 'approved by staff'),
- meta.ForeignKey(core.Site),
- )
- ordering = ('-submit_date',)
- admin = meta.Admin(
- fields = (
- (None, {'fields': ('content_type_id', 'object_id', 'site_id')}),
- ('Content', {'fields': ('person_name', 'comment')}),
- ('Meta', {'fields': ('submit_date', 'is_public', 'ip_address', 'approved')}),
- ),
- list_display = ('person_name', 'submit_date', 'content_type_id', 'get_content_object'),
- list_filter = ('submit_date',),
- date_hierarchy = 'submit_date',
- search_fields = ('comment', 'person_name'),
- )
+ # A FreeComment is a comment by a non-registered user.
+ content_type = meta.ForeignKey(core.ContentType)
+ object_id = meta.IntegerField('object ID')
+ comment = meta.TextField(maxlength=3000)
+ person_name = meta.CharField("person's name", maxlength=50)
+ submit_date = meta.DateTimeField('date/time submitted', auto_now_add=True)
+ is_public = meta.BooleanField()
+ ip_address = meta.IPAddressField()
+ # TODO: Change this to is_removed, like Comment
+ approved = meta.BooleanField('approved by staff')
+ site = meta.ForeignKey(core.Site)
+ class META:
+ db_table = 'comments_free'
+ ordering = ('-submit_date',)
+ admin = meta.Admin(
+ fields = (
+ (None, {'fields': ('content_type', 'object_id', 'site')}),
+ ('Content', {'fields': ('person_name', 'comment')}),
+ ('Meta', {'fields': ('submit_date', 'is_public', 'ip_address', 'approved')}),
+ ),
+ list_display = ('person_name', 'submit_date', 'content_type', 'get_content_object'),
+ list_filter = ('submit_date',),
+ date_hierarchy = 'submit_date',
+ search_fields = ('comment', 'person_name'),
+ )
def __repr__(self):
return "%s: %s..." % (self.person_name, self.comment[:100])
@@ -203,26 +199,25 @@ def get_content_object(self):
get_content_object.short_description = 'Content object'
class KarmaScore(meta.Model):
- module_name = 'karma'
- fields = (
- meta.ForeignKey(auth.User),
- meta.ForeignKey(Comment),
- meta.SmallIntegerField('score', 'score', db_index=True),
- meta.DateTimeField('scored_date', 'date scored', auto_now=True),
- )
- unique_together = (('user_id', 'comment_id'),)
- module_constants = {
- # what users get if they don't have any karma
- 'DEFAULT_KARMA': 5,
- 'KARMA_NEEDED_BEFORE_DISPLAYED': 3,
- }
+ user = meta.ForeignKey(auth.User)
+ comment = meta.ForeignKey(Comment)
+ score = meta.SmallIntegerField(db_index=True)
+ scored_date = meta.DateTimeField(auto_now=True)
+ class META:
+ module_name = 'karma'
+ unique_together = (('user', 'comment'),)
+ module_constants = {
+ # what users get if they don't have any karma
+ 'DEFAULT_KARMA': 5,
+ 'KARMA_NEEDED_BEFORE_DISPLAYED': 3,
+ }
def __repr__(self):
return "%d rating by %s" % (self.score, self.get_user())
def _module_vote(user_id, comment_id, score):
try:
- karma = get_object(comment_id__exact=comment_id, user_id__exact=user_id)
+ karma = get_object(comment__id__exact=comment_id, user__id__exact=user_id)
except KarmaScoreDoesNotExist:
karma = KarmaScore(None, user_id, comment_id, score, datetime.datetime.now())
karma.save()
@@ -241,13 +236,12 @@ def _module_get_pretty_score(score):
return int(round((4.5 * score) + 5.5))
class UserFlag(meta.Model):
- db_table = 'comments_user_flags'
- fields = (
- meta.ForeignKey(auth.User),
- meta.ForeignKey(Comment),
- meta.DateTimeField('flag_date', 'date flagged', auto_now_add=True),
- )
- unique_together = (('user_id', 'comment_id'),)
+ user = meta.ForeignKey(auth.User)
+ comment = meta.ForeignKey(Comment)
+ flag_date = meta.DateTimeField(auto_now_add=True)
+ class META:
+ db_table = 'comments_user_flags'
+ unique_together = (('user', 'comment'),)
def __repr__(self):
return "Flag by %r" % self.get_user()
@@ -261,7 +255,7 @@ def _module_flag(comment, user):
if int(comment.user_id) == int(user.id):
return # A user can't flag his own comment. Fail silently.
try:
- f = get_object(user_id__exact=user.id, comment_id__exact=comment.id)
+ f = get_object(user__id__exact=user.id, comment__id__exact=comment.id)
except UserFlagDoesNotExist:
from django.core.mail import mail_managers
f = UserFlag(None, user.id, comment.id, None)
@@ -270,13 +264,12 @@ def _module_flag(comment, user):
f.save()
class ModeratorDeletion(meta.Model):
- db_table = 'comments_moderator_deletions'
- fields = (
- meta.ForeignKey(auth.User, verbose_name='moderator'),
- meta.ForeignKey(Comment),
- meta.DateTimeField('deletion_date', 'date deleted', auto_now_add=True),
- )
- unique_together = (('user_id', 'comment_id'),)
+ user = meta.ForeignKey(auth.User, verbose_name='moderator')
+ comment = meta.ForeignKey(Comment)
+ deletion_date = meta.DateTimeField(auto_now_add=True)
+ class META:
+ db_table = 'comments_moderator_deletions'
+ unique_together = (('user', 'comment'),)
def __repr__(self):
return "Moderator deletion by %r" % self.get_user()
View
4 django/contrib/comments/templatetags/comments.py
@@ -123,7 +123,7 @@ def render(self, context):
self.obj_id = template.resolve_variable(self.context_var_name, context)
comment_count = get_count_function(object_id__exact=self.obj_id,
content_type__package__label__exact=self.package,
- content_type__python_module_name__exact=self.module, site_id__exact=SITE_ID)
+ content_type__python_module_name__exact=self.module, site__id__exact=SITE_ID)
context[self.var_name] = comment_count
return ''
@@ -146,7 +146,7 @@ def render(self, context):
'object_id__exact': self.obj_id,
'content_type__package__label__exact': self.package,
'content_type__python_module_name__exact': self.module,
- 'site_id__exact': SITE_ID,
+ 'site__id__exact': SITE_ID,
'select_related': True,
'order_by': (self.ordering + 'submit_date',),
}
View
6 django/contrib/comments/views/comments.py
@@ -86,8 +86,8 @@ def get_comment(self, new_data):
def save(self, new_data):
today = datetime.date.today()
c = self.get_comment(new_data)
- for old in comments.get_list(content_type_id__exact=new_data["content_type_id"],
- object_id__exact=new_data["object_id"], user_id__exact=self.get_user_id()):
+ for old in comments.get_list(content_type__id__exact=new_data["content_type_id"],
+ object_id__exact=new_data["object_id"], user__id__exact=self.get_user_id()):
# Check that this comment isn't duplicate. (Sometimes people post
# comments twice by mistake.) If it is, fail silently by pretending
# the comment was posted successfully.
@@ -141,7 +141,7 @@ def save(self, new_data):
# Check that this comment isn't duplicate. (Sometimes people post
# comments twice by mistake.) If it is, fail silently by pretending
# the comment was posted successfully.
- for old_comment in freecomments.get_list(content_type_id__exact=new_data["content_type_id"],
+ for old_comment in freecomments.get_list(content_type__id__exact=new_data["content_type_id"],
object_id__exact=new_data["object_id"], person_name__exact=new_data["person_name"],
submit_date__year=today.year, submit_date__month=today.month,
submit_date__day=today.day):
View
8 django/contrib/comments/views/userflags.py
@@ -16,7 +16,7 @@ def flag(request, comment_id):
the flagged `comments.comments` object
"""
try:
- comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
+ comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
except comments.CommentDoesNotExist:
raise Http404
if request.POST:
@@ -31,7 +31,7 @@ def flag(request, comment_id):
def flag_done(request, comment_id):
try:
- comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
+ comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
except comments.CommentDoesNotExist:
raise Http404
t = template_loader.get_template('comments/flag_done')
@@ -50,7 +50,7 @@ def delete(request, comment_id):
the flagged `comments.comments` object
"""
try:
- comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
+ comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
except comments.CommentDoesNotExist:
raise Http404
if not comments.user_is_moderator(request.user):
@@ -72,7 +72,7 @@ def delete(request, comment_id):
def delete_done(request, comment_id):
try:
- comment = comments.get_object(pk=comment_id, site_id__exact=SITE_ID)
+ comment = comments.get_object(pk=comment_id, site__id__exact=SITE_ID)
except comments.CommentDoesNotExist:
raise Http404
t = template_loader.get_template('comments/delete_done')
View
18 django/core/management.py
@@ -67,7 +67,7 @@ def get_sql_create(mod):
data_type = f.__class__.__name__
col_type = db.DATA_TYPES[data_type]
if col_type is not None:
- field_output = [f.name, col_type % rel_field.__dict__]
+ field_output = [f.column, col_type % rel_field.__dict__]
field_output.append('%sNULL' % (not f.null and 'NOT ' or ''))
if f.unique:
field_output.append('UNIQUE')
@@ -75,12 +75,12 @@ def get_sql_create(mod):
field_output.append('PRIMARY KEY')
if f.rel:
field_output.append('REFERENCES %s (%s)' % \
- (f.rel.to.db_table, f.rel.to.get_field(f.rel.field_name).name))
+ (f.rel.to.db_table, f.rel.to.get_field(f.rel.field_name).column))
table_output.append(' '.join(field_output))
if opts.order_with_respect_to:
table_output.append('_order %s NULL' % db.DATA_TYPES['IntegerField'])
for field_constraints in opts.unique_together:
- table_output.append('UNIQUE (%s)' % ", ".join(field_constraints))
+ table_output.append('UNIQUE (%s)' % ", ".join([opts.get_field(f).column for f in field_constraints]))
full_statement = ['CREATE TABLE %s (' % opts.db_table]
for i, line in enumerate(table_output): # Combine and add commas.
@@ -94,9 +94,9 @@ def get_sql_create(mod):
table_output = ['CREATE TABLE %s (' % f.get_m2m_db_table(opts)]
table_output.append(' id %s NOT NULL PRIMARY KEY,' % db.DATA_TYPES['AutoField'])
table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \
- (opts.object_name.lower(), db.DATA_TYPES['IntegerField'], opts.db_table, opts.pk.name))
+ (opts.object_name.lower(), db.DATA_TYPES['IntegerField'], opts.db_table, opts.pk.column))
table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \
- (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.name))
+ (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.column))
table_output.append(' UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower()))
table_output.append(');')
final_output.append('\n'.join(table_output))
@@ -186,7 +186,7 @@ def get_sql_sequence_reset(mod):
for klass in mod._MODELS:
for f in klass._meta.fields:
if isinstance(f, meta.AutoField):
- output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.name, f.name, klass._meta.db_table))
+ output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
return output
get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given app(s)."
get_sql_sequence_reset.args = APP_ARGS
@@ -199,7 +199,7 @@ def get_sql_indexes(mod):
if f.db_index:
unique = f.unique and "UNIQUE " or ""
output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
- (unique, klass._meta.db_table, f.name, klass._meta.db_table, f.name))
+ (unique, klass._meta.db_table, f.column, klass._meta.db_table, f.column))
return output
get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given app(s)."
get_sql_indexes.args = APP_ARGS
@@ -490,7 +490,7 @@ def __init__(self, outfile=sys.stdout):
def add(self, opts, error):
self.errors.append((opts, error))
- self.outfile.write("%s.%s: %s\n" % (opts.module_name, opts.object_name, error))
+ self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
def validate():
"Validates all installed models."
@@ -524,6 +524,8 @@ def validate():
if field_name == '?': continue
if field_name.startswith('-'):
field_name = field_name[1:]
+ if opts.order_with_respect_to and field_name == '_order':
+ continue
try:
opts.get_field(field_name, many_to_many=False)
except meta.FieldDoesNotExist:
View
283 django/core/meta/__init__.py
@@ -50,15 +50,21 @@ def handle_legacy_orderlist(order_list):
warnings.warn("%r ordering syntax is deprecated. Use %r instead." % (order_list, new_order_list), DeprecationWarning)
return new_order_list
-def orderlist2sql(order_list, prefix=''):
+def orderfield2column(f, opts):
+ try:
+ return opts.get_field(f, False).column
+ except FieldDoesNotExist:
+ return f
+
+def orderlist2sql(order_list, opts, prefix=''):
output = []
for f in handle_legacy_orderlist(order_list):
if f.startswith('-'):
- output.append('%s%s DESC' % (prefix, f[1:]))
+ output.append('%s%s DESC' % (prefix, orderfield2column(f[1:], opts)))
elif f == '?':
output.append('RANDOM()')
else:
- output.append('%s%s ASC' % (prefix, f))
+ output.append('%s%s ASC' % (prefix, orderfield2column(f, opts)))
return ', '.join(output)
def get_module(app_label, module_name):
@@ -206,7 +212,7 @@ def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_t
# 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('id', 'ID', primary_key=True))
+ self.fields.insert(0, AutoField(name='id', verbose_name='ID', primary_key=True))
self.pk = self.fields[0]
# Cache whether this has an AutoField.
self.has_auto_field = False
@@ -249,7 +255,7 @@ def get_order_sql(self, table_prefix=''):
"Returns the full 'ORDER BY' clause for this object, according to self.ordering."
if not self.ordering: return ''
pre = table_prefix and (table_prefix + '.') or ''
- return 'ORDER BY ' + orderlist2sql(self.ordering, pre)
+ return 'ORDER BY ' + orderlist2sql(self.ordering, self, pre)
def get_add_permission(self):
return 'add_%s' % self.object_name.lower()
@@ -298,7 +304,7 @@ def get_all_related_objects(self):
# subsequently loaded object with related links will override this
# relationship we're adding.
link_field = copy.copy(core.RelatedLink._meta.get_field('object_id'))
- link_field.rel = ManyToOne(self.get_model_module().Klass, 'related_links', 'id',
+ link_field.rel = ManyToOne(self.get_model_module().Klass, 'id',
num_in_admin=3, min_num_in_admin=3, edit_inline=TABULAR,
lookup_overrides={
'content_type__package__label__exact': self.app_label,
@@ -386,34 +392,48 @@ def __new__(cls, name, bases, attrs):
if not bases:
return type.__new__(cls, name, bases, attrs)
- # If this model is a subclass of another Model, create an Options
+ try:
+ meta_attrs = attrs.pop('META').__dict__
+ del meta_attrs['__module__']
+ del meta_attrs['__doc__']
+ except KeyError:
+ meta_attrs = {}
+
+ # Gather all attributes that are Field instances.
+ fields = []
+ for obj_name, obj in attrs.items():
+ if isinstance(obj, Field):
+ obj.set_name(obj_name)
+ fields.append(obj)
+ del attrs[obj_name]
+
+ # Sort the fields 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)
+
+ # If this model is a subclass of another model, create an Options
# object by first copying the base class's _meta and then updating it
# with the overrides from this class.
replaces_module = None
if bases[0] != Model:
- if not attrs.has_key('fields'):
- attrs['fields'] = list(bases[0]._meta._orig_init_args['fields'][:])
- if attrs.has_key('ignore_fields'):
- ignore_fields = attrs.pop('ignore_fields')
- new_fields = []
- for i, f in enumerate(attrs['fields']):
- if f.name not in ignore_fields:
- new_fields.append(f)
- attrs['fields'] = new_fields
- if attrs.has_key('add_fields'):
- attrs['fields'].extend(attrs.pop('add_fields'))
- if attrs.has_key('replaces_module'):
+ field_names = [f.name for f in fields]
+ remove_fields = meta_attrs.pop('remove_fields', [])
+ for f in bases[0]._meta._orig_init_args['fields']:
+ if f.name not in field_names and f.name not in remove_fields:
+ fields.insert(0, f)
+ if meta_attrs.has_key('replaces_module'):
# Set the replaces_module variable for now. We can't actually
# do anything with it yet, because the module hasn't yet been
# created.
- replaces_module = attrs.pop('replaces_module').split('.')
+ replaces_module = meta_attrs.pop('replaces_module').split('.')
# Pass any Options overrides to the base's Options instance, and
# simultaneously remove them from attrs. When this is done, attrs
# will be a dictionary of custom methods, plus __module__.
- meta_overrides = {}
- for k, v in attrs.items():
+ meta_overrides = {'fields': fields}
+ for k, v in meta_attrs.items():
if not callable(v) and k != '__module__':
- meta_overrides[k] = attrs.pop(k)
+ meta_overrides[k] = meta_attrs.pop(k)
opts = bases[0]._meta.copy(**meta_overrides)
opts.object_name = name
del meta_overrides
@@ -422,28 +442,31 @@ def __new__(cls, name, bases, attrs):
# If the module_name wasn't given, use the class name
# in lowercase, plus a trailing "s" -- a poor-man's
# pluralization.
- module_name = attrs.pop('module_name', name.lower() + 's'),
+ module_name = meta_attrs.pop('module_name', name.lower() + 's'),
# If the verbose_name wasn't given, use the class name,
# converted from InitialCaps to "lowercase with spaces".
- verbose_name = attrs.pop('verbose_name',
+ verbose_name = meta_attrs.pop('verbose_name',
re.sub('([A-Z])', ' \\1', name).lower().strip()),
- verbose_name_plural = attrs.pop('verbose_name_plural', ''),
- db_table = attrs.pop('db_table', ''),
- fields = attrs.pop('fields'),
- ordering = attrs.pop('ordering', None),
- unique_together = attrs.pop('unique_together', None),
- admin = attrs.pop('admin', None),
- has_related_links = attrs.pop('has_related_links', False),
- where_constraints = attrs.pop('where_constraints', None),
+ 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),
+ has_related_links = meta_attrs.pop('has_related_links', False),
+ where_constraints = meta_attrs.pop('where_constraints', None),
object_name = name,
- app_label = attrs.pop('app_label', None),
- exceptions = attrs.pop('exceptions', None),
- permissions = attrs.pop('permissions', None),
- get_latest_by = attrs.pop('get_latest_by', None),
- order_with_respect_to = attrs.pop('order_with_respect_to', None),
- module_constants = attrs.pop('module_constants', None),
+ app_label = meta_attrs.pop('app_label', None),
+ exceptions = meta_attrs.pop('exceptions', None),
+ permissions = meta_attrs.pop('permissions', None),
+ get_latest_by = meta_attrs.pop('get_latest_by', None),
+ order_with_respect_to = meta_attrs.pop('order_with_respect_to', None),
+ module_constants = meta_attrs.pop('module_constants', None),
)
+ if meta_attrs != {}:
+ raise TypeError, "'class META' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
+
# Dynamically create the module that will contain this class and its
# associated helper functions.
if replaces_module is not None:
@@ -511,7 +534,7 @@ def __new__(cls, name, bases, attrs):
# RECURSIVE_RELATIONSHIP_CONSTANT, create that relationship formally.
if f.rel and f.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT:
f.rel.to = opts
- f.name = f.name or ((f.rel.name or f.rel.to.object_name.lower()) + '_' + f.rel.to.pk.name)
+ f.name = f.name or (f.rel.to.object_name.lower() + '_' + f.rel.to.pk.name)
f.verbose_name = f.verbose_name or f.rel.to.verbose_name
f.rel.field_name = f.rel.field_name or f.rel.to.pk.name
# Add "get_thingie" methods for many-to-one related objects.
@@ -519,14 +542,14 @@ def __new__(cls, name, bases, attrs):
if isinstance(f.rel, ManyToOne):
func = curry(method_get_many_to_one, f)
func.__doc__ = "Returns the associated `%s.%s` object." % (f.rel.to.app_label, f.rel.to.module_name)
- attrs['get_%s' % f.rel.name] = func
+ attrs['get_%s' % f.name] = func
for f in opts.many_to_many:
# Add "get_thingie" methods for many-to-many related objects.
# EXAMPLES: Poll.get_site_list(), Story.get_byline_list()
func = curry(method_get_many_to_many, f)
func.__doc__ = "Returns a list of associated `%s.%s` objects." % (f.rel.to.app_label, f.rel.to.module_name)
- attrs['get_%s_list' % f.rel.name] = func
+ attrs['get_%s_list' % f.rel.singular] = func
# Add "set_thingie" methods for many-to-many related objects.
# EXAMPLES: Poll.set_sites(), Story.set_bylines()
func = curry(method_set_many_to_many, f)
@@ -711,14 +734,36 @@ def __repr__(self):
def method_init(opts, self, *args, **kwargs):
if kwargs:
for f in opts.fields:
- setattr(self, f.name, kwargs.pop(f.name, f.get_default()))
+ if isinstance(f.rel, ManyToOne):
+ try:
+ # Assume object instance was passed in.
+ rel_obj = kwargs.pop(f.name)
+ except KeyError:
+ try:
+ # Object instance wasn't passed in -- must be an ID.
+ val = kwargs.pop(f.column)
+ except KeyError:
+ val = f.get_default()
+ else:
+ # Special case: You can pass in "None" for related objects if it's allowed.
+ if rel_obj is None and f.null:
+ val = None
+ else:
+ try:
+ val = getattr(rel_obj, f.rel.field_name)
+ except AttributeError:
+ raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj))
+ setattr(self, f.column, val)
+ else:
+ val = kwargs.pop(f.name, f.get_default())
+ setattr(self, f.name, val)
if kwargs:
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
for i, arg in enumerate(args):
- setattr(self, opts.fields[i].name, arg)
+ setattr(self, opts.fields[i].column, arg)
def method_eq(opts, self, other):
- return isinstance(other, self.__class__) and getattr(self, opts.pk.name) == getattr(other, opts.pk.name)
+ return isinstance(other, self.__class__) and getattr(self, opts.pk.column) == getattr(other, opts.pk.column)
def method_save(opts, self):
# Run any pre-save hooks.
@@ -728,41 +773,41 @@ def method_save(opts, self):
cursor = db.db.cursor()
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
- pk_val = getattr(self, opts.pk.name)
+ pk_val = getattr(self, opts.pk.column)
pk_set = bool(pk_val)
record_exists = True
if pk_set:
# Determine whether a record with the primary key already exists.
- cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.name), [pk_val])
+ cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.column), [pk_val])
# If it does already exist, do an UPDATE.
if cursor.fetchone():
- db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.name), False)) for f in non_pks]
+ db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks]
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table,
- ','.join(['%s=%%s' % f.name for f in non_pks]), opts.pk.name),
+ ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column),
db_values + [pk_val])
else:
record_exists = False
if not pk_set or not record_exists:
- field_names = [f.name for f in opts.fields if not isinstance(f, AutoField)]
+ field_names = [f.column for f in opts.fields if not isinstance(f, AutoField)]
placeholders = ['%s'] * len(field_names)
- 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)]
+ 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)]
if opts.order_with_respect_to:
field_names.append('_order')
# TODO: This assumes the database supports subqueries.
placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \
- (opts.db_table, opts.order_with_respect_to.name))
- db_values.append(getattr(self, opts.order_with_respect_to.name))
+ (opts.db_table, opts.order_with_respect_to.column))
+ db_values.append(getattr(self, opts.order_with_respect_to.column))
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % (opts.db_table,
','.join(field_names), ','.join(placeholders)), db_values)
if opts.has_auto_field:
- setattr(self, opts.pk.name, db.get_last_insert_id(cursor, opts.db_table, opts.pk.name))
+ setattr(self, opts.pk.column, db.get_last_insert_id(cursor, opts.db_table, opts.pk.column))
db.db.commit()
# Run any post-save hooks.
if hasattr(self, '_post_save'):
self._post_save()
def method_delete(opts, self):
- assert getattr(self, opts.pk.name) is not None, "%r can't be deleted because it doesn't have an ID."
+ assert getattr(self, opts.pk.column) is not None, "%r can't be deleted because it doesn't have an ID."
# Run any pre-delete hooks.
if hasattr(self, '_pre_delete'):
self._pre_delete()
@@ -781,15 +826,15 @@ def method_delete(opts, self):
sub_obj.delete()
for rel_opts, rel_field in opts.get_all_related_many_to_many_objects():
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (rel_field.get_m2m_db_table(rel_opts),
- self._meta.object_name.lower()), [getattr(self, opts.pk.name)])
+ self._meta.object_name.lower()), [getattr(self, opts.pk.column)])
for f in opts.many_to_many:
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (f.get_m2m_db_table(opts), self._meta.object_name.lower()),
- [getattr(self, opts.pk.name)])
- cursor.execute("DELETE FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.name), [getattr(self, opts.pk.name)])
+ [getattr(self, opts.pk.column)])
+ cursor.execute("DELETE FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.column), [getattr(self, opts.pk.column)])
db.db.commit()
- setattr(self, opts.pk.name, None)
+ setattr(self, opts.pk.column, None)
for f in opts.fields:
- if isinstance(f, FileField) and getattr(self, f.name):
+ if isinstance(f, FileField) and getattr(self, f.column):
file_name = getattr(self, 'get_%s_filename' % f.name)()
# If the file exists and no other object of this type references it,
# delete it from the filesystem.
@@ -802,26 +847,26 @@ def method_delete(opts, self):
def method_get_next_in_order(opts, order_field, self):
if not hasattr(self, '_next_in_order_cache'):
self._next_in_order_cache = opts.get_model_module().get_object(order_by=('_order',),
- where=['_order > (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.name),
- '%s=%%s' % order_field.name], limit=1,
- params=[getattr(self, opts.pk.name), getattr(self, order_field.name)])
+ where=['_order > (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.column),
+ '%s=%%s' % order_field.column], limit=1,
+ params=[getattr(self, opts.pk.column), getattr(self, order_field.name)])
return self._next_in_order_cache
def method_get_previous_in_order(opts, order_field, self):
if not hasattr(self, '_previous_in_order_cache'):
self._previous_in_order_cache = opts.get_model_module().get_object(order_by=('-_order',),
- where=['_order < (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.name),
- '%s=%%s' % order_field.name], limit=1,
- params=[getattr(self, opts.pk.name), getattr(self, order_field.name)])
+ where=['_order < (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.column),
+ '%s=%%s' % order_field.column], limit=1,
+ params=[getattr(self, opts.pk.column), getattr(self, order_field.name)])
return self._previous_in_order_cache
# RELATIONSHIP METHODS #####################
# Example: Story.get_dateline()
def method_get_many_to_one(field_with_rel, self):
- cache_var = field_with_rel.rel.get_cache_name()
+ cache_var = field_with_rel.get_cache_name()
if not hasattr(self, cache_var):
- val = getattr(self, field_with_rel.name)
+ val = getattr(self, field_with_rel.column)
mod = field_with_rel.rel.to.get_model_module()
if val is None:
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):
if not hasattr(self, cache_var):
mod = rel.get_model_module()
sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s_id AND b.%s_id = %%s %s" % \
- (','.join(['a.%s' % f.name for f in rel.fields]), rel.db_table,
- field_with_rel.get_m2m_db_table(self._meta), rel.pk.name,
+ (','.join(['a.%s' % f.column for f in rel.fields]), rel.db_table,
+ field_with_rel.get_m2m_db_table(self._meta), rel.pk.column,
rel.object_name.lower(), self._meta.object_name.lower(), rel.get_order_sql('a'))
cursor = db.db.cursor()
- cursor.execute(sql, [getattr(self, self._meta.pk.name)])
+ cursor.execute(sql, [getattr(self, self._meta.pk.column)])
setattr(self, cache_var, [getattr(mod, rel.object_name)(*row) for row in cursor.fetchall()])
return getattr(self, cache_var)
@@ -863,7 +908,7 @@ def method_set_many_to_many(rel_field, self, id_list):
rel = rel_field.rel.to
m2m_table = rel_field.get_m2m_db_table(self._meta)
cursor = db.db.cursor()
- this_id = getattr(self, self._meta.pk.name)
+ this_id = getattr(self, self._meta.pk.column)
if ids_to_delete:
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)))
cursor.execute(sql, [this_id])
@@ -880,7 +925,7 @@ def method_set_many_to_many(rel_field, self, id_list):
# Handles related-object retrieval.
# Examples: Poll.get_choice(), Poll.get_choice_list(), Poll.get_choice_count()
def method_get_related(method_name, rel_mod, rel_field, self, **kwargs):
- kwargs['%s__exact' % rel_field.name] = getattr(self, rel_field.rel.field_name)
+ kwargs['%s__%s__exact' % (rel_field.name, rel_field.rel.to.pk.name)] = getattr(self, rel_field.rel.field_name)
kwargs.update(rel_field.rel.lookup_overrides)
return getattr(rel_mod, method_name)(**kwargs)
@@ -892,7 +937,7 @@ def method_add_related(rel_obj, rel_mod, rel_field, self, *args, **kwargs):
for f in rel_obj.fields:
if isinstance(f, AutoField):
init_kwargs[f.name] = None
- init_kwargs[rel_field.name] = getattr(self, rel_field.rel.field_name)
+ init_kwargs[rel_field.name] = self
obj = rel_mod.Klass(**init_kwargs)
obj.save()
return obj
@@ -909,7 +954,7 @@ def method_set_related_many_to_many(rel_opts, rel_field, self, id_list):
id_list = map(int, id_list) # normalize to integers
rel = rel_field.rel.to
m2m_table = rel_field.get_m2m_db_table(rel_opts)
- this_id = getattr(self, self._meta.pk.name)
+ this_id = getattr(self, self._meta.pk.column)
cursor = db.db.cursor()
cursor.execute("DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower()), [this_id])
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):
def method_set_order(ordered_obj, self, id_list):
cursor = db.db.cursor()
# Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
- 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)
+ 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)
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
db.db.commit()
@@ -929,7 +974,7 @@ def method_set_order(ordered_obj, self, id_list):
def method_get_order(ordered_obj, self):
cursor = db.db.cursor()
# Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
- 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)
+ 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)
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
cursor.execute(sql, [rel_val])
return [r[0] for r in cursor.fetchall()]
@@ -937,7 +982,7 @@ def method_get_order(ordered_obj, self):
# DATE-RELATED METHODS #####################
def method_get_next_or_previous(get_object_func, field, is_next, self, **kwargs):
- kwargs.setdefault('where', []).append('%s %s %%s' % (field.name, (is_next and '>' or '<')))
+ kwargs.setdefault('where', []).append('%s %s %%s' % (field.column, (is_next and '>' or '<')))
kwargs.setdefault('params', []).append(str(getattr(self, field.name)))
kwargs['order_by'] = [(not is_next and '-' or '') + field.name]
kwargs['limit'] = 1
@@ -1045,7 +1090,7 @@ def _get_cached_row(opts, row, index_start):
for f in opts.fields:
if f.rel and not f.null:
rel_obj, index_end = _get_cached_row(f.rel.to, row, index_end)
- setattr(obj, f.rel.get_cache_name(), rel_obj)
+ setattr(obj, f.get_cache_name(), rel_obj)
return obj, index_end
def function_get_iterator(opts, klass, **kwargs):
@@ -1091,9 +1136,9 @@ def function_get_values_iterator(opts, klass, **kwargs):
# 'fields' is a list of field names to fetch.
try:
- fields = kwargs.pop('fields')
+ fields = [opts.get_field(f).column for f in kwargs.pop('fields')]
except KeyError: # Default to all fields.
- fields = [f.name for f in opts.fields]
+ fields = [f.column for f in opts.fields]
cursor = db.db.cursor()
_, 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
tables.append('%s %s' % (db_table, new_prefix))
db_table = new_prefix
cache_tables_seen.append(db_table)
- where.append('%s.%s = %s.%s' % (old_prefix, f.name, db_table, f.rel.field_name))
- select.extend(['%s.%s' % (db_table, f2.name) for f2 in f.rel.to.fields])
+ where.append('%s.%s = %s.%s' % (old_prefix, f.column, db_table, f.rel.get_related_field().column))
+ select.extend(['%s.%s' % (db_table, f2.column) for f2 in f.rel.to.fields])
_fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
def _throw_bad_kwarg_error(kwarg):
@@ -1158,7 +1203,10 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
lookup_list = kwarg.split(LOOKUP_SEPARATOR)
# pk="value" is shorthand for (primary key)__exact="value"
if lookup_list[-1] == 'pk':
- lookup_list = lookup_list[:-1] + [opts.pk.name, 'exact']
+ if opts.pk.rel:
+ lookup_list = lookup_list[:-1] + [opts.pk.name, opts.pk.rel.field_name, 'exact']
+ else:
+ lookup_list = lookup_list[:-1] + [opts.pk.name, 'exact']
if len(lookup_list) == 1:
_throw_bad_kwarg_error(kwarg)
lookup_type = lookup_list.pop()
@@ -1184,7 +1232,7 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
rel_table_alias = 't%s' % table_count
table_count += 1
tables.append('%s %s' % (f.get_m2m_db_table(current_opts), rel_table_alias))
- join_where.append('%s.%s = %s.%s_id' % (current_table_alias, current_opts.pk.name,
+ join_where.append('%s.%s = %s.%s_id' % (current_table_alias, current_opts.pk.column,
rel_table_alias, current_opts.object_name.lower()))
# Optimization: In the case of primary-key lookups, we
# don't have to do an extra join.
@@ -1198,32 +1246,39 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
new_table_alias = 't%s' % table_count
tables.append('%s %s' % (f.rel.to.db_table, new_table_alias))
join_where.append('%s.%s_id = %s.%s' % (rel_table_alias, f.rel.to.object_name.lower(),
- new_table_alias, f.rel.to.pk.name))
+ new_table_alias, f.rel.to.pk.column))
current_table_alias = new_table_alias
param_required = True
current_opts = f.rel.to
raise StopIteration
for f in current_opts.fields:
# Try many-to-one relationships...
- if f.rel and f.rel.name == current:
+ if f.rel and f.name == current:
# Optimization: In the case of primary-key lookups, we
# don't have to do an extra join.
if lookup_list and lookup_list[0] == f.rel.to.pk.name and lookup_type == 'exact':
- where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.name, kwarg_value))
+ where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.column, kwarg_value))
params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
lookup_list.pop()
param_required = False
+ # 'isnull' lookups in many-to-one relationships are a special case,
+ # because we don't want to do a join. We just want to find out
+ # whether the foreign key field is NULL.
+ elif lookup_type == 'isnull' and not lookup_list:
+ where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.column, kwarg_value))
+ params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
else:
new_table_alias = 't%s' % table_count
tables.append('%s %s' % (f.rel.to.db_table, new_table_alias))
- join_where.append('%s.%s = %s.%s' % (current_table_alias, f.name, new_table_alias, f.rel.to.pk.name))
+ join_where.append('%s.%s = %s.%s' % (current_table_alias, f.column, \
+ new_table_alias, f.rel.to.pk.column))
current_table_alias = new_table_alias
param_required = True
current_opts = f.rel.to
raise StopIteration
# Try direct field-name lookups...
if f.name == current:
- where.append(_get_where_clause(lookup_type, current_table_alias+'.', current, kwarg_value))
+ where.append(_get_where_clause(lookup_type, current_table_alias+'.', f.column, kwarg_value))
params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
param_required = False
raise StopIteration
@@ -1235,7 +1290,7 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
return tables, join_where, where, params, table_count
def function_get_sql_clause(opts, **kwargs):
- select = ["%s.%s" % (opts.db_table, f.name) for f in opts.fields]
+ select = ["%s.%s" % (opts.db_table, f.column) for f in opts.fields]
tables = [opts.db_table] + (kwargs.get('tables') and kwargs['tables'][:] or [])
where = kwargs.get('where') and kwargs['where'][:] or []
params = kwargs.get('params') and kwargs['params'][:] or []
@@ -1270,9 +1325,9 @@ def function_get_sql_clause(opts, **kwargs):
else:
table_prefix = ''
if f.startswith('-'):
- order_by.append('%s%s DESC' % (table_prefix, f[1:]))
+ order_by.append('%s%s DESC' % (table_prefix, orderfield2column(f[1:], opts)))
else:
- order_by.append('%s%s ASC' % (table_prefix, f))
+ order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts)))
order_by = ", ".join(order_by)
# LIMIT and OFFSET clauses
@@ -1307,9 +1362,9 @@ def function_get_date_list(opts, field, *args, **kwargs):
assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'"
kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise.
if field.null:
- kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.name))
+ kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.column))
select, sql, params = function_get_sql_clause(opts, **kwargs)
- sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.name)), sql)
+ sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.column)), sql)
cursor = db.db.cursor()
cursor.execute(sql, params)
# 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):
lookup_kwargs = opts.one_to_one_field.rel.limit_choices_to
lookup_kwargs['%s__exact' % opts.one_to_one_field.rel.field_name] = obj_key
_ = opts.one_to_one_field.rel.to.get_model_module().get_object(**lookup_kwargs)
- params = dict([(f.name, f.get_default()) for f in opts.fields])
- params[opts.pk.name] = obj_key
+ params = dict([(f.column, f.get_default()) for f in opts.fields])
+ params[opts.pk.column] = obj_key
self.original_object = opts.get_model_module().Klass(**params)
else:
raise
@@ -1396,9 +1451,9 @@ def manipulator_save(opts, klass, add, change, self, new_data):
# Fields with auto_now_add are another special case; they should keep
# their original value in the change stage.
if change and getattr(f, 'auto_now_add', False):
- params[f.name] = getattr(self.original_object, f.name)
+ params[f.column] = getattr(self.original_object, f.name)
else:
- params[f.name] = f.get_manipulator_new_data(new_data)
+ params[f.column] = f.get_manipulator_new_data(new_data)
if change:
params[opts.pk.name] = self.obj_key
@@ -1416,7 +1471,7 @@ def manipulator_save(opts, klass, add, change, self, new_data):
if change:
self.fields_added, self.fields_changed, self.fields_deleted = [], [], []
for f in opts.fields:
- if not f.primary_key and str(getattr(self.original_object, f.name)) != str(getattr(new_object, f.name)):
+ if not f.primary_key and str(getattr(self.original_object, f.column)) != str(getattr(new_object, f.column)):
self.fields_changed.append(f.verbose_name)
# Save many-to-many objects. Example: Poll.set_sites()
@@ -1467,15 +1522,15 @@ def manipulator_save(opts, klass, add, change, self, new_data):
# case, because they'll be dealt with later.
if change and (isinstance(f, FileField) or not f.editable):
if rel_new_data.get(rel_opts.pk.name, False) and rel_new_data[rel_opts.pk.name][0]:
- params[f.name] = getattr(old_rel_obj, f.name)
+ params[f.column] = getattr(old_rel_obj, f.column)
else:
- params[f.name] = f.get_default()
+ params[f.column] = f.get_default()
elif f == rel_field:
- params[f.name] = getattr(new_object, rel_field.rel.field_name)
+ params[f.column] = getattr(new_object, rel_field.rel.field_name)
elif add and isinstance(f, AutoField):
- params[f.name] = None
+ params[f.column] = None
else:
- params[f.name] = f.get_manipulator_new_data(rel_new_data, rel=True)
+ params[f.column] = f.get_manipulator_new_data(rel_new_data, rel=True)
# Related links are a special case, because we have to
# manually set the "content_type_id" field.
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):
self.fields_added.append('%s "%r"' % (rel_opts.verbose_name, new_rel_obj))
else:
for f in rel_opts.fields:
- if not f.primary_key and f != rel_field and str(getattr(old_rel_obj, f.name)) != str(getattr(new_rel_obj, f.name)):
+ if not f.primary_key and f != rel_field and str(getattr(old_rel_obj, f.column)) != str(getattr(new_rel_obj, f.column)):
self.fields_changed.append('%s for %s "%r"' % (f.verbose_name, rel_opts.verbose_name, new_rel_obj))
# Save many-to-many objects.
@@ -1527,20 +1582,26 @@ def manipulator_save(opts, klass, add, change, self, new_data):
def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
from django.utils.text import get_text_list
field_list = [opts.get_field(field_name) for field_name in field_name_list]
- kwargs = {'%s__iexact' % field_name_list[0]: field_data}
+ if isinstance(field_list[0].rel, ManyToOne):
+ kwargs = {'%s__%s__iexact' % (field_name_list[0], field_list[0].rel.field_name): field_data}
+ else:
+ kwargs = {'%s__iexact' % field_name_list[0]: field_data}
for f in field_list[1:]:
- field_val = all_data.get(f.name, None)
+ field_val = all_data.get(f.column, None)
if field_val is None:
# This will be caught by another validator, assuming the field
# doesn't have blank=True.
return
- kwargs['%s__iexact' % f.name] = field_val
+ if isinstance(f.rel, ManyToOne):
+ kwargs['%s__pk' % f.name] = field_val
+ else:
+ kwargs['%s__iexact' % f.name] = field_val
mod = opts.get_model_module()
try:
old_obj = mod.get_object(**kwargs)
except ObjectDoesNotExist:
return
- if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
+ if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.column) == getattr(old_obj, opts.pk.column):
pass
else:
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
except ObjectDoesNotExist:
return
else:
- if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
+ if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.column) == getattr(old_obj, opts.pk.column):
pass
else:
format_string = (lookup_type == 'date') and '%B %d, %Y' or '%B %Y'
View
98 django/core/meta/fields.py
@@ -40,7 +40,7 @@ def manipulator_validator_unique(f, opts, self, field_data, all_data):
old_obj = opts.get_model_module().get_object(**{'%s__exact' % f.name: field_data})
except ObjectDoesNotExist:
return
- if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
+ if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.column) == getattr(old_obj, opts.pk.column):
return
raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name)
@@ -50,14 +50,17 @@ class Field(object):
# database level.
empty_strings_allowed = True
- def __init__(self, name, verbose_name=None, primary_key=False,
+ # Tracks each time a Field instance is created. Used to retain order.
+ creation_counter = 0
+
+ def __init__(self, verbose_name=None, name=None, primary_key=False,
maxlength=None, unique=False, blank=False, null=False, db_index=None,
core=False, rel=None, default=NOT_PROVIDED, editable=True,
prepopulate_from=None, unique_for_date=None, unique_for_month=None,
unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
- help_text=''):
+ help_text='', db_column=None):
self.name = name
- self.verbose_name = verbose_name or name.replace('_', ' ')
+ self.verbose_name = verbose_name or (name and name.replace('_', ' '))
self.primary_key = primary_key
self.maxlength, self.unique = maxlength, unique
self.blank, self.null = blank, null
@@ -70,6 +73,7 @@ def __init__(self, name, verbose_name=None, primary_key=False,
self.choices = choices or []
self.radio_admin = radio_admin
self.help_text = help_text
+ self.db_column = db_column
if rel and isinstance(rel, ManyToMany):
if rel.raw_id_admin:
self.help_text += ' Separate multiple IDs with commas.'
@@ -85,6 +89,27 @@ def __init__(self, name, verbose_name=None, primary_key=False,
else:
self.db_index = db_index
+ # Increase the creation counter, and save our local copy.
+ self.creation_counter = Field.creation_counter
+ Field.creation_counter += 1
+
+ # Set the name of the database column.
+ self.column = self.get_db_column()
+
+ def set_name(self, name):
+ self.name = name
+ self.verbose_name = self.verbose_name or name.replace('_', ' ')
+ self.column = self.get_db_column()
+
+ def get_db_column(self):
+ if self.db_column: return self.db_column
+ if isinstance(self.rel, ManyToOne):
+ return '%s_id' % self.name
+ return self.name
+
+ def get_cache_name(self):
+ return '_%s_cache' % self.name
+
def pre_save(self, value, add):
"Returns field's value just before saving."
return value
@@ -232,7 +257,7 @@ def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
if self.choices:
return first_choice + list(self.choices)
rel_obj = self.rel.to
- return first_choice + [(getattr(x, rel_obj.pk.name), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
+ return first_choice + [(getattr(x, rel_obj.pk.column), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
class AutoField(Field):
empty_strings_allowed = False
@@ -271,11 +296,11 @@ def get_manipulator_field_objs(self):
class DateField(Field):
empty_strings_allowed = False
- def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
+ def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
if auto_now or auto_now_add:
kwargs['editable'] = False
- Field.__init__(self, name, verbose_name, **kwargs)
+ Field.__init__(self, verbose_name, name, **kwargs)
def get_db_prep_lookup(self, lookup_type, value):
if lookup_type == 'range':
@@ -332,9 +357,9 @@ def get_manipulator_field_objs(self):
return [formfields.EmailField]
class FileField(Field):
- def __init__(self, name, verbose_name=None, upload_to='', **kwargs):
+ def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
- Field.__init__(self, name, verbose_name, **kwargs)
+ Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
@@ -397,17 +422,17 @@ def get_filename(self, filename):
class FloatField(Field):
empty_strings_allowed = False
- def __init__(self, name, verbose_name=None, max_digits=None, decimal_places=None, **kwargs):
+ def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
self.max_digits, self.decimal_places = max_digits, decimal_places
- Field.__init__(self, name, verbose_name, **kwargs)
+ Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [curry(formfields.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
class ImageField(FileField):
- def __init__(self, name, verbose_name=None, width_field=None, height_field=None, **kwargs):
+ def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
self.width_field, self.height_field = width_field, height_field
- FileField.__init__(self, name, verbose_name, **kwargs)
+ FileField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.ImageUploadField, formfields.HiddenField]
@@ -479,11 +504,11 @@ def get_manipulator_field_objs(self):
class TimeField(Field):
empty_strings_allowed = False
- def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
+ def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
if auto_now or auto_now_add:
kwargs['editable'] = False
- Field.__init__(self, name, verbose_name, **kwargs)
+ Field.__init__(self, verbose_name, name, **kwargs)
def get_db_prep_lookup(self, lookup_type, value):
if lookup_type == 'range':
@@ -511,10 +536,10 @@ def get_manipulator_field_objs(self):
return [formfields.TimeField]
class URLField(Field):
- def __init__(self, name, verbose_name=None, verify_exists=True, **kwargs):
+ def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
- Field.__init__(self, name, verbose_name, **kwargs)
+ Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.URLField]
@@ -524,34 +549,31 @@ def get_manipulator_field_objs(self):
return [formfields.USStateField]
class XMLField(Field):
- def __init__(self, name, verbose_name=None, schema_path=None, **kwargs):
+ def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
self.schema_path = schema_path
- Field.__init__(self, name, verbose_name, **kwargs)
+ Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [curry(formfields.XMLLargeTextField, schema_path=self.schema_path)]
class ForeignKey(Field):
empty_strings_allowed = False
- def __init__(self, to, to_field=None, rel_name=None, **kwargs):
+ def __init__(self, to, to_field=None, **kwargs):
try:
to_name = to._meta.object_name.lower()
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert to == 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT)
- kwargs['name'] = kwargs.get('name', '')
kwargs['verbose_name'] = kwargs.get('verbose_name', '')
else:
to_field = to_field or to._meta.pk.name
- kwargs['name'] = kwargs.get('name', to_name + '_id')
kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name)
- rel_name = rel_name or to_name
if kwargs.has_key('edit_inline_type'):
import warnings
warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
- kwargs['rel'] = ManyToOne(to, rel_name, to_field,
+ kwargs['rel'] = ManyToOne(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 3),
min_num_in_admin=kwargs.pop('min_num_in_admin', None),
max_num_in_admin=kwargs.pop('max_num_in_admin', None),
@@ -567,11 +589,9 @@ def get_manipulator_field_objs(self):
return [formfields.IntegerField]
class ManyToManyField(Field):
- def __init__(self, to, rel_name=None, **kwargs):
- kwargs['name'] = kwargs.get('name', to._meta.module_name)
+ def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural)
- rel_name = rel_name or to._meta.object_name.lower()
- kwargs['rel'] = ManyToMany(to, rel_name,
+ kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None),
num_in_admin=kwargs.pop('num_in_admin', 0),
related_name=kwargs.pop('related_name', None),
filter_interface=kwargs.pop('filter_interface', None),
@@ -609,18 +629,16 @@ def isValidIDList(self, field_data, all_data):
len(badkeys) == 1 and "is" or "are")
class OneToOneField(IntegerField):
- def __init__(self, to, to_field=None, rel_name=None, **kwargs):
- kwargs['name'] = kwargs.get('name', 'id')
+ def __init__(self, to, to_field=None, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
to_field = to_field or to._meta.pk.name
- rel_name = rel_name or to._meta.object_name.lower()
if kwargs.has_key('edit_inline_type'):
import warnings
warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
- kwargs['rel'] = OneToOne(to, rel_name, to_field,
+ kwargs['rel'] = OneToOne(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 0),
edit_inline=kwargs.pop('edit_inline', False),
related_name=kwargs.pop('related_name', None),
@@ -631,7 +649,7 @@ def __init__(self, to, to_field=None, rel_name=None, **kwargs):
IntegerField.__init__(self, **kwargs)
class ManyToOne:
- def __init__(self, to, name, field_name, num_in_admin=3, min_num_in_admin=None,
+ def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
try:
@@ -639,7 +657,7 @@ def __init__(self, to, name, field_name, num_in_admin=3, min_num_in_admin=None,
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert to == RECURSIVE_RELATIONSHIP_CONSTANT, "'to' must be either a model or the string '%s'" % RECURSIVE_RELATIONSHIP_CONSTANT
self.to = to
- self.name, self.field_name = name, field_name
+ self.field_name = field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
@@ -647,17 +665,15 @@ def __init__(self, to, name, field_name, num_in_admin=3, min_num_in_admin=None,
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
- def get_cache_name(self):
- return '_%s_cache' % self.name
-
def get_related_field(self):
"Returns the Field in the 'to' object to which this relationship is tied."
return self.to.get_field(self.field_name)
class ManyToMany:
- def __init__(self, to, name, num_in_admin=0, related_name=None,
+ def __init__(self, to, singular=None, num_in_admin=0, related_name=None,
filter_interface=None, limit_choices_to=None, raw_id_admin=False):
- self.to, self.name = to._meta, name
+ self.to = to._meta
+ self.singular = singular or to._meta.object_name.lower()
self.num_in_admin = num_in_admin
self.related_name = related_name
self.filter_interface = filter_interface
@@ -667,10 +683,10 @@ def __init__(self, to, name, num_in_admin=0, related_name=None,
assert not (self.raw_id_admin and self.filter_interface), "ManyToMany relationships may not use both raw_id_admin and filter_interface"
class OneToOne(ManyToOne):
- def __init__(self, to, name, field_name, num_in_admin=0, edit_inline=False,
+ def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False):
- self.to, self.name, self.field_name = to._meta, name, field_name
+ self.to, self.field_name = to._meta, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.related_name = related_name
self.limit_choices_to = limit_choices_to or {}
View
136 django/models/auth.py
@@ -2,65 +2,60 @@
from django.models import core
class Permission(meta.Model):
- fields = (
- meta.CharField('name', maxlength=50),
- meta.ForeignKey(core.Package, name='package'),
- meta.CharField('codename', maxlength=100),
- )
- unique_together = (('package', 'codename'),)
- ordering = ('package', 'codename')
+ name = meta.CharField(maxlength=50)
+ package = meta.ForeignKey(core.Package, db_column='package')
+ codename = meta.CharField(maxlength=100)
+ class META:
+ unique_together = (('package', 'codename'),)
+ ordering = ('package', 'codename')
def __repr__(self):
return "%s | %s" % (self.package, self.name)
class Group(meta.Model):
- fields = (
- meta.CharField('name', maxlength=80, unique=True),
- meta.ManyToManyField(Permission, blank=True, filter_interface=meta.HORIZONTAL),
- )
- ordering = ('name',)
- admin = meta.Admin(
- search_fields = ('name',),
- )
+ name = meta.CharField(maxlength=80, unique=True)
+ permissions = meta.ManyToManyField(Permission, blank=True, filter_interface=meta.HORIZONTAL)
+ class META:
+ ordering = ('name',)
+ admin = meta.Admin(
+ search_fields = ('name',),
+ )
def __repr__(self):
return self.name
class User(meta.Model):
- fields = (
- meta.CharField('username', maxlength=30, unique=True,
- validator_list=[validators.isAlphaNumeric]),
- meta.CharField('first_name', maxlength=30, blank=True),
- meta.CharField('last_name', maxlength=30, blank=True),
- meta.EmailField('email', 'e-mail address', blank=True),
- meta.CharField('password_md5', 'password', maxlength=32, help_text="Use an MD5 hash -- not the raw password."),
- meta.BooleanField('is_staff', 'staff status',
- help_text="Designates whether the user can log into this admin site."),
- meta.BooleanField('is_active', 'active', default=True),
- meta.BooleanField('is_superuser', 'superuser status'),
- meta.DateTimeField('last_login', default=meta.LazyDate()),
- meta.DateTimeField('date_joined', default=meta.LazyDate()),
- meta.ManyToManyField(Group, blank=True,
- help_text="In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."),
- meta.ManyToManyField(Permission, name='user_permissions', blank=True, filter_interface=meta.HORIZONTAL),
- )
- module_constants = {
- 'SESSION_KEY': '_auth_user_id',
- }
- ordering = ('username',)
- exceptions = ('SiteProfileNotAvailable',)
- admin = meta.Admin(
- fields = (
- (None, {'fields': ('username', 'password_md5')}),
- ('Personal info', {'fields': ('first_name', 'last_name', 'email')}),
- ('Permissions', {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
- ('Important dates', {'fields': ('last_login', 'date_joined')}),
- ('Groups', {'fields': ('groups',)}),
- ),
- list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff'),
- list_filter = ('is_staff', 'is_superuser'),
- search_fields = ('username', 'first_name', 'last_name', 'email'),
- )
+ username = meta.CharField(maxlength=30, unique=True, validator_list=[validators.isAlphaNumeric])
+ first_name = meta.CharField(maxlength=30, blank=True)
+ last_name = meta.CharField(maxlength=30, blank=True)
+ email = meta.EmailField('e-mail address', blank=True)
+ password_md5 = meta.CharField('password', maxlength=32, help_text="Use an MD5 hash -- not the raw password.")
+ is_staff = meta.BooleanField('staff status', help_text="Designates whether the user can log into this admin site.")
+ is_active = meta.BooleanField('active', default=True)
+ is_superuser = meta.BooleanField('superuser status')
+ last_login = meta.DateTimeField(default=meta.LazyDate())
+ date_joined = meta.DateTimeField(default=meta.LazyDate())
+ groups = meta.ManyToManyField(Group, blank=True,
+ help_text="In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in.")
+ user_permissions = meta.ManyToManyField(Permission, blank=True, filter_interface=meta.HORIZONTAL)
+ class META:
+ module_constants = {
+ 'SESSION_KEY': '_auth_user_id',
+ }
+ ordering = ('username',)
+ exceptions = ('SiteProfileNotAvailable',)
+ admin = meta.Admin(
+ fields = (
+ (None, {'fields': ('username', 'password_md5')}),
+ ('Personal info', {'fields': ('first_name', 'last_name', 'email')}),
+ ('Permissions', {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
+ ('Important dates', {'fields': ('last_login', 'date_joined')}),
+ ('Groups', {'fields': ('groups',)}),
+ ),
+ list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff'),
+ list_filter = ('is_staff', 'is_superuser'),
+ search_fields = ('username', 'first_name', 'last_name', 'email'),
+ )
def __repr__(self):
return self.username
@@ -154,7 +149,7 @@ def get_profile(self):
except ImportError:
try:
module = __import__('django.models.%s' % AUTH_PROFILE_MODULE, [], [], [''])
- self._profile_cache = module.get_object(user_id__exact=self.id)
+ self._profile_cache = module.get_object(user__id__exact=self.id)
except ImportError:
raise SiteProfileNotAvailable
return self._profile_cache
@@ -176,33 +171,30 @@ def _module_make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwx
return ''.join([choice(allowed_chars) for i in range(length)])
class Message(meta.Model):
- fields = (
- meta.ForeignKey(User),
- meta.TextField('message'),
- )
+ user = meta.ForeignKey(User)
+ message = meta.TextField()
def __repr__(self):
return self.message
class LogEntry(meta.Model):
- module_name = 'log'
- verbose_name_plural = 'log entries'
- db_table = 'auth_admin_log'
- fields = (
- meta.DateTimeField('action_time', auto_now=True),
- meta.ForeignKey(User),
- meta.ForeignKey(core.ContentType, name='content_type_id', rel_name='content_type', blank=True, null=True),
- meta.TextField('object_id', blank=True, null=True),
- meta.CharField('object_repr', maxlength=200),
- meta.PositiveSmallIntegerField('action_flag'),
- meta.TextField('change_message', blank=True),
- )
- ordering = ('-action_time',)
- module_constants = {
- 'ADDITION': 1,
- 'CHANGE': 2,
- 'DELETION': 3,
- }
+ action_time = meta.DateTimeField(auto_now=True)
+ user = meta.ForeignKey(User)
+ content_type = meta.ForeignKey(core.ContentType, blank=True, null=True) # TODO: content_type_id name?
+ object_id = meta.TextField(blank=True, null=True)
+ object_repr = meta.CharField(maxlength=200)
+ action_flag = meta.PositiveSmallIntegerField()
+ change_message = meta.TextField(blank=True)
+ class META:
+ module_name = 'log'
+ verbose_name_plural = 'log entries'
+ db_table = 'auth_admin_log'
+ ordering = ('-action_time',)
+ module_constants = {
+ 'ADDITION': 1,
+ 'CHANGE': 2,
+ 'DELETION': 3,
+ }
def __repr__(self):
return str(self.action_time)
View
132 django/models/core.py
@@ -1,12 +1,11 @@
from django.core import meta, validators
class Site(meta.Model):
- db_table = 'sites'
- fields = (
- meta.CharField('domain', 'domain name', maxlength=100),
- meta.CharField('name', 'display name', maxlength=50),
- )
- ordering = ('domain',)
+ domain = meta.CharField('domain name', maxlength=100)
+ name = meta.CharField('display name', maxlength=50)
+ class META:
+ db_table = 'sites'
+ ordering = ('domain',)
def __repr__(self):
return self.domain
@@ -17,25 +16,23 @@ def _module_get_current():
return get_object(pk=SITE_ID)
class Package(meta.Model):
- db_table = 'packages'
- fields = (
- meta.CharField('label', maxlength=20, primary_key=True),
- meta.CharField('name', maxlength=30, unique=True),
- )
- ordering = ('name',)
+ label = meta.CharField(maxlength=20, primary_key=True)
+ name = meta.CharField(maxlength=30, unique=True)
+ class META:
+ db_table = 'packages'
+ ordering = ('name',)
def __repr__(self):
return self.name
class ContentType(meta.Model):
- db_table = 'content_types'
- fields = (
- meta.CharField('name', maxlength=100),
- meta.ForeignKey(Package, name='package'),
- meta.CharField('python_module_name', maxlength=50),
- )
- ordering = ('package', 'name')
- unique_together = (('package', 'python_module_name'),)
+ name = meta.CharField(maxlength=100)
+ package = meta.ForeignKey(Package, db_column='package')
+ python_module_name = meta.CharField(maxlength=50)
+ class META:
+ db_table = 'content_types'
+ ordering = ('package', 'name')
+ unique_together = (('package', 'python_module_name'),)
def __repr__(self):
return "%s | %s" % (self.package, self.name)
@@ -54,49 +51,45 @@ def get_object_for_this_type(self, **kwargs):
return self.get_model_module().get_object(**kwargs)
class Redirect(meta.Model):
- db_table = 'redirects'
- fields = (
- meta.ForeignKey(Site, radio_admin=meta.VERTICAL),
- meta.CharField('old_path', 'redirect from', maxlength=200, db_index=True,
- help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'."),
- meta.CharField('new_path', 'redirect to', maxlength=200, blank=True,
- help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'."),
- )
- unique_together=(('site_id', 'old_path'),)
- ordering = ('old_path',)
- admin = meta.Admin(
- list_display = ('__repr__',),
- list_filter = ('site_id',),
- search_fields = ('old_path', 'new_path'),
- )
+ site = meta.ForeignKey(Site, radio_admin=meta.VERTICAL)
+ old_path = meta.CharField('redirect from', maxlength=200, db_index=True,
+ help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.")
+ new_path = meta.CharField('redirect to', maxlength=200, blank=True,
+ help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'.")
+ class META:
+ db_table = 'redirects'
+ unique_together=(('site', 'old_path'),)
+ ordering = ('old_path',)
+ admin = meta.Admin(
+ list_filter = ('site',),
+ search_fields = ('old_path', 'new_path'),
+ )
def __repr__(self):
return "%s ---> %s" % (self.old_path, self.new_path)
class FlatFile(meta.Model):
- db_table = 'flatfiles'
- verbose_name = 'flat page'
- fields = (
- meta.CharField('url', 'URL', maxlength=100, validator_list=[validators.isAlphaNumericURL],
- help_text="Example: '/about/contact/'. Make sure to have leading and trailing slashes."),
- meta.CharField('title', maxlength=200),
- meta.TextField('content', help_text="Full HTML is allowed."),
- meta.BooleanField('enable_comments'),
- meta.CharField('template_name', maxlength=70, blank=True,
- help_text="Example: 'flatfiles/contact_page'. If this isn't provided, the system will use 'flatfiles/default'."),
- meta.BooleanField('registration_required',
- help_text="If this is checked, only logged-in users will be able to view the page."),
- meta.ManyToManyField(Site),
- )
- ordering = ('url',)
- admin = meta.Admin(
- fields = (
- (None, {'fields': ('url', 'title', 'content', 'sites')}),
- ('Advanced options', {'classes': 'collapse', 'fields': ('enable_comments', 'registration_required', 'template_name')}),
- ),
- list_filter = ('sites',),
- search_fields = ('url', 'title'),
- )
+ url = meta.CharField('URL', maxlength=100, validator_list=[validators.isAlphaNumericURL],
+ help_text="Example: '/about/contact/'. Make sure to have leading and trailing slashes.")
+ title = meta.CharField(maxlength=200)
+ content = meta.TextField()
+ enable_comments = meta.BooleanField()
+ template_name = meta.CharField(maxlength=70, blank=True,
+ help_text="Example: 'flatfiles/contact_page'. If this isn't provided, the system will use 'flatfiles/default'.")
+ registration_required = meta.BooleanField(help_text="If this is checked, only logged-in users will be able to view the page.")
+ sites = meta.ManyToManyField(Site)
+ class META:
+ db_table = 'flatfiles'
+ verbose_name = 'flat page'
+ ordering = ('url',)
+ admin = meta.Admin(
+ fields = (
+ (None, {'fields': ('url', 'title', 'content', 'sites')}),
+ ('Advanced options', {'classes': 'collapse', 'fields': ('enable_comments', 'registration_required', 'template_name')}),
+ ),
+ list_filter = ('sites',),
+ search_fields = ('url', 'title'),
+ )
def __repr__(self):
return "%s -- %s" % (self.url, self.title)
@@ -108,18 +101,17 @@ def get_absolute_url(self):
import cPickle as pickle
class Session(meta.Model):
- fields = (
- meta.CharField('session_key', maxlength=40, primary_key=True),
- meta.TextField('session_data'),
- meta.DateTimeField('expire_date'),
- )
- module_constants = {
- 'base64': base64,
- 'md5': md5,
- 'pickle': pickle,
- 'random': random,
- 'sys': sys,
- }
+ session_key = meta.CharField(maxlength=40, primary_key=True)
+ session_data = meta.TextField()
+ expire_date = meta.DateTimeField()
+ class META:
+ module_constants = {
+ 'base64': base64,
+ 'md5': md5,
+ 'pickle': pickle,
+ 'random': random,
+ 'sys': sys,
+ }
def get_decoded(self):
from django.conf.settings import SECRET_KEY
View
12 django/templatetags/log.py
@@ -11,23 +11,23 @@ def __repr__(self):
def render(self, context):
if self.user is not None and not self.user.isdigit():
self.user = context[self.user].id
- context[self.varname] = log.get_list(user_id__exact=self.user, limit=self.limit, select_related=True)
+ context[self.varname] = log.get_list(user__id__exact=self.user, limit=self.limit, select_related=True)
return ''
class DoGetAdminLog:
"""
Populates a template variable with the admin log for the given criteria.
-
+
Usage::
-
+
{% get_admin_log [limit] as [varname] for_user [context_var_containing_user_obj] %}
-
+
Examples::
-
+
{% get_admin_log 10 as admin_log for_user 23 %}
{% get_admin_log 10 as admin_log for_user user %}
{% get_admin_log 10 as admin_log %}
-
+
Note that ``context_var_containing_user_obj`` can be a hard-coded integer
(user ID) or the name of a template context variable containing the user
object whose ID you want.
View
20 django/views/admin/main.py
@@ -387,12 +387,12 @@ def change_list(request, app_label, module_name):
except ObjectDoesNotExist:
result_repr = EMPTY_CHANGELIST_VALUE
else:
- field_val = getattr(result, f.name)
+ field_val = getattr(result, f.column)
# Foreign-key fields are special: Use the repr of the
# related object.
if isinstance(f.rel, meta.ManyToOne):
if field_val is not None:
- result_repr = getattr(result, 'get_%s' % f.rel.name)()
+ result_repr = getattr(result, 'get_%s' % f.name)()
else:
result_repr = EMPTY_CHANGELIST_VALUE
# Dates are special: They're formatted in a certain way.
@@ -723,10 +723,10 @@ def _get_admin_field(field_list, name_prefix, rel, add, change):
t.append('{{ %soriginal.%s }}' % ((rel and name_prefix or ''), field.name))
if change and use_raw_id_admin(field):
if isinstance(field.rel, meta.ManyToOne):
- if_bit = '%soriginal.get_%s' % (rel and name_prefix or '', field.rel.name)
+ if_bit = '%soriginal.get_%s' % (rel and name_prefix <