Connect to a Django database through the ORM and retrieve model fields

    :type pillar_name: str
    :param pillar_name: The name of the pillar to be returned

    :type project_path: str
    :param project_path: The full path to your Django project (the directory
        manage.py is in)

    :type settings_module: str
    :param settings_module: The settings module for your project. This can be
        found in your manage.py file

    :type django_app: str
    :param django_app: A dictionary containing your apps, models, and fields

    :type env: str
    :param env: The full path to the virtualenv for your Django project

    :type env_file: str
    :param env_file: An optional bash file that sets up your environment. The
        file is run in a subprocess and the changed variables are then added

def ext_pillar(minion_id,  # pylint: disable=W0613
               pillar,  # pylint: disable=W0613
               pillar_name,
               project_path,
               settings_module,
               django_app,
               env=None,
               env_file=None,
               *args,  # pylint: disable=W0613
               **kwargs):  # pylint: disable=W0613
    '''
    Connect to a Django database through the ORM and retrieve model fields

    :type pillar_name: str
    :param pillar_name: The name of the pillar to be returned

    :type project_path: str
    :param project_path: The full path to your Django project (the directory
        manage.py is in)

    :type settings_module: str
    :param settings_module: The settings module for your project. This can be
        found in your manage.py file

    :type django_app: str
    :param django_app: A dictionary containing your apps, models, and fields

    :type env: str
    :param env: The full path to the virtualenv for your Django project

    :type env_file: str
    :param env_file: An optional bash file that sets up your environment. The
        file is run in a subprocess and the changed variables are then added
    '''

    if not os.path.isdir(project_path):
        log.error('Django project dir: \'%s\' not a directory!', project_path)
        return {}
    if HAS_VIRTUALENV and env is not None and os.path.isdir(env):
        for path in virtualenv.path_locations(env):
            if not os.path.isdir(path):
                log.error('Virtualenv %s not a directory!', path)
                return {}
        # load the virtualenv first
        sys.path.insert(0,
                        os.path.join(
                            virtualenv.path_locations(env)[1],
                            'site-packages'))

    # load the django project
    sys.path.append(project_path)

    os.environ['DJANGO_SETTINGS_MODULE'] = settings_module

    if env_file is not None:
        import subprocess

        base_env = {}
        proc = subprocess.Popen(['bash', '-c', 'env'], stdout=subprocess.PIPE)
        for line in proc.stdout:
            (key, _, value) = salt.utils.stringutils.to_str(line).partition('=')
            base_env[key] = value

        command = ['bash', '-c', 'source {0} && env'.format(env_file)]
        proc = subprocess.Popen(command, stdout=subprocess.PIPE)

        for line in proc.stdout:
            (key, _, value) = salt.utils.stringutils.to_str(line).partition('=')
            # only add a key if it is different or doesn't already exist
            if key not in base_env or base_env[key] != value:
                os.environ[key] = value.rstrip('\n')
                log.debug('Adding %s = %s to Django environment', key, value.rstrip('\n'))

    try:
        from django.db.models.loading import get_model

        django_pillar = {}

        for proj_app, models in six.iteritems(django_app):
            _, _, app = proj_app.rpartition('.')
            django_pillar[app] = {}
            for model_name, model_meta in six.iteritems(models):
                model_orm = get_model(app, model_name)
                if model_orm is None:
                    raise salt.exceptions.SaltException(
                        "Django model '{0}' not found in app '{1}'."
                        .format(app, model_name))

                pillar_for_model = django_pillar[app][model_orm.__name__] = {}

                name_field = model_meta['name']
                fields = model_meta['fields']

                if 'filter' in model_meta:
                    qs = (model_orm.objects
                        .filter(**model_meta['filter'])
                        .values(*fields))
                else:
                    qs = model_orm.objects.values(*fields)

                for model in qs:
                    # Check that the human-friendly name given is valid (will
                    # be able to pick up a value from the query) and unique
                    # (since we're using it as the key in a dictionary)
                    if name_field not in model:
                        raise salt.exceptions.SaltException(
                            "Name '{0}' not found in returned fields.".format(
                                name_field))

                    if model[name_field] in pillar_for_model:
                        raise salt.exceptions.SaltException(
                            "Value for '{0}' is not unique: {0}".format(
                                model[name_field]))

                    pillar_for_model[model[name_field]] = model

        return {pillar_name: django_pillar}
    except ImportError as e:
        log.error('Failed to import library: %s', e)
        return {}
    except Exception as e:
        log.error('Failed on Error: %s', e)
        log.debug('django_orm traceback', exc_info=True)
        return {}