Example #1
0
    def test_models_py(self):
        """
        Tests that the models in the models.py file were loaded correctly.
        """
        self.assertEqual(app_cache.get_model("app_cache", "TotallyNormal"), TotallyNormal)
        self.assertEqual(app_cache.get_model("app_cache", "SoAlternative"), None)

        self.assertEqual(new_app_cache.get_model("app_cache", "TotallyNormal"), None)
        self.assertEqual(new_app_cache.get_model("app_cache", "SoAlternative"), SoAlternative)
Example #2
0
def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs):
    try:
        app_cache.get_model('auth', 'Permission')
    except UnavailableApp:
        return

    if not router.allow_migrate(db, auth_app.Permission):
        return

    from django.contrib.contenttypes.models import ContentType

    app_models = app_cache.get_models(app)

    # This will hold the permissions we're looking for as
    # (content_type, (codename, name))
    searched_perms = list()
    # The codenames and ctypes that should exist.
    ctypes = set()
    for klass in app_models:
        # Force looking up the content types in the current database
        # before creating foreign keys to them.
        ctype = ContentType.objects.db_manager(db).get_for_model(klass)
        ctypes.add(ctype)
        for perm in _get_all_permissions(klass._meta, ctype):
            searched_perms.append((ctype, perm))

    # Find all the Permissions that have a content_type for a model we're
    # looking for.  We don't need to check for codenames since we already have
    # a list of the ones we're going to create.
    all_perms = set(auth_app.Permission.objects.using(db).filter(
        content_type__in=ctypes,
    ).values_list(
        "content_type", "codename"
    ))

    perms = [
        auth_app.Permission(codename=codename, name=name, content_type=ctype)
        for ctype, (codename, name) in searched_perms
        if (ctype.pk, codename) not in all_perms
    ]
    # Validate the permissions before bulk_creation to avoid cryptic
    # database error when the verbose_name is longer than 50 characters
    permission_name_max_length = auth_app.Permission._meta.get_field('name').max_length
    verbose_name_max_length = permission_name_max_length - 11  # len('Can change ') prefix
    for perm in perms:
        if len(perm.name) > permission_name_max_length:
            raise exceptions.ValidationError(
                "The verbose_name of %s is longer than %s characters" % (
                    perm.content_type,
                    verbose_name_max_length,
                )
            )
    auth_app.Permission.objects.using(db).bulk_create(perms)
    if verbosity >= 2:
        for perm in perms:
            print("Adding permission '%s'" % perm)
Example #3
0
 def test_missing_app(self):
     """
     Test that repeated app loading doesn't succeed in case there is an
     error. Refs #17667.
     """
     app_cache = AppCache()
     # Pretend we're the master app cache to test the population process.
     app_cache._apps_loaded = False
     app_cache._models_loaded = False
     with warnings.catch_warnings():
         warnings.filterwarnings("ignore", "Overriding setting INSTALLED_APPS")
         with override_settings(INSTALLED_APPS=['notexists']):
             with self.assertRaises(ImportError):
                 app_cache.get_model('notexists', 'nomodel')
             with self.assertRaises(ImportError):
                 app_cache.get_model('notexists', 'nomodel')
Example #4
0
def _get_model(model_identifier):
    """
    Helper to look up a model from an "app_label.model_name" string.
    """
    try:
        Model = app_cache.get_model(*model_identifier.split("."))
    except TypeError:
        Model = None
    if Model is None:
        raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
    return Model
Example #5
0
def model_unpickle(model_id, attrs, factory):
    """
    Used to unpickle Model subclasses with deferred fields.
    """
    if isinstance(model_id, tuple):
        model = app_cache.get_model(*model_id)
    else:
        # Backwards compat - the model was cached directly in earlier versions.
        model = model_id
    cls = factory(model, attrs)
    return cls.__new__(cls)
Example #6
0
def create_superuser(app, created_models, verbosity, db, **kwargs):
    try:
        app_cache.get_model('auth', 'Permission')
        UserModel = get_user_model()
    except UnavailableApp:
        return

    from django.core.management import call_command

    if UserModel in created_models and kwargs.get('interactive', True):
        msg = ("\nYou just installed Django's auth system, which means you "
            "don't have any superusers defined.\nWould you like to create one "
            "now? (yes/no): ")
        confirm = input(msg)
        while 1:
            if confirm not in ('yes', 'no'):
                confirm = input('Please enter either "yes" or "no": ')
                continue
            if confirm == 'yes':
                call_command("createsuperuser", interactive=True, database=db)
            break
Example #7
0
def get_user_model():
    """
    Returns the User model that is active in this project.
    """
    from django.apps import app_cache

    try:
        app_label, model_name = settings.AUTH_USER_MODEL.split(".")
    except ValueError:
        raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
    user_model = app_cache.get_model(app_label, model_name)
    if user_model is None:
        raise ImproperlyConfigured(
            "AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL
        )
    return user_model
Example #8
0
def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB_ALIAS):
    """
    This view generates KML for the given app label, model, and field name.

    The model's default manager must be GeoManager, and the field name
    must be that of a geographic field.
    """
    placemarks = []
    klass = app_cache.get_model(label, model)
    if not klass:
        raise Http404('You must supply a valid app label and module name.  Got "%s.%s"' % (label, model))

    if field_name:
        try:
            field, _, _, _ = klass._meta.get_field_by_name(field_name)
            if not isinstance(field, GeometryField):
                raise FieldDoesNotExist
        except FieldDoesNotExist:
            raise Http404('Invalid geometry field.')

    connection = connections[using]

    if connection.ops.postgis:
        # PostGIS will take care of transformation.
        placemarks = klass._default_manager.using(using).kml(field_name=field_name)
    else:
        # There's no KML method on Oracle or MySQL, so we use the `kml`
        # attribute of the lazy geometry instead.
        placemarks = []
        if connection.ops.oracle:
            qs = klass._default_manager.using(using).transform(4326, field_name=field_name)
        else:
            qs = klass._default_manager.using(using).all()
        for mod in qs:
            mod.kml = getattr(mod, field_name).kml
            placemarks.append(mod)

    # Getting the render function and rendering to the correct.
    if compress:
        render = render_to_kmz
    else:
        render = render_to_kml
    return render('gis/kml/placemarks.kml', {'places': placemarks})
Example #9
0
 def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
     if isinstance(sender, six.string_types):
         try:
             app_label, object_name = sender.split('.')
         except ValueError:
             raise ValueError(
                 "Specified sender must either be a model or a "
                 "model name of the 'app_label.ModelName' form."
             )
         sender = app_cache.get_model(app_label, object_name, only_installed=False)
         if sender is None:
             reference = (app_label, object_name)
             self.unresolved_references[reference].append(
                 (receiver, weak, dispatch_uid)
             )
             return
     super(ModelSignal, self).connect(
         receiver, sender=sender, weak=weak, dispatch_uid=dispatch_uid
     )
Example #10
0
 def _get_model_from_node(self, node, attr):
     """
     Helper to look up a model from a <object model=...> or a <field
     rel=... to=...> node.
     """
     model_identifier = node.getAttribute(attr)
     if not model_identifier:
         raise base.DeserializationError(
             "<%s> node is missing the required '%s' attribute"
             % (node.nodeName, attr))
     try:
         Model = app_cache.get_model(*model_identifier.split("."))
     except TypeError:
         Model = None
     if Model is None:
         raise base.DeserializationError(
             "<%s> node has invalid model identifier: '%s'"
             % (node.nodeName, model_identifier))
     return Model
Example #11
0
    def get_context_data(self, **kwargs):
        # Get the model class.
        try:
            app_cache.get_app_config(self.kwargs['app_label'])
        except LookupError:
            raise Http404(_("App %(app_label)r not found") % self.kwargs)
        model = app_cache.get_model(self.kwargs['app_label'], self.kwargs['model_name'])
        if model is None:
            raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)

        opts = model._meta

        # Gather fields/field descriptions.
        fields = []
        for field in opts.fields:
            # ForeignKey is a special case since the field will actually be a
            # descriptor that returns the other object
            if isinstance(field, models.ForeignKey):
                data_type = field.rel.to.__name__
                app_label = field.rel.to._meta.app_label
                verbose = utils.parse_rst(
                    (_("the related `%(app_label)s.%(data_type)s` object") % {
                        'app_label': app_label, 'data_type': data_type,
                    }),
                    'model',
                    _('model:') + data_type,
                )
            else:
                data_type = get_readable_field_data_type(field)
                verbose = field.verbose_name
            fields.append({
                'name': field.name,
                'data_type': data_type,
                'verbose': verbose,
                'help_text': field.help_text,
            })

        # Gather many-to-many fields.
        for field in opts.many_to_many:
            data_type = field.rel.to.__name__
            app_label = field.rel.to._meta.app_label
            verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': app_label, 'object_name': data_type}
            fields.append({
                'name': "%s.all" % field.name,
                "data_type": 'List',
                'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name),
            })
            fields.append({
                'name': "%s.count" % field.name,
                'data_type': 'Integer',
                'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name),
            })

        # Gather model methods.
        for func_name, func in model.__dict__.items():
            if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1):
                try:
                    for exclude in MODEL_METHODS_EXCLUDE:
                        if func_name.startswith(exclude):
                            raise StopIteration
                except StopIteration:
                    continue
                verbose = func.__doc__
                if verbose:
                    verbose = utils.parse_rst(utils.trim_docstring(verbose), 'model', _('model:') + opts.model_name)
                fields.append({
                    'name': func_name,
                    'data_type': get_return_data_type(func_name),
                    'verbose': verbose,
                })

        # Gather related objects
        for rel in opts.get_all_related_objects() + opts.get_all_related_many_to_many_objects():
            verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name}
            accessor = rel.get_accessor_name()
            fields.append({
                'name': "%s.all" % accessor,
                'data_type': 'List',
                'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name),
            })
            fields.append({
                'name': "%s.count" % accessor,
                'data_type': 'Integer',
                'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name),
            })
        kwargs.update({
            'name': '%s.%s' % (opts.app_label, opts.object_name),
            # Translators: %s is an object type name
            'summary': _("Attributes on %s objects") % opts.object_name,
            'description': model.__doc__,
            'fields': fields,
        })
        return super(ModelDetailView, self).get_context_data(**kwargs)
Example #12
0
 def test_get_model_with_not_installed(self):
     self.assertEqual(
         app_cache.get_model(
             "not_installed", "NotInstalledModel", only_installed=False),
         self.not_installed_module.NotInstalledModel)
Example #13
0
 def test_get_model_only_returns_installed_models(self):
     self.assertEqual(
         app_cache.get_model("not_installed", "NotInstalledModel"), None)
Example #14
0
 def model_class(self):
     "Returns the Python model class for this type of content."
     return app_cache.get_model(self.app_label, self.model,
                             only_installed=False)
Example #15
0
def post_comment(request, next=None, using=None):
    """
    Post a comment.

    HTTP POST is required. If ``POST['submit'] == "preview"`` or if there are
    errors a preview template, ``comments/preview.html``, will be rendered.
    """
    # Fill out some initial data fields from an authenticated user, if present
    data = request.POST.copy()
    if request.user.is_authenticated():
        if not data.get('name', ''):
            data["name"] = request.user.get_full_name() or request.user.get_username()
        if not data.get('email', ''):
            data["email"] = request.user.email

    # Look up the object we're trying to comment about
    ctype = data.get("content_type")
    object_pk = data.get("object_pk")
    if ctype is None or object_pk is None:
        return CommentPostBadRequest("Missing content_type or object_pk field.")
    try:
        model = app_cache.get_model(*ctype.split(".", 1))
        target = model._default_manager.using(using).get(pk=object_pk)
    except TypeError:
        return CommentPostBadRequest(
            "Invalid content_type value: %r" % escape(ctype))
    except AttributeError:
        return CommentPostBadRequest(
            "The given content-type %r does not resolve to a valid model." % \
                escape(ctype))
    except ObjectDoesNotExist:
        return CommentPostBadRequest(
            "No object matching content-type %r and object PK %r exists." % \
                (escape(ctype), escape(object_pk)))
    except (ValueError, ValidationError) as e:
        return CommentPostBadRequest(
            "Attempting go get content-type %r and object PK %r exists raised %s" % \
                (escape(ctype), escape(object_pk), e.__class__.__name__))

    # Do we want to preview the comment?
    preview = "preview" in data

    # Construct the comment form
    form = comments.get_form()(target, data=data)

    # Check security information
    if form.security_errors():
        return CommentPostBadRequest(
            "The comment form failed security verification: %s" % \
                escape(str(form.security_errors())))

    # If there are errors or if we requested a preview show the comment
    if form.errors or preview:
        template_list = [
            # These first two exist for purely historical reasons.
            # Django v1.0 and v1.1 allowed the underscore format for
            # preview templates, so we have to preserve that format.
            "comments/%s_%s_preview.html" % (model._meta.app_label, model._meta.model_name),
            "comments/%s_preview.html" % model._meta.app_label,
            # Now the usual directory based template hierarchy.
            "comments/%s/%s/preview.html" % (model._meta.app_label, model._meta.model_name),
            "comments/%s/preview.html" % model._meta.app_label,
            "comments/preview.html",
        ]
        return render_to_response(
            template_list, {
                "comment": form.data.get("comment", ""),
                "form": form,
                "next": data.get("next", next),
            },
            RequestContext(request, {})
        )

    # Otherwise create the comment
    comment = form.get_comment_object()
    comment.ip_address = request.META.get("REMOTE_ADDR", None)
    if request.user.is_authenticated():
        comment.user = request.user

    # Signal that the comment is about to be saved
    responses = signals.comment_will_be_posted.send(
        sender=comment.__class__,
        comment=comment,
        request=request
    )

    for (receiver, response) in responses:
        if response == False:
            return CommentPostBadRequest(
                "comment_will_be_posted receiver %r killed the comment" % receiver.__name__)

    # Save the comment and signal that it was saved
    comment.save()
    signals.comment_was_posted.send(
        sender=comment.__class__,
        comment=comment,
        request=request
    )

    return next_redirect(request, fallback=next or 'comments-comment-done',
        c=comment._get_pk_val())
Example #16
0
    def handle(self, *app_labels, **options):
        from django.apps import app_cache

        format = options.get('format')
        indent = options.get('indent')
        using = options.get('database')
        excludes = options.get('exclude')
        show_traceback = options.get('traceback')
        use_natural_keys = options.get('use_natural_keys')
        if use_natural_keys:
            warnings.warn("``--natural`` is deprecated; use ``--natural-foreign`` instead.",
                PendingDeprecationWarning)
        use_natural_foreign_keys = options.get('use_natural_foreign_keys') or use_natural_keys
        use_natural_primary_keys = options.get('use_natural_primary_keys')
        use_base_manager = options.get('use_base_manager')
        pks = options.get('primary_keys')

        if pks:
            primary_keys = pks.split(',')
        else:
            primary_keys = []

        excluded_apps = set()
        excluded_models = set()
        for exclude in excludes:
            if '.' in exclude:
                app_label, model_name = exclude.split('.', 1)
                model_obj = app_cache.get_model(app_label, model_name)
                if not model_obj:
                    raise CommandError('Unknown model in excludes: %s' % exclude)
                excluded_models.add(model_obj)
            else:
                try:
                    app_obj = app_cache.get_app_config(exclude).models_module
                    if app_obj is not None:
                        excluded_apps.add(app_obj)
                except LookupError:
                    raise CommandError('Unknown app in excludes: %s' % exclude)

        if len(app_labels) == 0:
            if primary_keys:
                raise CommandError("You can only use --pks option with one model")
            app_list = OrderedDict((app_config.models_module, None)
                for app_config in app_cache.get_app_configs(only_with_models_module=True)
                if app_config.models_module not in excluded_apps)
        else:
            if len(app_labels) > 1 and primary_keys:
                raise CommandError("You can only use --pks option with one model")
            app_list = OrderedDict()
            for label in app_labels:
                try:
                    app_label, model_label = label.split('.')
                    try:
                        app = app_cache.get_app_config(app_label).models_module
                    except LookupError:
                        raise CommandError("Unknown application: %s" % app_label)
                    if app is None or app in excluded_apps:
                        continue
                    model = app_cache.get_model(app_label, model_label)
                    if model is None:
                        raise CommandError("Unknown model: %s.%s" % (app_label, model_label))

                    if app in app_list.keys():
                        if app_list[app] and model not in app_list[app]:
                            app_list[app].append(model)
                    else:
                        app_list[app] = [model]
                except ValueError:
                    if primary_keys:
                        raise CommandError("You can only use --pks option with one model")
                    # This is just an app - no model qualifier
                    app_label = label
                    try:
                        app = app_cache.get_app_config(app_label).models_module
                    except LookupError:
                        raise CommandError("Unknown application: %s" % app_label)
                    if app is None or app in excluded_apps:
                        continue
                    app_list[app] = None

        # Check that the serialization format exists; this is a shortcut to
        # avoid collating all the objects and _then_ failing.
        if format not in serializers.get_public_serializer_formats():
            try:
                serializers.get_serializer(format)
            except serializers.SerializerDoesNotExist:
                pass

            raise CommandError("Unknown serialization format: %s" % format)

        def get_objects():
            # Collate the objects to be serialized.
            for model in sort_dependencies(app_list.items()):
                if model in excluded_models:
                    continue
                if not model._meta.proxy and router.allow_migrate(using, model):
                    if use_base_manager:
                        objects = model._base_manager
                    else:
                        objects = model._default_manager

                    queryset = objects.using(using).order_by(model._meta.pk.name)
                    if primary_keys:
                        queryset = queryset.filter(pk__in=primary_keys)
                    for obj in queryset.iterator():
                        yield obj

        try:
            self.stdout.ending = None
            serializers.serialize(format, get_objects(), indent=indent,
                    use_natural_foreign_keys=use_natural_foreign_keys,
                    use_natural_primary_keys=use_natural_primary_keys,
                    stream=self.stdout)
        except Exception as e:
            if show_traceback:
                raise
            raise CommandError("Unable to serialize database: %s" % e)
Example #17
0
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 of all installed apps. Writes errors, if any, to outfile.
    Returns number of errors.
    """
    from django.apps import app_cache
    from django.db import connection, models
    from django.db.models.deletion import SET_NULL, SET_DEFAULT

    e = ModelErrorCollection(outfile)

    for cls in app_cache.get_models(app, include_swapped=True):
        opts = cls._meta

        # Check swappable attribute.
        if opts.swapped:
            try:
                app_label, model_name = opts.swapped.split('.')
            except ValueError:
                e.add(opts, "%s is not of the form 'app_label.app_name'." % opts.swappable)
                continue
            if not app_cache.get_model(app_label, model_name):
                e.add(opts, "Model has been swapped out for '%s' which has not been installed or is abstract." % opts.swapped)
            # No need to perform any other validation checks on a swapped model.
            continue

        # If this is the current User model, check known validation problems with User models
        if settings.AUTH_USER_MODEL == '%s.%s' % (opts.app_label, opts.object_name):
            # Check that REQUIRED_FIELDS is a list
            if not isinstance(cls.REQUIRED_FIELDS, (list, tuple)):
                e.add(opts, 'The REQUIRED_FIELDS must be a list or tuple.')

            # Check that the USERNAME FIELD isn't included in REQUIRED_FIELDS.
            if cls.USERNAME_FIELD in cls.REQUIRED_FIELDS:
                e.add(opts, 'The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.')

            # Check that the username field is unique
            if not opts.get_field(cls.USERNAME_FIELD).unique:
                e.add(opts, 'The USERNAME_FIELD must be unique. Add unique=True to the field parameters.')

        # Store a list of column names which have already been used by other fields.
        used_column_names = []

        # Model isn't swapped; do field-specific validation.
        for f in opts.local_fields:
            if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
                e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name)
            if f.name.endswith('_'):
                e.add(opts, '"%s": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.' % f.name)
            if (f.primary_key and f.null and
                    not connection.features.interprets_empty_strings_as_nulls):
                # We cannot reliably check this for backends like Oracle which
                # consider NULL and '' to be equal (and thus set up
                # character-based fields a little differently).
                e.add(opts, '"%s": Primary key fields cannot have null=True.' % f.name)

            # Column name validation.
            # Determine which column name this field wants to use.
            _, column_name = f.get_attname_column()

            # Ensure the column name is not already in use.
            if column_name and column_name in used_column_names:
                e.add(opts, "Field '%s' has column name '%s' that is already used." % (f.name, column_name))
            else:
                used_column_names.append(column_name)

            if isinstance(f, models.CharField):
                try:
                    max_length = int(f.max_length)
                    if max_length <= 0:
                        e.add(opts, '"%s": CharFields require a "max_length" attribute that is a positive integer.' % f.name)
                except (ValueError, TypeError):
                    e.add(opts, '"%s": CharFields require a "max_length" attribute that is a positive integer.' % f.name)
            if isinstance(f, models.DecimalField):
                decimalp_ok, mdigits_ok = False, False
                decimalp_msg = '"%s": DecimalFields require a "decimal_places" attribute that is a non-negative integer.'
                try:
                    decimal_places = int(f.decimal_places)
                    if decimal_places < 0:
                        e.add(opts, decimalp_msg % f.name)
                    else:
                        decimalp_ok = True
                except (ValueError, TypeError):
                    e.add(opts, decimalp_msg % f.name)
                mdigits_msg = '"%s": DecimalFields require a "max_digits" attribute that is a positive integer.'
                try:
                    max_digits = int(f.max_digits)
                    if max_digits <= 0:
                        e.add(opts, mdigits_msg % f.name)
                    else:
                        mdigits_ok = True
                except (ValueError, TypeError):
                    e.add(opts, mdigits_msg % f.name)
                invalid_values_msg = '"%s": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute.'
                if decimalp_ok and mdigits_ok:
                    if decimal_places > max_digits:
                        e.add(opts, invalid_values_msg % f.name)
            if isinstance(f, models.ImageField):
                try:
                    from django.utils.image import Image  # NOQA
                except ImportError:
                    e.add(opts, '"%s": To use ImageFields, you need to install Pillow. Get it at https://pypi.python.org/pypi/Pillow.' % f.name)
            if isinstance(f, models.BooleanField) and getattr(f, 'null', False):
                e.add(opts, '"%s": BooleanFields do not accept null values. Use a NullBooleanField instead.' % f.name)
            if isinstance(f, models.FilePathField) and not (f.allow_files or f.allow_folders):
                e.add(opts, '"%s": FilePathFields must have either allow_files or allow_folders set to True.' % f.name)
            if isinstance(f, models.GenericIPAddressField) and not getattr(f, 'null', False) and getattr(f, 'blank', False):
                e.add(opts, '"%s": GenericIPAddressField can not accept blank values if null values are not allowed, as blank values are stored as null.' % f.name)
            if f.choices:
                if isinstance(f.choices, six.string_types) or not is_iterable(f.choices):
                    e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
                else:
                    for c in f.choices:
                        if isinstance(c, six.string_types) or not is_iterable(c) or len(c) != 2:
                            e.add(opts, '"%s": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).' % f.name)
            if f.db_index not in (None, True, False):
                e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)

            # Perform any backend-specific field validation.
            connection.validation.validate_field(e, opts, f)

            # Check if the on_delete behavior is sane
            if f.rel and hasattr(f.rel, 'on_delete'):
                if f.rel.on_delete == SET_NULL and not f.null:
                    e.add(opts, "'%s' specifies on_delete=SET_NULL, but cannot be null." % f.name)
                elif f.rel.on_delete == SET_DEFAULT and not f.has_default():
                    e.add(opts, "'%s' specifies on_delete=SET_DEFAULT, but has no default value." % f.name)

            # Check to see if the related field will clash with any existing
            # fields, m2m fields, m2m related objects or related objects
            if f.rel:
                if f.rel.to not in app_cache.get_models():
                    # If the related model is swapped, provide a hint;
                    # otherwise, the model just hasn't been installed.
                    if not isinstance(f.rel.to, six.string_types) and 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))
                    else:
                        e.add(opts, "'%s' has a relation with model %s, which has either not been installed or is abstract." % (f.name, f.rel.to))
                # it is a string and we could not find the model it refers to
                # so skip the next section
                if isinstance(f.rel.to, six.string_types):
                    continue

                # Make sure the related field specified by a ForeignKey is unique
                if f.requires_unique_target:
                    if len(f.foreign_related_fields) > 1:
                        has_unique_field = False
                        for rel_field in f.foreign_related_fields:
                            has_unique_field = has_unique_field or rel_field.unique
                        if not has_unique_field:
                            e.add(opts, "Field combination '%s' under model '%s' must have a unique=True constraint" % (','.join(rel_field.name for rel_field in f.foreign_related_fields), f.rel.to.__name__))
                    else:
                        if not f.foreign_related_fields[0].unique:
                            e.add(opts, "Field '%s' under model '%s' must have a unique=True constraint." % (f.foreign_related_fields[0].name, f.rel.to.__name__))

                rel_opts = f.rel.to._meta
                rel_name = f.related.get_accessor_name()
                rel_query_name = f.related_query_name()
                if not f.rel.is_hidden():
                    for r in rel_opts.fields:
                        if r.name == rel_name:
                            e.add(opts, "Accessor for field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                        if r.name == rel_query_name:
                            e.add(opts, "Reverse query name for field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                    for r in rel_opts.local_many_to_many:
                        if r.name == rel_name:
                            e.add(opts, "Accessor for field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                        if r.name == rel_query_name:
                            e.add(opts, "Reverse query name for field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                    for r in rel_opts.get_all_related_many_to_many_objects():
                        if r.get_accessor_name() == rel_name:
                            e.add(opts, "Accessor for field '%s' clashes with accessor for field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))
                        if r.get_accessor_name() == rel_query_name:
                            e.add(opts, "Reverse query name for field '%s' clashes with accessor for field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))
                    for r in rel_opts.get_all_related_objects():
                        if r.field is not f:
                            if r.get_accessor_name() == rel_name:
                                e.add(opts, "Accessor for field '%s' clashes with accessor for field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))
                            if r.get_accessor_name() == rel_query_name:
                                e.add(opts, "Reverse query name for field '%s' clashes with accessor for field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))

        seen_intermediary_signatures = []
        for i, f in enumerate(opts.local_many_to_many):
            # Check to see if the related m2m field will clash with any
            # existing fields, m2m fields, m2m related objects or related
            # objects
            if f.rel.to not in app_cache.get_models():
                # If the related model is swapped, provide a hint;
                # otherwise, the model just hasn't been installed.
                if not isinstance(f.rel.to, six.string_types) and 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))
                else:
                    e.add(opts, "'%s' has an m2m relation with model %s, which has either not been installed or is abstract." % (f.name, f.rel.to))

                # it is a string and we could not find the model it refers to
                # so skip the next section
                if isinstance(f.rel.to, six.string_types):
                    continue

            # Check that the field is not set to unique.  ManyToManyFields do not support unique.
            if f.unique:
                e.add(opts, "ManyToManyFields cannot be unique.  Remove the unique argument on '%s'." % f.name)

            if f.rel.through is not None and not isinstance(f.rel.through, six.string_types):
                from_model, to_model = cls, f.rel.to
                if from_model == to_model and f.rel.symmetrical and not f.rel.through._meta.auto_created:
                    e.add(opts, "Many-to-many fields with intermediate tables cannot be symmetrical.")
                seen_from, seen_to, seen_self = False, False, 0
                for inter_field in f.rel.through._meta.fields:
                    rel_to = getattr(inter_field.rel, 'to', None)
                    if from_model == to_model:  # relation to self
                        if rel_to == from_model:
                            seen_self += 1
                        if seen_self > 2:
                            e.add(opts, "Intermediary model %s has more than "
                                "two foreign keys to %s, which is ambiguous "
                                "and is not permitted." % (
                                    f.rel.through._meta.object_name,
                                    from_model._meta.object_name
                                )
                            )
                    else:
                        if rel_to == from_model:
                            if seen_from:
                                e.add(opts, "Intermediary model %s has more "
                                    "than one foreign key to %s, which is "
                                    "ambiguous and is not permitted." % (
                                        f.rel.through._meta.object_name,
                                        from_model._meta.object_name
                                    )
                                )
                            else:
                                seen_from = True
                        elif rel_to == to_model:
                            if seen_to:
                                e.add(opts, "Intermediary model %s has more "
                                    "than one foreign key to %s, which is "
                                    "ambiguous and is not permitted." % (
                                        f.rel.through._meta.object_name,
                                        rel_to._meta.object_name
                                    )
                                )
                            else:
                                seen_to = True
                if f.rel.through not in app_cache.get_models(include_auto_created=True):
                    e.add(opts, "'%s' specifies an m2m relation through model "
                        "%s, which has not been installed." % (f.name, f.rel.through))
                signature = (f.rel.to, cls, f.rel.through)
                if signature in seen_intermediary_signatures:
                    e.add(opts, "The model %s has two manually-defined m2m "
                        "relations through the model %s, which is not "
                        "permitted. Please consider using an extra field on "
                        "your intermediary model instead." % (
                            cls._meta.object_name,
                            f.rel.through._meta.object_name
                        )
                    )
                else:
                    seen_intermediary_signatures.append(signature)
                if not f.rel.through._meta.auto_created:
                    seen_related_fk, seen_this_fk = False, False
                    for field in f.rel.through._meta.fields:
                        if field.rel:
                            if not seen_related_fk and field.rel.to == f.rel.to:
                                seen_related_fk = True
                            elif field.rel.to == cls:
                                seen_this_fk = True
                    if not seen_related_fk or not seen_this_fk:
                        e.add(opts, "'%s' is a manually-defined m2m relation "
                            "through model %s, which does not have foreign keys "
                            "to %s and %s" % (
                                f.name, f.rel.through._meta.object_name,
                                f.rel.to._meta.object_name, cls._meta.object_name
                            )
                        )
            elif isinstance(f.rel.through, six.string_types):
                e.add(opts, "'%s' specifies an m2m relation through model %s, "
                    "which has not been installed" % (f.name, f.rel.through))

            rel_opts = f.rel.to._meta
            rel_name = f.related.get_accessor_name()
            rel_query_name = f.related_query_name()
            # If rel_name is none, there is no reverse accessor (this only
            # occurs for symmetrical m2m relations to self). If this is the
            # case, there are no clashes to check for this field, as there are
            # no reverse descriptors for this field.
            if not f.rel.is_hidden():
                for r in rel_opts.fields:
                    if r.name == rel_name:
                        e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                    if r.name == rel_query_name:
                        e.add(opts, "Reverse query name for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                for r in rel_opts.local_many_to_many:
                    if r.name == rel_name:
                        e.add(opts, "Accessor for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                    if r.name == rel_query_name:
                        e.add(opts, "Reverse query name for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
                for r in rel_opts.get_all_related_many_to_many_objects():
                    if r.field is not f:
                        if r.get_accessor_name() == rel_name:
                            e.add(opts, "Accessor for m2m field '%s' clashes with accessor for m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))
                        if r.get_accessor_name() == rel_query_name:
                            e.add(opts, "Reverse query name for m2m field '%s' clashes with accessor for m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))
                for r in rel_opts.get_all_related_objects():
                    if r.get_accessor_name() == rel_name:
                        e.add(opts, "Accessor for m2m field '%s' clashes with accessor for field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))
                    if r.get_accessor_name() == rel_query_name:
                        e.add(opts, "Reverse query name for m2m field '%s' clashes with accessor for field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, r.model._meta.object_name, r.field.name, f.name))

        # Check ordering attribute.
        if opts.ordering:
            for field_name in opts.ordering:
                if field_name == '?':
                    continue
                if field_name.startswith('-'):
                    field_name = field_name[1:]
                if opts.order_with_respect_to and field_name == '_order':
                    continue
                # Skip ordering in the format field1__field2 (FIXME: checking
                # this format would be nice, but it's a little fiddly).
                if '__' in field_name:
                    continue
                # Skip ordering on pk. This is always a valid order_by field
                # but is an alias and therefore won't be found by opts.get_field.
                if field_name == 'pk':
                    continue
                try:
                    opts.get_field(field_name, many_to_many=False)
                except models.FieldDoesNotExist:
                    e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)

        # Check unique_together.
        for ut in opts.unique_together:
            validate_local_fields(e, opts, "unique_together", ut)
        if not isinstance(opts.index_together, collections.Sequence):
            e.add(opts, '"index_together" must a sequence')
        else:
            for it in opts.index_together:
                validate_local_fields(e, opts, "index_together", it)

    validate_model_signals(e)

    return len(e.errors)
Example #18
0
def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, **kwargs):
    """
    Creates content types for models in the given app, removing any model
    entries that no longer have a matching model class.
    """
    try:
        app_cache.get_model('contenttypes', 'ContentType')
    except UnavailableApp:
        return

    if not router.allow_migrate(db, ContentType):
        return

    ContentType.objects.clear_cache()
    app_models = app_cache.get_models(app)
    if not app_models:
        return
    # They all have the same app_label, get the first one.
    app_label = app_models[0]._meta.app_label
    app_models = dict(
        (model._meta.model_name, model)
        for model in app_models
    )

    # Get all the content types
    content_types = dict(
        (ct.model, ct)
        for ct in ContentType.objects.using(db).filter(app_label=app_label)
    )
    to_remove = [
        ct
        for (model_name, ct) in six.iteritems(content_types)
        if model_name not in app_models
    ]

    cts = [
        ContentType(
            name=smart_text(model._meta.verbose_name_raw),
            app_label=app_label,
            model=model_name,
        )
        for (model_name, model) in six.iteritems(app_models)
        if model_name not in content_types
    ]
    ContentType.objects.using(db).bulk_create(cts)
    if verbosity >= 2:
        for ct in cts:
            print("Adding content type '%s | %s'" % (ct.app_label, ct.model))

    # Confirm that the content type is stale before deletion.
    if to_remove:
        if kwargs.get('interactive', False):
            content_type_display = '\n'.join(
                '    %s | %s' % (ct.app_label, ct.model)
                for ct in to_remove
            )
            ok_to_delete = input("""The following content types are stale and need to be deleted:

%s

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

    Type 'yes' to continue, or 'no' to cancel: """ % content_type_display)
        else:
            ok_to_delete = False

        if ok_to_delete == 'yes':
            for ct in to_remove:
                if verbosity >= 2:
                    print("Deleting stale content type '%s | %s'" % (ct.app_label, ct.model))
                ct.delete()
        else:
            if verbosity >= 2:
                print("Stale content types remain.")
Example #19
0
def sort_dependencies(app_list):
    """Sort a list of app,modellist pairs into a single list of models.

    The single list of models is sorted so that any model with a natural key
    is serialized before a normal model, and any model with a natural key
    dependency has it's dependencies serialized first.
    """
    from django.apps import app_cache
    # Process the list of models, and get the list of dependencies
    model_dependencies = []
    models = set()
    for app, model_list in app_list:
        if model_list is None:
            model_list = app_cache.get_models(app)

        for model in model_list:
            models.add(model)
            # Add any explicitly defined dependencies
            if hasattr(model, 'natural_key'):
                deps = getattr(model.natural_key, 'dependencies', [])
                if deps:
                    deps = [app_cache.get_model(*d.split('.')) for d in deps]
            else:
                deps = []

            # Now add a dependency for any FK or M2M relation with
            # a model that defines a natural key
            for field in model._meta.fields:
                if hasattr(field.rel, 'to'):
                    rel_model = field.rel.to
                    if hasattr(rel_model, 'natural_key') and rel_model != model:
                        deps.append(rel_model)
            for field in model._meta.many_to_many:
                rel_model = field.rel.to
                if hasattr(rel_model, 'natural_key') and rel_model != model:
                    deps.append(rel_model)
            model_dependencies.append((model, deps))

    model_dependencies.reverse()
    # Now sort the models to ensure that dependencies are met. This
    # is done by repeatedly iterating over the input list of models.
    # If all the dependencies of a given model are in the final list,
    # that model is promoted to the end of the final list. This process
    # continues until the input list is empty, or we do a full iteration
    # over the input models without promoting a model to the final list.
    # If we do a full iteration without a promotion, that means there are
    # circular dependencies in the list.
    model_list = []
    while model_dependencies:
        skipped = []
        changed = False
        while model_dependencies:
            model, deps = model_dependencies.pop()

            # If all of the models in the dependency list are either already
            # on the final model list, or not on the original serialization list,
            # then we've found another model with all it's dependencies satisfied.
            found = True
            for candidate in ((d not in models or d in model_list) for d in deps):
                if not candidate:
                    found = False
            if found:
                model_list.append(model)
                changed = True
            else:
                skipped.append((model, deps))
        if not changed:
            raise CommandError("Can't resolve dependencies for %s in serialized app list." %
                ', '.join('%s.%s' % (model._meta.app_label, model._meta.object_name)
                for model, deps in sorted(skipped, key=lambda obj: obj[0].__name__))
            )
        model_dependencies = skipped

    return model_list