Added model Meta option for swappable models, and made auth.User a swappable model
This commit is contained in:
parent
f29234167a
commit
7cc0baf89d
|
@ -488,6 +488,8 @@ PROFANITIES_LIST = ()
|
||||||
# AUTHENTICATION #
|
# AUTHENTICATION #
|
||||||
##################
|
##################
|
||||||
|
|
||||||
|
AUTH_USER_MODEL = 'auth.User'
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
|
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
|
||||||
|
|
||||||
LOGIN_URL = '/accounts/login/'
|
LOGIN_URL = '/accounts/login/'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.admin.util import quote
|
from django.contrib.admin.util import quote
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
|
@ -10,14 +10,16 @@ ADDITION = 1
|
||||||
CHANGE = 2
|
CHANGE = 2
|
||||||
DELETION = 3
|
DELETION = 3
|
||||||
|
|
||||||
|
|
||||||
class LogEntryManager(models.Manager):
|
class LogEntryManager(models.Manager):
|
||||||
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
|
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
|
||||||
e = self.model(None, None, user_id, content_type_id, smart_unicode(object_id), object_repr[:200], action_flag, change_message)
|
e = self.model(None, None, user_id, content_type_id, smart_unicode(object_id), object_repr[:200], action_flag, change_message)
|
||||||
e.save()
|
e.save()
|
||||||
|
|
||||||
|
|
||||||
class LogEntry(models.Model):
|
class LogEntry(models.Model):
|
||||||
action_time = models.DateTimeField(_('action time'), auto_now=True)
|
action_time = models.DateTimeField(_('action time'), auto_now=True)
|
||||||
user = models.ForeignKey(User)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||||
content_type = models.ForeignKey(ContentType, blank=True, null=True)
|
content_type = models.ForeignKey(ContentType, blank=True, null=True)
|
||||||
object_id = models.TextField(_('object id'), blank=True, null=True)
|
object_id = models.TextField(_('object id'), blank=True, null=True)
|
||||||
object_repr = models.CharField(_('object repr'), max_length=200)
|
object_repr = models.CharField(_('object repr'), max_length=200)
|
||||||
|
|
|
@ -93,6 +93,7 @@ class GroupManager(models.Manager):
|
||||||
def get_by_natural_key(self, name):
|
def get_by_natural_key(self, name):
|
||||||
return self.get(name=name)
|
return self.get(name=name)
|
||||||
|
|
||||||
|
|
||||||
class Group(models.Model):
|
class Group(models.Model):
|
||||||
"""
|
"""
|
||||||
Groups are a generic way of categorizing users to apply permissions, or
|
Groups are a generic way of categorizing users to apply permissions, or
|
||||||
|
@ -197,8 +198,6 @@ def _user_get_all_permissions(user, obj):
|
||||||
|
|
||||||
|
|
||||||
def _user_has_perm(user, perm, obj):
|
def _user_has_perm(user, perm, obj):
|
||||||
anon = user.is_anonymous()
|
|
||||||
active = user.is_active
|
|
||||||
for backend in auth.get_backends():
|
for backend in auth.get_backends():
|
||||||
if hasattr(backend, "has_perm"):
|
if hasattr(backend, "has_perm"):
|
||||||
if obj is not None:
|
if obj is not None:
|
||||||
|
@ -211,8 +210,6 @@ def _user_has_perm(user, perm, obj):
|
||||||
|
|
||||||
|
|
||||||
def _user_has_module_perms(user, app_label):
|
def _user_has_module_perms(user, app_label):
|
||||||
anon = user.is_anonymous()
|
|
||||||
active = user.is_active
|
|
||||||
for backend in auth.get_backends():
|
for backend in auth.get_backends():
|
||||||
if hasattr(backend, "has_module_perms"):
|
if hasattr(backend, "has_module_perms"):
|
||||||
if backend.has_module_perms(user, app_label):
|
if backend.has_module_perms(user, app_label):
|
||||||
|
@ -220,7 +217,54 @@ def _user_has_module_perms(user, app_label):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class User(models.Model):
|
class AbstractBaseUser(models.Model):
|
||||||
|
password = models.CharField(_('password'), max_length=128)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def is_anonymous(self):
|
||||||
|
"""
|
||||||
|
Always returns False. This is a way of comparing User objects to
|
||||||
|
anonymous users.
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_authenticated(self):
|
||||||
|
"""
|
||||||
|
Always return True. This is a way to tell if the user has been
|
||||||
|
authenticated in templates.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_password(self, raw_password):
|
||||||
|
self.password = make_password(raw_password)
|
||||||
|
|
||||||
|
def check_password(self, raw_password):
|
||||||
|
"""
|
||||||
|
Returns a boolean of whether the raw_password was correct. Handles
|
||||||
|
hashing formats behind the scenes.
|
||||||
|
"""
|
||||||
|
def setter(raw_password):
|
||||||
|
self.set_password(raw_password)
|
||||||
|
self.save()
|
||||||
|
return check_password(raw_password, self.password, setter)
|
||||||
|
|
||||||
|
def set_unusable_password(self):
|
||||||
|
# Sets a value that will never be a valid hash
|
||||||
|
self.password = make_password(None)
|
||||||
|
|
||||||
|
def has_usable_password(self):
|
||||||
|
return is_password_usable(self.password)
|
||||||
|
|
||||||
|
def get_full_name(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_short_name(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class User(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
Users within the Django authentication system are represented by this
|
Users within the Django authentication system are represented by this
|
||||||
model.
|
model.
|
||||||
|
@ -233,7 +277,6 @@ class User(models.Model):
|
||||||
first_name = models.CharField(_('first name'), max_length=30, blank=True)
|
first_name = models.CharField(_('first name'), max_length=30, blank=True)
|
||||||
last_name = models.CharField(_('last name'), max_length=30, blank=True)
|
last_name = models.CharField(_('last name'), max_length=30, blank=True)
|
||||||
email = models.EmailField(_('e-mail address'), blank=True)
|
email = models.EmailField(_('e-mail address'), blank=True)
|
||||||
password = models.CharField(_('password'), max_length=128)
|
|
||||||
is_staff = models.BooleanField(_('staff status'), default=False,
|
is_staff = models.BooleanField(_('staff status'), default=False,
|
||||||
help_text=_('Designates whether the user can log into this admin '
|
help_text=_('Designates whether the user can log into this admin '
|
||||||
'site.'))
|
'site.'))
|
||||||
|
@ -257,6 +300,7 @@ class User(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('user')
|
verbose_name = _('user')
|
||||||
verbose_name_plural = _('users')
|
verbose_name_plural = _('users')
|
||||||
|
swappable = 'AUTH_USER_MODEL'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
@ -267,20 +311,6 @@ class User(models.Model):
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return "/users/%s/" % urllib.quote(smart_str(self.username))
|
return "/users/%s/" % urllib.quote(smart_str(self.username))
|
||||||
|
|
||||||
def is_anonymous(self):
|
|
||||||
"""
|
|
||||||
Always returns False. This is a way of comparing User objects to
|
|
||||||
anonymous users.
|
|
||||||
"""
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_authenticated(self):
|
|
||||||
"""
|
|
||||||
Always return True. This is a way to tell if the user has been
|
|
||||||
authenticated in templates.
|
|
||||||
"""
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
"""
|
"""
|
||||||
Returns the first_name plus the last_name, with a space in between.
|
Returns the first_name plus the last_name, with a space in between.
|
||||||
|
@ -288,26 +318,6 @@ class User(models.Model):
|
||||||
full_name = u'%s %s' % (self.first_name, self.last_name)
|
full_name = u'%s %s' % (self.first_name, self.last_name)
|
||||||
return full_name.strip()
|
return full_name.strip()
|
||||||
|
|
||||||
def set_password(self, raw_password):
|
|
||||||
self.password = make_password(raw_password)
|
|
||||||
|
|
||||||
def check_password(self, raw_password):
|
|
||||||
"""
|
|
||||||
Returns a boolean of whether the raw_password was correct. Handles
|
|
||||||
hashing formats behind the scenes.
|
|
||||||
"""
|
|
||||||
def setter(raw_password):
|
|
||||||
self.set_password(raw_password)
|
|
||||||
self.save()
|
|
||||||
return check_password(raw_password, self.password, setter)
|
|
||||||
|
|
||||||
def set_unusable_password(self):
|
|
||||||
# Sets a value that will never be a valid hash
|
|
||||||
self.password = make_password(None)
|
|
||||||
|
|
||||||
def has_usable_password(self):
|
|
||||||
return is_password_usable(self.password)
|
|
||||||
|
|
||||||
def get_group_permissions(self, obj=None):
|
def get_group_permissions(self, obj=None):
|
||||||
"""
|
"""
|
||||||
Returns a list of permission strings that this user has through his/her
|
Returns a list of permission strings that this user has through his/her
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
from django.contrib.auth.models import User
|
from django.conf import settings
|
||||||
from django.contrib.comments.managers import CommentManager
|
from django.contrib.comments.managers import CommentManager
|
||||||
from django.contrib.contenttypes import generic
|
from django.contrib.contenttypes import generic
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.db import models
|
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
COMMENT_MAX_LENGTH = getattr(settings,'COMMENT_MAX_LENGTH',3000)
|
COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)
|
||||||
|
|
||||||
|
|
||||||
class BaseCommentAbstractModel(models.Model):
|
class BaseCommentAbstractModel(models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -39,6 +39,7 @@ class BaseCommentAbstractModel(models.Model):
|
||||||
args=(self.content_type_id, self.object_pk)
|
args=(self.content_type_id, self.object_pk)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Comment(BaseCommentAbstractModel):
|
class Comment(BaseCommentAbstractModel):
|
||||||
"""
|
"""
|
||||||
A user comment about some object.
|
A user comment about some object.
|
||||||
|
@ -47,7 +48,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
# Who posted this comment? If ``user`` is set then it was an authenticated
|
# Who posted this comment? If ``user`` is set then it was an authenticated
|
||||||
# user; otherwise at least user_name should have been set and the comment
|
# user; otherwise at least user_name should have been set and the comment
|
||||||
# was posted by a non-authenticated user.
|
# was posted by a non-authenticated user.
|
||||||
user = models.ForeignKey(User, verbose_name=_('user'),
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
|
||||||
blank=True, null=True, related_name="%(class)s_comments")
|
blank=True, null=True, related_name="%(class)s_comments")
|
||||||
user_name = models.CharField(_("user's name"), max_length=50, blank=True)
|
user_name = models.CharField(_("user's name"), max_length=50, blank=True)
|
||||||
user_email = models.EmailField(_("user's email address"), blank=True)
|
user_email = models.EmailField(_("user's email address"), blank=True)
|
||||||
|
@ -115,6 +116,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
|
|
||||||
def _get_name(self):
|
def _get_name(self):
|
||||||
return self.userinfo["name"]
|
return self.userinfo["name"]
|
||||||
|
|
||||||
def _set_name(self, val):
|
def _set_name(self, val):
|
||||||
if self.user_id:
|
if self.user_id:
|
||||||
raise AttributeError(_("This comment was posted by an authenticated "\
|
raise AttributeError(_("This comment was posted by an authenticated "\
|
||||||
|
@ -124,6 +126,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
|
|
||||||
def _get_email(self):
|
def _get_email(self):
|
||||||
return self.userinfo["email"]
|
return self.userinfo["email"]
|
||||||
|
|
||||||
def _set_email(self, val):
|
def _set_email(self, val):
|
||||||
if self.user_id:
|
if self.user_id:
|
||||||
raise AttributeError(_("This comment was posted by an authenticated "\
|
raise AttributeError(_("This comment was posted by an authenticated "\
|
||||||
|
@ -133,6 +136,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
|
|
||||||
def _get_url(self):
|
def _get_url(self):
|
||||||
return self.userinfo["url"]
|
return self.userinfo["url"]
|
||||||
|
|
||||||
def _set_url(self, val):
|
def _set_url(self, val):
|
||||||
self.user_url = val
|
self.user_url = val
|
||||||
url = property(_get_url, _set_url, doc="The URL given by the user who posted this comment")
|
url = property(_get_url, _set_url, doc="The URL given by the user who posted this comment")
|
||||||
|
@ -153,6 +157,7 @@ class Comment(BaseCommentAbstractModel):
|
||||||
}
|
}
|
||||||
return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d
|
return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d
|
||||||
|
|
||||||
|
|
||||||
class CommentFlag(models.Model):
|
class CommentFlag(models.Model):
|
||||||
"""
|
"""
|
||||||
Records a flag on a comment. This is intentionally flexible; right now, a
|
Records a flag on a comment. This is intentionally flexible; right now, a
|
||||||
|
@ -166,7 +171,7 @@ class CommentFlag(models.Model):
|
||||||
design users are only allowed to flag a comment with a given flag once;
|
design users are only allowed to flag a comment with a given flag once;
|
||||||
if you want rating look elsewhere.
|
if you want rating look elsewhere.
|
||||||
"""
|
"""
|
||||||
user = models.ForeignKey(User, verbose_name=_('user'), related_name="comment_flags")
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), related_name="comment_flags")
|
||||||
comment = models.ForeignKey(Comment, verbose_name=_('comment'), related_name="flags")
|
comment = models.ForeignKey(Comment, verbose_name=_('comment'), related_name="flags")
|
||||||
flag = models.CharField(_('flag'), max_length=30, db_index=True)
|
flag = models.CharField(_('flag'), max_length=30, db_index=True)
|
||||||
flag_date = models.DateTimeField(_('date'), default=None)
|
flag_date = models.DateTimeField(_('date'), default=None)
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.core.management.base import AppCommand
|
||||||
from django.core.management.sql import sql_all
|
from django.core.management.sql import sql_all
|
||||||
from django.db import connections, DEFAULT_DB_ALIAS
|
from django.db import connections, DEFAULT_DB_ALIAS
|
||||||
|
|
||||||
|
|
||||||
class Command(AppCommand):
|
class Command(AppCommand):
|
||||||
help = "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s)."
|
help = "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s)."
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ class Command(NoArgsCommand):
|
||||||
if router.allow_syncdb(db, m)])
|
if router.allow_syncdb(db, m)])
|
||||||
for app in models.get_apps()
|
for app in models.get_apps()
|
||||||
]
|
]
|
||||||
|
|
||||||
def model_installed(model):
|
def model_installed(model):
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
converter = connection.introspection.table_name_converter
|
converter = connection.introspection.table_name_converter
|
||||||
|
@ -101,7 +102,6 @@ class Command(NoArgsCommand):
|
||||||
cursor.execute(statement)
|
cursor.execute(statement)
|
||||||
tables.append(connection.introspection.table_name_converter(model._meta.db_table))
|
tables.append(connection.introspection.table_name_converter(model._meta.db_table))
|
||||||
|
|
||||||
|
|
||||||
transaction.commit_unless_managed(using=db)
|
transaction.commit_unless_managed(using=db)
|
||||||
|
|
||||||
# Send the post_syncdb signal, so individual apps can do whatever they need
|
# Send the post_syncdb signal, so individual apps can do whatever they need
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = "Validates all installed models."
|
help = "Validates all installed models."
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.core.management.base import CommandError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import get_models
|
from django.db.models import get_models
|
||||||
|
|
||||||
|
|
||||||
def sql_create(app, style, connection):
|
def sql_create(app, style, connection):
|
||||||
"Returns a list of the CREATE TABLE SQL statements for the given app."
|
"Returns a list of the CREATE TABLE SQL statements for the given app."
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ def sql_create(app, style, connection):
|
||||||
|
|
||||||
return final_output
|
return final_output
|
||||||
|
|
||||||
|
|
||||||
def sql_delete(app, style, connection):
|
def sql_delete(app, style, connection):
|
||||||
"Returns a list of the DROP TABLE SQL statements for the given app."
|
"Returns a list of the DROP TABLE SQL statements for the given app."
|
||||||
|
|
||||||
|
@ -80,7 +82,7 @@ def sql_delete(app, style, connection):
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
for f in opts.local_fields:
|
for f in opts.local_fields:
|
||||||
if f.rel and f.rel.to not in to_delete:
|
if f.rel and f.rel.to not in to_delete:
|
||||||
references_to_delete.setdefault(f.rel.to, []).append( (model, f) )
|
references_to_delete.setdefault(f.rel.to, []).append((model, f))
|
||||||
|
|
||||||
to_delete.add(model)
|
to_delete.add(model)
|
||||||
|
|
||||||
|
@ -96,6 +98,7 @@ def sql_delete(app, style, connection):
|
||||||
|
|
||||||
return output[::-1] # Reverse it, to deal with table dependencies.
|
return output[::-1] # Reverse it, to deal with table dependencies.
|
||||||
|
|
||||||
|
|
||||||
def sql_flush(style, connection, only_django=False):
|
def sql_flush(style, connection, only_django=False):
|
||||||
"""
|
"""
|
||||||
Returns a list of the SQL statements used to flush the database.
|
Returns a list of the SQL statements used to flush the database.
|
||||||
|
@ -112,6 +115,7 @@ def sql_flush(style, connection, only_django=False):
|
||||||
)
|
)
|
||||||
return statements
|
return statements
|
||||||
|
|
||||||
|
|
||||||
def sql_custom(app, style, connection):
|
def sql_custom(app, style, connection):
|
||||||
"Returns a list of the custom table modifying SQL statements for the given app."
|
"Returns a list of the custom table modifying SQL statements for the given app."
|
||||||
output = []
|
output = []
|
||||||
|
@ -123,6 +127,7 @@ def sql_custom(app, style, connection):
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def sql_indexes(app, style, connection):
|
def sql_indexes(app, style, connection):
|
||||||
"Returns a list of the CREATE INDEX SQL statements for all models in the given app."
|
"Returns a list of the CREATE INDEX SQL statements for all models in the given app."
|
||||||
output = []
|
output = []
|
||||||
|
@ -130,10 +135,12 @@ def sql_indexes(app, style, connection):
|
||||||
output.extend(connection.creation.sql_indexes_for_model(model, style))
|
output.extend(connection.creation.sql_indexes_for_model(model, style))
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def sql_all(app, style, connection):
|
def sql_all(app, style, connection):
|
||||||
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
|
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
|
||||||
return sql_create(app, style, connection) + sql_custom(app, style, connection) + sql_indexes(app, style, connection)
|
return sql_create(app, style, connection) + sql_custom(app, style, connection) + sql_indexes(app, style, connection)
|
||||||
|
|
||||||
|
|
||||||
def custom_sql_for_model(model, style, connection):
|
def custom_sql_for_model(model, style, connection):
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
|
app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
|
||||||
|
|
|
@ -3,6 +3,7 @@ import sys
|
||||||
from django.core.management.color import color_style
|
from django.core.management.color import color_style
|
||||||
from django.utils.itercompat import is_iterable
|
from django.utils.itercompat import is_iterable
|
||||||
|
|
||||||
|
|
||||||
class ModelErrorCollection:
|
class ModelErrorCollection:
|
||||||
def __init__(self, outfile=sys.stdout):
|
def __init__(self, outfile=sys.stdout):
|
||||||
self.errors = []
|
self.errors = []
|
||||||
|
@ -13,6 +14,7 @@ class ModelErrorCollection:
|
||||||
self.errors.append((context, error))
|
self.errors.append((context, error))
|
||||||
self.outfile.write(self.style.ERROR("%s: %s\n" % (context, error)))
|
self.outfile.write(self.style.ERROR("%s: %s\n" % (context, error)))
|
||||||
|
|
||||||
|
|
||||||
def get_validation_errors(outfile, app=None):
|
def get_validation_errors(outfile, app=None):
|
||||||
"""
|
"""
|
||||||
Validates all models that are part of the specified app. If no app name is provided,
|
Validates all models that are part of the specified app. If no app name is provided,
|
||||||
|
@ -54,7 +56,7 @@ def get_validation_errors(outfile, app=None):
|
||||||
e.add(opts, '"%s": CharFields require a "max_length" attribute that is a positive integer.' % f.name)
|
e.add(opts, '"%s": CharFields require a "max_length" attribute that is a positive integer.' % f.name)
|
||||||
if isinstance(f, models.DecimalField):
|
if isinstance(f, models.DecimalField):
|
||||||
decimalp_ok, mdigits_ok = False, False
|
decimalp_ok, mdigits_ok = False, False
|
||||||
decimalp_msg ='"%s": DecimalFields require a "decimal_places" attribute that is a non-negative integer.'
|
decimalp_msg = '"%s": DecimalFields require a "decimal_places" attribute that is a non-negative integer.'
|
||||||
try:
|
try:
|
||||||
decimal_places = int(f.decimal_places)
|
decimal_places = int(f.decimal_places)
|
||||||
if decimal_places < 0:
|
if decimal_places < 0:
|
||||||
|
@ -121,6 +123,10 @@ def get_validation_errors(outfile, app=None):
|
||||||
if isinstance(f.rel.to, (str, unicode)):
|
if isinstance(f.rel.to, (str, unicode)):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Make sure the model we're related hasn't been swapped out
|
||||||
|
if f.rel.to._meta.swapped:
|
||||||
|
e.add(opts, "'%s' defines a relation with the model '%s.%s', which has been swapped out. Update the relation to point at settings.%s." % (f.name, f.rel.to._meta.app_label, f.rel.to._meta.object_name, f.rel.to._meta.swappable))
|
||||||
|
|
||||||
# Make sure the related field specified by a ForeignKey is unique
|
# Make sure the related field specified by a ForeignKey is unique
|
||||||
if not f.rel.to._meta.get_field(f.rel.field_name).unique:
|
if not f.rel.to._meta.get_field(f.rel.field_name).unique:
|
||||||
e.add(opts, "Field '%s' under model '%s' must have a unique=True constraint." % (f.rel.field_name, f.rel.to.__name__))
|
e.add(opts, "Field '%s' under model '%s' must have a unique=True constraint." % (f.rel.field_name, f.rel.to.__name__))
|
||||||
|
@ -163,6 +169,10 @@ def get_validation_errors(outfile, app=None):
|
||||||
if isinstance(f.rel.to, (str, unicode)):
|
if isinstance(f.rel.to, (str, unicode)):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Make sure the model we're related hasn't been swapped out
|
||||||
|
if f.rel.to._meta.swapped:
|
||||||
|
e.add(opts, "'%s' defines a relation with the model '%s.%s', which has been swapped out. Update the relation to point at settings.%s." % (f.name, f.rel.to._meta.app_label, f.rel.to._meta.object_name, f.rel.to._meta.swappable))
|
||||||
|
|
||||||
# Check that the field is not set to unique. ManyToManyFields do not support unique.
|
# Check that the field is not set to unique. ManyToManyFields do not support unique.
|
||||||
if f.unique:
|
if f.unique:
|
||||||
e.add(opts, "ManyToManyFields cannot be unique. Remove the unique argument on '%s'." % f.name)
|
e.add(opts, "ManyToManyFields cannot be unique. Remove the unique argument on '%s'." % f.name)
|
||||||
|
@ -276,7 +286,8 @@ def get_validation_errors(outfile, app=None):
|
||||||
# Check ordering attribute.
|
# Check ordering attribute.
|
||||||
if opts.ordering:
|
if opts.ordering:
|
||||||
for field_name in opts.ordering:
|
for field_name in opts.ordering:
|
||||||
if field_name == '?': continue
|
if field_name == '?':
|
||||||
|
continue
|
||||||
if field_name.startswith('-'):
|
if field_name.startswith('-'):
|
||||||
field_name = field_name[1:]
|
field_name = field_name[1:]
|
||||||
if opts.order_with_respect_to and field_name == '_order':
|
if opts.order_with_respect_to and field_name == '_order':
|
||||||
|
|
|
@ -34,7 +34,7 @@ class BaseDatabaseCreation(object):
|
||||||
(list_of_sql, pending_references_dict)
|
(list_of_sql, pending_references_dict)
|
||||||
"""
|
"""
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
if not opts.managed or opts.proxy:
|
if not opts.managed or opts.proxy or opts.swapped:
|
||||||
return [], {}
|
return [], {}
|
||||||
final_output = []
|
final_output = []
|
||||||
table_output = []
|
table_output = []
|
||||||
|
@ -88,7 +88,7 @@ class BaseDatabaseCreation(object):
|
||||||
style.SQL_TABLE(qn(opts.db_table)) + ' (']
|
style.SQL_TABLE(qn(opts.db_table)) + ' (']
|
||||||
for i, line in enumerate(table_output): # Combine and add commas.
|
for i, line in enumerate(table_output): # Combine and add commas.
|
||||||
full_statement.append(
|
full_statement.append(
|
||||||
' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
|
' %s%s' % (line, i < len(table_output) - 1 and ',' or ''))
|
||||||
full_statement.append(')')
|
full_statement.append(')')
|
||||||
if opts.db_tablespace:
|
if opts.db_tablespace:
|
||||||
tablespace_sql = self.connection.ops.tablespace_sql(
|
tablespace_sql = self.connection.ops.tablespace_sql(
|
||||||
|
@ -137,11 +137,11 @@ class BaseDatabaseCreation(object):
|
||||||
"""
|
"""
|
||||||
from django.db.backends.util import truncate_name
|
from django.db.backends.util import truncate_name
|
||||||
|
|
||||||
if not model._meta.managed or model._meta.proxy:
|
opts = model._meta
|
||||||
|
if not opts.managed or opts.proxy or opts.swapped:
|
||||||
return []
|
return []
|
||||||
qn = self.connection.ops.quote_name
|
qn = self.connection.ops.quote_name
|
||||||
final_output = []
|
final_output = []
|
||||||
opts = model._meta
|
|
||||||
if model in pending_references:
|
if model in pending_references:
|
||||||
for rel_class, f in pending_references[model]:
|
for rel_class, f in pending_references[model]:
|
||||||
rel_opts = rel_class._meta
|
rel_opts = rel_class._meta
|
||||||
|
@ -166,7 +166,7 @@ class BaseDatabaseCreation(object):
|
||||||
"""
|
"""
|
||||||
Returns the CREATE INDEX SQL statements for a single model.
|
Returns the CREATE INDEX SQL statements for a single model.
|
||||||
"""
|
"""
|
||||||
if not model._meta.managed or model._meta.proxy:
|
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
|
||||||
return []
|
return []
|
||||||
output = []
|
output = []
|
||||||
for f in model._meta.local_fields:
|
for f in model._meta.local_fields:
|
||||||
|
@ -205,7 +205,7 @@ class BaseDatabaseCreation(object):
|
||||||
Return the DROP TABLE and restraint dropping statements for a single
|
Return the DROP TABLE and restraint dropping statements for a single
|
||||||
model.
|
model.
|
||||||
"""
|
"""
|
||||||
if not model._meta.managed or model._meta.proxy:
|
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
|
||||||
return []
|
return []
|
||||||
# Drop the table now
|
# Drop the table now
|
||||||
qn = self.connection.ops.quote_name
|
qn = self.connection.ops.quote_name
|
||||||
|
@ -222,7 +222,7 @@ class BaseDatabaseCreation(object):
|
||||||
|
|
||||||
def sql_remove_table_constraints(self, model, references_to_delete, style):
|
def sql_remove_table_constraints(self, model, references_to_delete, style):
|
||||||
from django.db.backends.util import truncate_name
|
from django.db.backends.util import truncate_name
|
||||||
if not model._meta.managed or model._meta.proxy:
|
if not model._meta.managed or model._meta.proxy or model._meta.swapped:
|
||||||
return []
|
return []
|
||||||
output = []
|
output = []
|
||||||
qn = self.connection.ops.quote_name
|
qn = self.connection.ops.quote_name
|
||||||
|
|
|
@ -230,6 +230,7 @@ class ModelBase(type):
|
||||||
if opts.order_with_respect_to:
|
if opts.order_with_respect_to:
|
||||||
cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True)
|
cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True)
|
||||||
cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)
|
cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)
|
||||||
|
|
||||||
# defer creating accessors on the foreign class until we are
|
# defer creating accessors on the foreign class until we are
|
||||||
# certain it has been created
|
# certain it has been created
|
||||||
def make_foreign_order_accessors(field, model, cls):
|
def make_foreign_order_accessors(field, model, cls):
|
||||||
|
@ -260,6 +261,7 @@ class ModelBase(type):
|
||||||
|
|
||||||
signals.class_prepared.send(sender=cls)
|
signals.class_prepared.send(sender=cls)
|
||||||
|
|
||||||
|
|
||||||
class ModelState(object):
|
class ModelState(object):
|
||||||
"""
|
"""
|
||||||
A class for storing instance state
|
A class for storing instance state
|
||||||
|
@ -271,6 +273,7 @@ class ModelState(object):
|
||||||
# This impacts validation only; it has no effect on the actual save.
|
# This impacts validation only; it has no effect on the actual save.
|
||||||
self.adding = True
|
self.adding = True
|
||||||
|
|
||||||
|
|
||||||
class Model(object):
|
class Model(object):
|
||||||
__metaclass__ = ModelBase
|
__metaclass__ = ModelBase
|
||||||
_deferred = False
|
_deferred = False
|
||||||
|
@ -585,7 +588,6 @@ class Model(object):
|
||||||
signals.post_save.send(sender=origin, instance=self, created=(not record_exists),
|
signals.post_save.send(sender=origin, instance=self, created=(not record_exists),
|
||||||
update_fields=update_fields, raw=raw, using=using)
|
update_fields=update_fields, raw=raw, using=using)
|
||||||
|
|
||||||
|
|
||||||
save_base.alters_data = True
|
save_base.alters_data = True
|
||||||
|
|
||||||
def delete(self, using=None):
|
def delete(self, using=None):
|
||||||
|
@ -609,7 +611,7 @@ class Model(object):
|
||||||
order = not is_next and '-' or ''
|
order = not is_next and '-' or ''
|
||||||
param = smart_str(getattr(self, field.attname))
|
param = smart_str(getattr(self, field.attname))
|
||||||
q = Q(**{'%s__%s' % (field.name, op): param})
|
q = Q(**{'%s__%s' % (field.name, op): param})
|
||||||
q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk})
|
q = q | Q(**{field.name: param, 'pk__%s' % op: self.pk})
|
||||||
qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
|
qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
|
||||||
try:
|
try:
|
||||||
return qs[0]
|
return qs[0]
|
||||||
|
@ -915,6 +917,7 @@ def get_absolute_url(opts, func, self, *args, **kwargs):
|
||||||
class Empty(object):
|
class Empty(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def simple_class_factory(model, attrs):
|
def simple_class_factory(model, attrs):
|
||||||
"""Used to unpickle Models without deferred fields.
|
"""Used to unpickle Models without deferred fields.
|
||||||
|
|
||||||
|
@ -924,6 +927,7 @@ def simple_class_factory(model, attrs):
|
||||||
"""
|
"""
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
def model_unpickle(model, attrs, factory):
|
def model_unpickle(model, attrs, factory):
|
||||||
"""
|
"""
|
||||||
Used to unpickle Model subclasses with deferred fields.
|
Used to unpickle Model subclasses with deferred fields.
|
||||||
|
@ -932,5 +936,6 @@ def model_unpickle(model, attrs, factory):
|
||||||
return cls.__new__(cls)
|
return cls.__new__(cls)
|
||||||
model_unpickle.__safe_for_unpickle__ = True
|
model_unpickle.__safe_for_unpickle__ = True
|
||||||
|
|
||||||
|
|
||||||
def subclass_exception(name, parents, module):
|
def subclass_exception(name, parents, module):
|
||||||
return type(name, parents, {'__module__': module})
|
return type(name, parents, {'__module__': module})
|
||||||
|
|
|
@ -20,6 +20,7 @@ RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
|
||||||
|
|
||||||
pending_lookups = {}
|
pending_lookups = {}
|
||||||
|
|
||||||
|
|
||||||
def add_lazy_relation(cls, field, relation, operation):
|
def add_lazy_relation(cls, field, relation, operation):
|
||||||
"""
|
"""
|
||||||
Adds a lookup on ``cls`` when a related field is defined using a string,
|
Adds a lookup on ``cls`` when a related field is defined using a string,
|
||||||
|
@ -76,6 +77,7 @@ def add_lazy_relation(cls, field, relation, operation):
|
||||||
value = (cls, field, operation)
|
value = (cls, field, operation)
|
||||||
pending_lookups.setdefault(key, []).append(value)
|
pending_lookups.setdefault(key, []).append(value)
|
||||||
|
|
||||||
|
|
||||||
def do_pending_lookups(sender, **kwargs):
|
def do_pending_lookups(sender, **kwargs):
|
||||||
"""
|
"""
|
||||||
Handle any pending relations to the sending model. Sent from class_prepared.
|
Handle any pending relations to the sending model. Sent from class_prepared.
|
||||||
|
@ -86,6 +88,7 @@ def do_pending_lookups(sender, **kwargs):
|
||||||
|
|
||||||
signals.class_prepared.connect(do_pending_lookups)
|
signals.class_prepared.connect(do_pending_lookups)
|
||||||
|
|
||||||
|
|
||||||
#HACK
|
#HACK
|
||||||
class RelatedField(object):
|
class RelatedField(object):
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
|
@ -219,6 +222,7 @@ class RelatedField(object):
|
||||||
# "related_name" option.
|
# "related_name" option.
|
||||||
return self.rel.related_name or self.opts.object_name.lower()
|
return self.rel.related_name or self.opts.object_name.lower()
|
||||||
|
|
||||||
|
|
||||||
class SingleRelatedObjectDescriptor(object):
|
class SingleRelatedObjectDescriptor(object):
|
||||||
# This class provides the functionality that makes the related-object
|
# This class provides the functionality that makes the related-object
|
||||||
# managers available as attributes on a model class, for fields that have
|
# managers available as attributes on a model class, for fields that have
|
||||||
|
@ -305,6 +309,7 @@ class SingleRelatedObjectDescriptor(object):
|
||||||
setattr(instance, self.cache_name, value)
|
setattr(instance, self.cache_name, value)
|
||||||
setattr(value, self.related.field.get_cache_name(), instance)
|
setattr(value, self.related.field.get_cache_name(), instance)
|
||||||
|
|
||||||
|
|
||||||
class ReverseSingleRelatedObjectDescriptor(object):
|
class ReverseSingleRelatedObjectDescriptor(object):
|
||||||
# This class provides the functionality that makes the related-object
|
# This class provides the functionality that makes the related-object
|
||||||
# managers available as attributes on a model class, for fields that have
|
# managers available as attributes on a model class, for fields that have
|
||||||
|
@ -429,6 +434,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
|
||||||
if value is not None and not self.field.rel.multiple:
|
if value is not None and not self.field.rel.multiple:
|
||||||
setattr(value, self.field.related.get_cache_name(), instance)
|
setattr(value, self.field.related.get_cache_name(), instance)
|
||||||
|
|
||||||
|
|
||||||
class ForeignRelatedObjectsDescriptor(object):
|
class ForeignRelatedObjectsDescriptor(object):
|
||||||
# This class provides the functionality that makes the related-object
|
# This class provides the functionality that makes the related-object
|
||||||
# managers available as attributes on a model class, for fields that have
|
# managers available as attributes on a model class, for fields that have
|
||||||
|
@ -751,6 +757,7 @@ def create_many_related_manager(superclass, rel):
|
||||||
|
|
||||||
return ManyRelatedManager
|
return ManyRelatedManager
|
||||||
|
|
||||||
|
|
||||||
class ManyRelatedObjectsDescriptor(object):
|
class ManyRelatedObjectsDescriptor(object):
|
||||||
# This class provides the functionality that makes the related-object
|
# This class provides the functionality that makes the related-object
|
||||||
# managers available as attributes on a model class, for fields that have
|
# managers available as attributes on a model class, for fields that have
|
||||||
|
@ -859,6 +866,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
||||||
manager.clear()
|
manager.clear()
|
||||||
manager.add(*value)
|
manager.add(*value)
|
||||||
|
|
||||||
|
|
||||||
class ManyToOneRel(object):
|
class ManyToOneRel(object):
|
||||||
def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
|
def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
|
||||||
parent_link=False, on_delete=None):
|
parent_link=False, on_delete=None):
|
||||||
|
@ -890,6 +898,7 @@ class ManyToOneRel(object):
|
||||||
self.field_name)
|
self.field_name)
|
||||||
return data[0]
|
return data[0]
|
||||||
|
|
||||||
|
|
||||||
class OneToOneRel(ManyToOneRel):
|
class OneToOneRel(ManyToOneRel):
|
||||||
def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
|
def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
|
||||||
parent_link=False, on_delete=None):
|
parent_link=False, on_delete=None):
|
||||||
|
@ -899,6 +908,7 @@ class OneToOneRel(ManyToOneRel):
|
||||||
)
|
)
|
||||||
self.multiple = False
|
self.multiple = False
|
||||||
|
|
||||||
|
|
||||||
class ManyToManyRel(object):
|
class ManyToManyRel(object):
|
||||||
def __init__(self, to, related_name=None, limit_choices_to=None,
|
def __init__(self, to, related_name=None, limit_choices_to=None,
|
||||||
symmetrical=True, through=None):
|
symmetrical=True, through=None):
|
||||||
|
@ -923,15 +933,17 @@ class ManyToManyRel(object):
|
||||||
"""
|
"""
|
||||||
return self.to._meta.pk
|
return self.to._meta.pk
|
||||||
|
|
||||||
|
|
||||||
class ForeignKey(RelatedField, Field):
|
class ForeignKey(RelatedField, Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _('Model %(model)s with pk %(pk)r does not exist.')
|
'invalid': _('Model %(model)s with pk %(pk)r does not exist.')
|
||||||
}
|
}
|
||||||
description = _("Foreign Key (type determined by related field)")
|
description = _("Foreign Key (type determined by related field)")
|
||||||
|
|
||||||
def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
|
def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
|
||||||
try:
|
try:
|
||||||
to_name = to._meta.object_name.lower()
|
to._meta.object_name.lower()
|
||||||
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
|
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
|
||||||
assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
|
assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
|
||||||
else:
|
else:
|
||||||
|
@ -1049,6 +1061,7 @@ class ForeignKey(RelatedField, Field):
|
||||||
return IntegerField().db_type(connection=connection)
|
return IntegerField().db_type(connection=connection)
|
||||||
return rel_field.db_type(connection=connection)
|
return rel_field.db_type(connection=connection)
|
||||||
|
|
||||||
|
|
||||||
class OneToOneField(ForeignKey):
|
class OneToOneField(ForeignKey):
|
||||||
"""
|
"""
|
||||||
A OneToOneField is essentially the same as a ForeignKey, with the exception
|
A OneToOneField is essentially the same as a ForeignKey, with the exception
|
||||||
|
@ -1057,6 +1070,7 @@ class OneToOneField(ForeignKey):
|
||||||
rather than returning a list.
|
rather than returning a list.
|
||||||
"""
|
"""
|
||||||
description = _("One-to-one relationship")
|
description = _("One-to-one relationship")
|
||||||
|
|
||||||
def __init__(self, to, to_field=None, **kwargs):
|
def __init__(self, to, to_field=None, **kwargs):
|
||||||
kwargs['unique'] = True
|
kwargs['unique'] = True
|
||||||
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
|
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
|
||||||
|
@ -1076,12 +1090,14 @@ class OneToOneField(ForeignKey):
|
||||||
else:
|
else:
|
||||||
setattr(instance, self.attname, data)
|
setattr(instance, self.attname, data)
|
||||||
|
|
||||||
|
|
||||||
def create_many_to_many_intermediary_model(field, klass):
|
def create_many_to_many_intermediary_model(field, klass):
|
||||||
from django.db import models
|
from django.db import models
|
||||||
managed = True
|
managed = True
|
||||||
if isinstance(field.rel.to, basestring) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT:
|
if isinstance(field.rel.to, basestring) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT:
|
||||||
to_model = field.rel.to
|
to_model = field.rel.to
|
||||||
to = to_model.split('.')[-1]
|
to = to_model.split('.')[-1]
|
||||||
|
|
||||||
def set_managed(field, model, cls):
|
def set_managed(field, model, cls):
|
||||||
field.rel.through._meta.managed = model._meta.managed or cls._meta.managed
|
field.rel.through._meta.managed = model._meta.managed or cls._meta.managed
|
||||||
add_lazy_relation(klass, field, to_model, set_managed)
|
add_lazy_relation(klass, field, to_model, set_managed)
|
||||||
|
@ -1118,8 +1134,10 @@ def create_many_to_many_intermediary_model(field, klass):
|
||||||
to: models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace)
|
to: models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class ManyToManyField(RelatedField, Field):
|
class ManyToManyField(RelatedField, Field):
|
||||||
description = _("Many-to-many relationship")
|
description = _("Many-to-many relationship")
|
||||||
|
|
||||||
def __init__(self, to, **kwargs):
|
def __init__(self, to, **kwargs):
|
||||||
try:
|
try:
|
||||||
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)
|
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)
|
||||||
|
@ -1134,7 +1152,7 @@ class ManyToManyField(RelatedField, Field):
|
||||||
kwargs['rel'] = ManyToManyRel(to,
|
kwargs['rel'] = ManyToManyRel(to,
|
||||||
related_name=kwargs.pop('related_name', None),
|
related_name=kwargs.pop('related_name', None),
|
||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
symmetrical=kwargs.pop('symmetrical', to==RECURSIVE_RELATIONSHIP_CONSTANT),
|
symmetrical=kwargs.pop('symmetrical', to == RECURSIVE_RELATIONSHIP_CONSTANT),
|
||||||
through=kwargs.pop('through', None))
|
through=kwargs.pop('through', None))
|
||||||
|
|
||||||
self.db_table = kwargs.pop('db_table', None)
|
self.db_table = kwargs.pop('db_table', None)
|
||||||
|
@ -1165,7 +1183,7 @@ class ManyToManyField(RelatedField, Field):
|
||||||
if hasattr(self, cache_attr):
|
if hasattr(self, cache_attr):
|
||||||
return getattr(self, cache_attr)
|
return getattr(self, cache_attr)
|
||||||
for f in self.rel.through._meta.fields:
|
for f in self.rel.through._meta.fields:
|
||||||
if hasattr(f,'rel') and f.rel and f.rel.to == related.model:
|
if hasattr(f, 'rel') and f.rel and f.rel.to == related.model:
|
||||||
setattr(self, cache_attr, getattr(f, attr))
|
setattr(self, cache_attr, getattr(f, attr))
|
||||||
return getattr(self, cache_attr)
|
return getattr(self, cache_attr)
|
||||||
|
|
||||||
|
@ -1176,7 +1194,7 @@ class ManyToManyField(RelatedField, Field):
|
||||||
return getattr(self, cache_attr)
|
return getattr(self, cache_attr)
|
||||||
found = False
|
found = False
|
||||||
for f in self.rel.through._meta.fields:
|
for f in self.rel.through._meta.fields:
|
||||||
if hasattr(f,'rel') and f.rel and f.rel.to == related.parent_model:
|
if hasattr(f, 'rel') and f.rel and f.rel.to == related.parent_model:
|
||||||
if related.model == related.parent_model:
|
if related.model == related.parent_model:
|
||||||
# If this is an m2m-intermediate to self,
|
# If this is an m2m-intermediate to self,
|
||||||
# the first foreign key you find will be
|
# the first foreign key you find will be
|
||||||
|
@ -1221,7 +1239,8 @@ class ManyToManyField(RelatedField, Field):
|
||||||
# The intermediate m2m model is not auto created if:
|
# The intermediate m2m model is not auto created if:
|
||||||
# 1) There is a manually specified intermediate, or
|
# 1) There is a manually specified intermediate, or
|
||||||
# 2) The class owning the m2m field is abstract.
|
# 2) The class owning the m2m field is abstract.
|
||||||
if not self.rel.through and not cls._meta.abstract:
|
# 3) The class owning the m2m field has been swapped out.
|
||||||
|
if not self.rel.through and not cls._meta.abstract and not cls._meta.swapped:
|
||||||
self.rel.through = create_many_to_many_intermediary_model(self, cls)
|
self.rel.through = create_many_to_many_intermediary_model(self, cls)
|
||||||
|
|
||||||
# Add the descriptor for the m2m relation
|
# Add the descriptor for the m2m relation
|
||||||
|
|
|
@ -13,6 +13,7 @@ import threading
|
||||||
__all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
|
__all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
|
||||||
'load_app', 'app_cache_ready')
|
'load_app', 'app_cache_ready')
|
||||||
|
|
||||||
|
|
||||||
class AppCache(object):
|
class AppCache(object):
|
||||||
"""
|
"""
|
||||||
A cache that stores installed applications and their models. Used to
|
A cache that stores installed applications and their models. Used to
|
||||||
|
|
|
@ -17,7 +17,8 @@ get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|
|
||||||
DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
|
DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
|
||||||
'unique_together', 'permissions', 'get_latest_by',
|
'unique_together', 'permissions', 'get_latest_by',
|
||||||
'order_with_respect_to', 'app_label', 'db_tablespace',
|
'order_with_respect_to', 'app_label', 'db_tablespace',
|
||||||
'abstract', 'managed', 'proxy', 'auto_created')
|
'abstract', 'managed', 'proxy', 'swappable', 'auto_created')
|
||||||
|
|
||||||
|
|
||||||
class Options(object):
|
class Options(object):
|
||||||
def __init__(self, meta, app_label=None):
|
def __init__(self, meta, app_label=None):
|
||||||
|
@ -50,6 +51,7 @@ class Options(object):
|
||||||
# in the end of the proxy_for_model chain. In particular, for
|
# in the end of the proxy_for_model chain. In particular, for
|
||||||
# concrete models, the concrete_model is always the class itself.
|
# concrete models, the concrete_model is always the class itself.
|
||||||
self.concrete_model = None
|
self.concrete_model = None
|
||||||
|
self.swappable = None
|
||||||
self.parents = SortedDict()
|
self.parents = SortedDict()
|
||||||
self.duplicate_targets = {}
|
self.duplicate_targets = {}
|
||||||
self.auto_created = False
|
self.auto_created = False
|
||||||
|
@ -213,6 +215,14 @@ class Options(object):
|
||||||
return raw
|
return raw
|
||||||
verbose_name_raw = property(verbose_name_raw)
|
verbose_name_raw = property(verbose_name_raw)
|
||||||
|
|
||||||
|
def _swapped(self):
|
||||||
|
"""
|
||||||
|
Has this model been swapped out for another?
|
||||||
|
"""
|
||||||
|
model_label = '%s.%s' % (self.app_label, self.object_name)
|
||||||
|
return self.swappable and getattr(settings, self.swappable, None) not in (None, model_label)
|
||||||
|
swapped = property(_swapped)
|
||||||
|
|
||||||
def _fields(self):
|
def _fields(self):
|
||||||
"""
|
"""
|
||||||
The getter for self.fields. This returns the list of field objects
|
The getter for self.fields. This returns the list of field objects
|
||||||
|
|
|
@ -27,7 +27,7 @@ class Approximate(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if self.val == other:
|
if self.val == other:
|
||||||
return True
|
return True
|
||||||
return round(abs(self.val-other), self.places) == 0
|
return round(abs(self.val - other), self.places) == 0
|
||||||
|
|
||||||
|
|
||||||
class ContextList(list):
|
class ContextList(list):
|
||||||
|
@ -45,7 +45,7 @@ class ContextList(list):
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
try:
|
try:
|
||||||
value = self[key]
|
self[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -187,9 +187,11 @@ class override_settings(object):
|
||||||
if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase):
|
if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase):
|
||||||
original_pre_setup = test_func._pre_setup
|
original_pre_setup = test_func._pre_setup
|
||||||
original_post_teardown = test_func._post_teardown
|
original_post_teardown = test_func._post_teardown
|
||||||
|
|
||||||
def _pre_setup(innerself):
|
def _pre_setup(innerself):
|
||||||
self.enable()
|
self.enable()
|
||||||
original_pre_setup(innerself)
|
original_pre_setup(innerself)
|
||||||
|
|
||||||
def _post_teardown(innerself):
|
def _post_teardown(innerself):
|
||||||
original_post_teardown(innerself)
|
original_post_teardown(innerself)
|
||||||
self.disable()
|
self.disable()
|
||||||
|
@ -218,4 +220,3 @@ class override_settings(object):
|
||||||
new_value = getattr(settings, key, None)
|
new_value = getattr(settings, key, None)
|
||||||
setting_changed.send(sender=settings._wrapped.__class__,
|
setting_changed.send(sender=settings._wrapped.__class__,
|
||||||
setting=key, value=new_value)
|
setting=key, value=new_value)
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,15 @@ Default: Not defined
|
||||||
The site-specific user profile model used by this site. See
|
The site-specific user profile model used by this site. See
|
||||||
:ref:`auth-profiles`.
|
:ref:`auth-profiles`.
|
||||||
|
|
||||||
|
.. setting:: AUTH_USER_MODEL
|
||||||
|
|
||||||
|
AUTH_USER_MODEL
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Default: 'auth.User'
|
||||||
|
|
||||||
|
The model to use to represent a User. See :ref:`auth-custom-user`.
|
||||||
|
|
||||||
.. setting:: CACHES
|
.. setting:: CACHES
|
||||||
|
|
||||||
CACHES
|
CACHES
|
||||||
|
|
|
@ -1723,6 +1723,13 @@ Fields
|
||||||
group.permissions.remove(permission, permission, ...)
|
group.permissions.remove(permission, permission, ...)
|
||||||
group.permissions.clear()
|
group.permissions.clear()
|
||||||
|
|
||||||
|
.. _auth-custom-user:
|
||||||
|
|
||||||
|
Customizing the User model
|
||||||
|
==========================
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
.. _authentication-backends:
|
.. _authentication-backends:
|
||||||
|
|
||||||
Other authentication sources
|
Other authentication sources
|
||||||
|
|
|
@ -19,11 +19,12 @@ class FieldErrors(models.Model):
|
||||||
decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10)
|
decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10)
|
||||||
filefield = models.FileField()
|
filefield = models.FileField()
|
||||||
choices = models.CharField(max_length=10, choices='bad')
|
choices = models.CharField(max_length=10, choices='bad')
|
||||||
choices2 = models.CharField(max_length=10, choices=[(1,2,3),(1,2,3)])
|
choices2 = models.CharField(max_length=10, choices=[(1, 2, 3), (1, 2, 3)])
|
||||||
index = models.CharField(max_length=10, db_index='bad')
|
index = models.CharField(max_length=10, db_index='bad')
|
||||||
field_ = models.CharField(max_length=10)
|
field_ = models.CharField(max_length=10)
|
||||||
nullbool = models.BooleanField(null=True)
|
nullbool = models.BooleanField(null=True)
|
||||||
|
|
||||||
|
|
||||||
class Target(models.Model):
|
class Target(models.Model):
|
||||||
tgt_safe = models.CharField(max_length=10)
|
tgt_safe = models.CharField(max_length=10)
|
||||||
clash1 = models.CharField(max_length=10)
|
clash1 = models.CharField(max_length=10)
|
||||||
|
@ -31,12 +32,14 @@ class Target(models.Model):
|
||||||
|
|
||||||
clash1_set = models.CharField(max_length=10)
|
clash1_set = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
|
||||||
class Clash1(models.Model):
|
class Clash1(models.Model):
|
||||||
src_safe = models.CharField(max_length=10)
|
src_safe = models.CharField(max_length=10)
|
||||||
|
|
||||||
foreign = models.ForeignKey(Target)
|
foreign = models.ForeignKey(Target)
|
||||||
m2m = models.ManyToManyField(Target)
|
m2m = models.ManyToManyField(Target)
|
||||||
|
|
||||||
|
|
||||||
class Clash2(models.Model):
|
class Clash2(models.Model):
|
||||||
src_safe = models.CharField(max_length=10)
|
src_safe = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
@ -46,6 +49,7 @@ class Clash2(models.Model):
|
||||||
m2m_1 = models.ManyToManyField(Target, related_name='id')
|
m2m_1 = models.ManyToManyField(Target, related_name='id')
|
||||||
m2m_2 = models.ManyToManyField(Target, related_name='src_safe')
|
m2m_2 = models.ManyToManyField(Target, related_name='src_safe')
|
||||||
|
|
||||||
|
|
||||||
class Target2(models.Model):
|
class Target2(models.Model):
|
||||||
clash3 = models.CharField(max_length=10)
|
clash3 = models.CharField(max_length=10)
|
||||||
foreign_tgt = models.ForeignKey(Target)
|
foreign_tgt = models.ForeignKey(Target)
|
||||||
|
@ -54,6 +58,7 @@ class Target2(models.Model):
|
||||||
m2m_tgt = models.ManyToManyField(Target)
|
m2m_tgt = models.ManyToManyField(Target)
|
||||||
clashm2m_set = models.ManyToManyField(Target)
|
clashm2m_set = models.ManyToManyField(Target)
|
||||||
|
|
||||||
|
|
||||||
class Clash3(models.Model):
|
class Clash3(models.Model):
|
||||||
src_safe = models.CharField(max_length=10)
|
src_safe = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
@ -63,12 +68,15 @@ class Clash3(models.Model):
|
||||||
m2m_1 = models.ManyToManyField(Target2, related_name='foreign_tgt')
|
m2m_1 = models.ManyToManyField(Target2, related_name='foreign_tgt')
|
||||||
m2m_2 = models.ManyToManyField(Target2, related_name='m2m_tgt')
|
m2m_2 = models.ManyToManyField(Target2, related_name='m2m_tgt')
|
||||||
|
|
||||||
|
|
||||||
class ClashForeign(models.Model):
|
class ClashForeign(models.Model):
|
||||||
foreign = models.ForeignKey(Target2)
|
foreign = models.ForeignKey(Target2)
|
||||||
|
|
||||||
|
|
||||||
class ClashM2M(models.Model):
|
class ClashM2M(models.Model):
|
||||||
m2m = models.ManyToManyField(Target2)
|
m2m = models.ManyToManyField(Target2)
|
||||||
|
|
||||||
|
|
||||||
class SelfClashForeign(models.Model):
|
class SelfClashForeign(models.Model):
|
||||||
src_safe = models.CharField(max_length=10)
|
src_safe = models.CharField(max_length=10)
|
||||||
selfclashforeign = models.CharField(max_length=10)
|
selfclashforeign = models.CharField(max_length=10)
|
||||||
|
@ -77,6 +85,7 @@ class SelfClashForeign(models.Model):
|
||||||
foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id')
|
foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id')
|
||||||
foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe')
|
foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe')
|
||||||
|
|
||||||
|
|
||||||
class ValidM2M(models.Model):
|
class ValidM2M(models.Model):
|
||||||
src_safe = models.CharField(max_length=10)
|
src_safe = models.CharField(max_length=10)
|
||||||
validm2m = models.CharField(max_length=10)
|
validm2m = models.CharField(max_length=10)
|
||||||
|
@ -92,6 +101,7 @@ class ValidM2M(models.Model):
|
||||||
m2m_3 = models.ManyToManyField('self')
|
m2m_3 = models.ManyToManyField('self')
|
||||||
m2m_4 = models.ManyToManyField('self')
|
m2m_4 = models.ManyToManyField('self')
|
||||||
|
|
||||||
|
|
||||||
class SelfClashM2M(models.Model):
|
class SelfClashM2M(models.Model):
|
||||||
src_safe = models.CharField(max_length=10)
|
src_safe = models.CharField(max_length=10)
|
||||||
selfclashm2m = models.CharField(max_length=10)
|
selfclashm2m = models.CharField(max_length=10)
|
||||||
|
@ -106,120 +116,148 @@ class SelfClashM2M(models.Model):
|
||||||
m2m_3 = models.ManyToManyField('self', symmetrical=False)
|
m2m_3 = models.ManyToManyField('self', symmetrical=False)
|
||||||
m2m_4 = models.ManyToManyField('self', symmetrical=False)
|
m2m_4 = models.ManyToManyField('self', symmetrical=False)
|
||||||
|
|
||||||
|
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
"But it's valid to call a model Model."
|
"But it's valid to call a model Model."
|
||||||
year = models.PositiveIntegerField() #1960
|
year = models.PositiveIntegerField() # 1960
|
||||||
make = models.CharField(max_length=10) #Aston Martin
|
make = models.CharField(max_length=10) # Aston Martin
|
||||||
name = models.CharField(max_length=10) #DB 4 GT
|
name = models.CharField(max_length=10) # DB 4 GT
|
||||||
|
|
||||||
|
|
||||||
class Car(models.Model):
|
class Car(models.Model):
|
||||||
colour = models.CharField(max_length=5)
|
colour = models.CharField(max_length=5)
|
||||||
model = models.ForeignKey(Model)
|
model = models.ForeignKey(Model)
|
||||||
|
|
||||||
|
|
||||||
class MissingRelations(models.Model):
|
class MissingRelations(models.Model):
|
||||||
rel1 = models.ForeignKey("Rel1")
|
rel1 = models.ForeignKey("Rel1")
|
||||||
rel2 = models.ManyToManyField("Rel2")
|
rel2 = models.ManyToManyField("Rel2")
|
||||||
|
|
||||||
|
|
||||||
class MissingManualM2MModel(models.Model):
|
class MissingManualM2MModel(models.Model):
|
||||||
name = models.CharField(max_length=5)
|
name = models.CharField(max_length=5)
|
||||||
missing_m2m = models.ManyToManyField(Model, through="MissingM2MModel")
|
missing_m2m = models.ManyToManyField(Model, through="MissingM2MModel")
|
||||||
|
|
||||||
|
|
||||||
class Person(models.Model):
|
class Person(models.Model):
|
||||||
name = models.CharField(max_length=5)
|
name = models.CharField(max_length=5)
|
||||||
|
|
||||||
|
|
||||||
class Group(models.Model):
|
class Group(models.Model):
|
||||||
name = models.CharField(max_length=5)
|
name = models.CharField(max_length=5)
|
||||||
primary = models.ManyToManyField(Person, through="Membership", related_name="primary")
|
primary = models.ManyToManyField(Person, through="Membership", related_name="primary")
|
||||||
secondary = models.ManyToManyField(Person, through="Membership", related_name="secondary")
|
secondary = models.ManyToManyField(Person, through="Membership", related_name="secondary")
|
||||||
tertiary = models.ManyToManyField(Person, through="RelationshipDoubleFK", related_name="tertiary")
|
tertiary = models.ManyToManyField(Person, through="RelationshipDoubleFK", related_name="tertiary")
|
||||||
|
|
||||||
|
|
||||||
class GroupTwo(models.Model):
|
class GroupTwo(models.Model):
|
||||||
name = models.CharField(max_length=5)
|
name = models.CharField(max_length=5)
|
||||||
primary = models.ManyToManyField(Person, through="Membership")
|
primary = models.ManyToManyField(Person, through="Membership")
|
||||||
secondary = models.ManyToManyField(Group, through="MembershipMissingFK")
|
secondary = models.ManyToManyField(Group, through="MembershipMissingFK")
|
||||||
|
|
||||||
|
|
||||||
class Membership(models.Model):
|
class Membership(models.Model):
|
||||||
person = models.ForeignKey(Person)
|
person = models.ForeignKey(Person)
|
||||||
group = models.ForeignKey(Group)
|
group = models.ForeignKey(Group)
|
||||||
not_default_or_null = models.CharField(max_length=5)
|
not_default_or_null = models.CharField(max_length=5)
|
||||||
|
|
||||||
|
|
||||||
class MembershipMissingFK(models.Model):
|
class MembershipMissingFK(models.Model):
|
||||||
person = models.ForeignKey(Person)
|
person = models.ForeignKey(Person)
|
||||||
|
|
||||||
|
|
||||||
class PersonSelfRefM2M(models.Model):
|
class PersonSelfRefM2M(models.Model):
|
||||||
name = models.CharField(max_length=5)
|
name = models.CharField(max_length=5)
|
||||||
friends = models.ManyToManyField('self', through="Relationship")
|
friends = models.ManyToManyField('self', through="Relationship")
|
||||||
too_many_friends = models.ManyToManyField('self', through="RelationshipTripleFK")
|
too_many_friends = models.ManyToManyField('self', through="RelationshipTripleFK")
|
||||||
|
|
||||||
|
|
||||||
class PersonSelfRefM2MExplicit(models.Model):
|
class PersonSelfRefM2MExplicit(models.Model):
|
||||||
name = models.CharField(max_length=5)
|
name = models.CharField(max_length=5)
|
||||||
friends = models.ManyToManyField('self', through="ExplicitRelationship", symmetrical=True)
|
friends = models.ManyToManyField('self', through="ExplicitRelationship", symmetrical=True)
|
||||||
|
|
||||||
|
|
||||||
class Relationship(models.Model):
|
class Relationship(models.Model):
|
||||||
first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set")
|
first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set")
|
||||||
second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set")
|
second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set")
|
||||||
date_added = models.DateTimeField()
|
date_added = models.DateTimeField()
|
||||||
|
|
||||||
|
|
||||||
class ExplicitRelationship(models.Model):
|
class ExplicitRelationship(models.Model):
|
||||||
first = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_from_set")
|
first = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_from_set")
|
||||||
second = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_to_set")
|
second = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_to_set")
|
||||||
date_added = models.DateTimeField()
|
date_added = models.DateTimeField()
|
||||||
|
|
||||||
|
|
||||||
class RelationshipTripleFK(models.Model):
|
class RelationshipTripleFK(models.Model):
|
||||||
first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set_2")
|
first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set_2")
|
||||||
second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set_2")
|
second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set_2")
|
||||||
third = models.ForeignKey(PersonSelfRefM2M, related_name="too_many_by_far")
|
third = models.ForeignKey(PersonSelfRefM2M, related_name="too_many_by_far")
|
||||||
date_added = models.DateTimeField()
|
date_added = models.DateTimeField()
|
||||||
|
|
||||||
|
|
||||||
class RelationshipDoubleFK(models.Model):
|
class RelationshipDoubleFK(models.Model):
|
||||||
first = models.ForeignKey(Person, related_name="first_related_name")
|
first = models.ForeignKey(Person, related_name="first_related_name")
|
||||||
second = models.ForeignKey(Person, related_name="second_related_name")
|
second = models.ForeignKey(Person, related_name="second_related_name")
|
||||||
third = models.ForeignKey(Group, related_name="rel_to_set")
|
third = models.ForeignKey(Group, related_name="rel_to_set")
|
||||||
date_added = models.DateTimeField()
|
date_added = models.DateTimeField()
|
||||||
|
|
||||||
|
|
||||||
class AbstractModel(models.Model):
|
class AbstractModel(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
class AbstractRelationModel(models.Model):
|
class AbstractRelationModel(models.Model):
|
||||||
fk1 = models.ForeignKey('AbstractModel')
|
fk1 = models.ForeignKey('AbstractModel')
|
||||||
fk2 = models.ManyToManyField('AbstractModel')
|
fk2 = models.ManyToManyField('AbstractModel')
|
||||||
|
|
||||||
|
|
||||||
class UniqueM2M(models.Model):
|
class UniqueM2M(models.Model):
|
||||||
""" Model to test for unique ManyToManyFields, which are invalid. """
|
""" Model to test for unique ManyToManyFields, which are invalid. """
|
||||||
unique_people = models.ManyToManyField(Person, unique=True)
|
unique_people = models.ManyToManyField(Person, unique=True)
|
||||||
|
|
||||||
|
|
||||||
class NonUniqueFKTarget1(models.Model):
|
class NonUniqueFKTarget1(models.Model):
|
||||||
""" Model to test for non-unique FK target in yet-to-be-defined model: expect an error """
|
""" Model to test for non-unique FK target in yet-to-be-defined model: expect an error """
|
||||||
tgt = models.ForeignKey('FKTarget', to_field='bad')
|
tgt = models.ForeignKey('FKTarget', to_field='bad')
|
||||||
|
|
||||||
|
|
||||||
class UniqueFKTarget1(models.Model):
|
class UniqueFKTarget1(models.Model):
|
||||||
""" Model to test for unique FK target in yet-to-be-defined model: expect no error """
|
""" Model to test for unique FK target in yet-to-be-defined model: expect no error """
|
||||||
tgt = models.ForeignKey('FKTarget', to_field='good')
|
tgt = models.ForeignKey('FKTarget', to_field='good')
|
||||||
|
|
||||||
|
|
||||||
class FKTarget(models.Model):
|
class FKTarget(models.Model):
|
||||||
bad = models.IntegerField()
|
bad = models.IntegerField()
|
||||||
good = models.IntegerField(unique=True)
|
good = models.IntegerField(unique=True)
|
||||||
|
|
||||||
|
|
||||||
class NonUniqueFKTarget2(models.Model):
|
class NonUniqueFKTarget2(models.Model):
|
||||||
""" Model to test for non-unique FK target in previously seen model: expect an error """
|
""" Model to test for non-unique FK target in previously seen model: expect an error """
|
||||||
tgt = models.ForeignKey(FKTarget, to_field='bad')
|
tgt = models.ForeignKey(FKTarget, to_field='bad')
|
||||||
|
|
||||||
|
|
||||||
class UniqueFKTarget2(models.Model):
|
class UniqueFKTarget2(models.Model):
|
||||||
""" Model to test for unique FK target in previously seen model: expect no error """
|
""" Model to test for unique FK target in previously seen model: expect no error """
|
||||||
tgt = models.ForeignKey(FKTarget, to_field='good')
|
tgt = models.ForeignKey(FKTarget, to_field='good')
|
||||||
|
|
||||||
|
|
||||||
class NonExistingOrderingWithSingleUnderscore(models.Model):
|
class NonExistingOrderingWithSingleUnderscore(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("does_not_exist",)
|
ordering = ("does_not_exist",)
|
||||||
|
|
||||||
|
|
||||||
class InvalidSetNull(models.Model):
|
class InvalidSetNull(models.Model):
|
||||||
fk = models.ForeignKey('self', on_delete=models.SET_NULL)
|
fk = models.ForeignKey('self', on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
|
||||||
class InvalidSetDefault(models.Model):
|
class InvalidSetDefault(models.Model):
|
||||||
fk = models.ForeignKey('self', on_delete=models.SET_DEFAULT)
|
fk = models.ForeignKey('self', on_delete=models.SET_DEFAULT)
|
||||||
|
|
||||||
|
|
||||||
class UnicodeForeignKeys(models.Model):
|
class UnicodeForeignKeys(models.Model):
|
||||||
"""Foreign keys which can translate to ascii should be OK, but fail if
|
"""Foreign keys which can translate to ascii should be OK, but fail if
|
||||||
they're not."""
|
they're not."""
|
||||||
|
@ -230,9 +268,11 @@ class UnicodeForeignKeys(models.Model):
|
||||||
# when adding the errors in core/management/validation.py
|
# when adding the errors in core/management/validation.py
|
||||||
#bad = models.ForeignKey(u'★')
|
#bad = models.ForeignKey(u'★')
|
||||||
|
|
||||||
|
|
||||||
class PrimaryKeyNull(models.Model):
|
class PrimaryKeyNull(models.Model):
|
||||||
my_pk_field = models.IntegerField(primary_key=True, null=True)
|
my_pk_field = models.IntegerField(primary_key=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
class OrderByPKModel(models.Model):
|
class OrderByPKModel(models.Model):
|
||||||
"""
|
"""
|
||||||
Model to test that ordering by pk passes validation.
|
Model to test that ordering by pk passes validation.
|
||||||
|
@ -243,6 +283,42 @@ class OrderByPKModel(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('pk',)
|
ordering = ('pk',)
|
||||||
|
|
||||||
|
|
||||||
|
class SwappableModel(models.Model):
|
||||||
|
"""A model that can be, but isn't swapped out.
|
||||||
|
|
||||||
|
References to this model *shoudln't* raise any validation error.
|
||||||
|
"""
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
swappable = 'TEST_SWAPPABLE_MODEL'
|
||||||
|
|
||||||
|
|
||||||
|
class SwappedModel(models.Model):
|
||||||
|
"""A model that is swapped out.
|
||||||
|
|
||||||
|
References to this model *should* raise a validation error.
|
||||||
|
Requires TEST_SWAPPED_MODEL to be defined in the test environment;
|
||||||
|
this is guaranteed by the test runner using @override_settings.
|
||||||
|
"""
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
swappable = 'TEST_SWAPPED_MODEL'
|
||||||
|
|
||||||
|
|
||||||
|
class HardReferenceModel(models.Model):
|
||||||
|
fk_1 = models.ForeignKey(SwappableModel, related_name='fk_hardref1')
|
||||||
|
fk_2 = models.ForeignKey('invalid_models.SwappableModel', related_name='fk_hardref2')
|
||||||
|
fk_3 = models.ForeignKey(SwappedModel, related_name='fk_hardref3')
|
||||||
|
fk_4 = models.ForeignKey('invalid_models.SwappedModel', related_name='fk_hardref4')
|
||||||
|
m2m_1 = models.ManyToManyField(SwappableModel, related_name='m2m_hardref1')
|
||||||
|
m2m_2 = models.ManyToManyField('invalid_models.SwappableModel', related_name='m2m_hardref2')
|
||||||
|
m2m_3 = models.ManyToManyField(SwappedModel, related_name='m2m_hardref3')
|
||||||
|
m2m_4 = models.ManyToManyField('invalid_models.SwappedModel', related_name='m2m_hardref4')
|
||||||
|
|
||||||
|
|
||||||
model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute that is a positive integer.
|
model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute that is a positive integer.
|
||||||
invalid_models.fielderrors: "charfield2": CharFields require a "max_length" attribute that is a positive integer.
|
invalid_models.fielderrors: "charfield2": CharFields require a "max_length" attribute that is a positive integer.
|
||||||
invalid_models.fielderrors: "charfield3": CharFields require a "max_length" attribute that is a positive integer.
|
invalid_models.fielderrors: "charfield3": CharFields require a "max_length" attribute that is a positive integer.
|
||||||
|
@ -351,6 +427,10 @@ invalid_models.nonuniquefktarget2: Field 'bad' under model 'FKTarget' must have
|
||||||
invalid_models.nonexistingorderingwithsingleunderscore: "ordering" refers to "does_not_exist", a field that doesn't exist.
|
invalid_models.nonexistingorderingwithsingleunderscore: "ordering" refers to "does_not_exist", a field that doesn't exist.
|
||||||
invalid_models.invalidsetnull: 'fk' specifies on_delete=SET_NULL, but cannot be null.
|
invalid_models.invalidsetnull: 'fk' specifies on_delete=SET_NULL, but cannot be null.
|
||||||
invalid_models.invalidsetdefault: 'fk' specifies on_delete=SET_DEFAULT, but has no default value.
|
invalid_models.invalidsetdefault: 'fk' specifies on_delete=SET_DEFAULT, but has no default value.
|
||||||
|
invalid_models.hardreferencemodel: 'fk_3' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL.
|
||||||
|
invalid_models.hardreferencemodel: 'fk_4' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL.
|
||||||
|
invalid_models.hardreferencemodel: 'm2m_3' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL.
|
||||||
|
invalid_models.hardreferencemodel: 'm2m_4' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not connection.features.interprets_empty_strings_as_nulls:
|
if not connection.features.interprets_empty_strings_as_nulls:
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.core.management.validation import get_validation_errors
|
||||||
from django.db.models.loading import cache, load_app
|
from django.db.models.loading import cache, load_app
|
||||||
|
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
|
||||||
class InvalidModelTestCase(unittest.TestCase):
|
class InvalidModelTestCase(unittest.TestCase):
|
||||||
|
@ -31,14 +32,18 @@ class InvalidModelTestCase(unittest.TestCase):
|
||||||
cache._get_models_cache = {}
|
cache._get_models_cache = {}
|
||||||
sys.stdout = self.old_stdout
|
sys.stdout = self.old_stdout
|
||||||
|
|
||||||
|
# Technically, this isn't an override -- TEST_SWAPPED_MODEL must be
|
||||||
|
# set to *something* in order for the test to work. However, it's
|
||||||
|
# easier to set this up as an override than to require every developer
|
||||||
|
# to specify a value in their test settings.
|
||||||
|
@override_settings(TEST_SWAPPED_MODEL='invalid_models.Target')
|
||||||
def test_invalid_models(self):
|
def test_invalid_models(self):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = load_app("modeltests.invalid_models.invalid_models")
|
module = load_app("modeltests.invalid_models.invalid_models")
|
||||||
except Exception:
|
except Exception:
|
||||||
self.fail('Unable to load invalid model module')
|
self.fail('Unable to load invalid model module')
|
||||||
|
|
||||||
count = get_validation_errors(self.stdout, module)
|
get_validation_errors(self.stdout, module)
|
||||||
self.stdout.seek(0)
|
self.stdout.seek(0)
|
||||||
error_log = self.stdout.read()
|
error_log = self.stdout.read()
|
||||||
actual = error_log.split('\n')
|
actual = error_log.split('\n')
|
||||||
|
|
Loading…
Reference in New Issue