def join_to_table(
        table,
        table2,
        table_field,
        table2_field,
        queryset,
        alias,
        extra_restriction_func=lambda where_class, alias, related_alias: None):
    """
    Add a join on `table2` to `queryset` (having table `table`).
    """
    foreign_object = ForeignObject(to=table2,
                                   from_fields=[None],
                                   to_fields=[None],
                                   rel=None)
    foreign_object.opts = Options(table._meta)
    foreign_object.opts.model = table
    foreign_object.get_joining_columns = lambda: (
        (table_field, table2_field), )
    foreign_object.get_extra_restriction = extra_restriction_func

    join = Join(table2._meta.db_table, table._meta.db_table, alias, LOUTER,
                foreign_object, True)
    queryset.query.join(join)

    # hook for set alias
    join.table_alias = alias
    queryset.query.external_aliases.add(alias)

    return queryset
Esempio n. 2
0
    def get_joins(self):
        """Returns a BaseTable and a list of Join objects that need to be added
        to a QuerySet object that properly joins this model and the parent.
        """
        # These arguments should match the spec of the Join object.
        # See https://github.com/django/django/blob/1.8.7/django/db/models/sql/query.py#L896  # noqa
        join_args = {
            'table_name': None,
            'parent_alias': None,
            'table_alias': None,
            'join_type': None,
            'join_field': None,
            'nullable': self.nullable,
        }

        joins = []

        related_field = self.parent_model._meta.get_field(self.related_name)
        # Setup two connections for m2m.
        if self.relation == 'manytomany':
            path = related_field.get_path_info()

            copy1 = join_args.copy()
            copy1.update({
                'join_field': path[0].join_field,
                'parent_alias': self.parent.db_table,
                'table_name': self.m2m_db_table,
                'join_type': LOUTER,
            })

            copy2 = join_args.copy()
            copy2.update({
                'join_field': path[1].join_field,
                'parent_alias': self.m2m_db_table,
                'table_name': self.db_table,
                'join_type': LOUTER,
            })
            joins.append(Join(**copy1))
            joins.append(Join(**copy2))
        else:
            copy = join_args.copy()
            copy.update({
                'table_name': self.db_table,
                'parent_alias': self.parent.db_table,
                'join_field': related_field,
                'join_type': LOUTER if self.nullable else INNER,
            })

            joins.append(Join(**copy))

        return BaseTable(self.parent.db_table, alias=None), joins
Esempio n. 3
0
def order_by_translation(qs, fieldname, model=None):
    """
    Order the QuerySet by the translated field, honoring the current and
    fallback locales.  Returns a new QuerySet.

    The model being sorted needs a get_fallback() classmethod that describes
    the fallback locale.  get_fallback() can return a string or a Field.
    """
    if fieldname.startswith('-'):
        desc = True
        fieldname = fieldname[1:]
    else:
        desc = False

    qs = qs.all()
    model = model or qs.model
    field = model._meta.get_field(fieldname)

    # Doing the manual joins is flying under Django's radar, so we need to make
    # sure the initial alias (the main table) is set up.
    if not qs.query.table_map:
        qs.query.get_initial_alias()

    # Force two new joins against the translation table, without reusing any
    # aliases. We'll hook up the language fallbacks later.
    # Passing `reuse=set()` force new joins, and passing `nullable=True`
    # forces django to make LEFT OUTER JOINs (otherwise django, because we are
    # building the query manually, does not detect that an inner join would
    # remove results and happily simplifies the LEFT OUTER JOINs to
    # INNER JOINs)
    qs.query = qs.query.clone()
    qs.query.__class__ = TranslationQuery
    qs.query.fallback_model = model
    t1 = qs.query.join(Join(field.remote_field.model._meta.db_table,
                            model._meta.db_table, None, LOUTER, field, True),
                       reuse=set())
    t2 = qs.query.join(Join(field.remote_field.model._meta.db_table,
                            model._meta.db_table, None, LOUTER, field, True),
                       reuse=set())

    qs.query.translation_aliases = {field: (t1, t2)}

    f1, f2 = '%s.`localized_string`' % t1, '%s.`localized_string`' % t2
    name = 'translated_%s' % field.column
    ifnull = 'IFNULL(%s, %s)' % (f1, f2)
    prefix = '-' if desc else ''
    return qs.extra(select={name: ifnull},
                    where=['(%s IS NOT NULL OR %s IS NOT NULL)' % (f1, f2)],
                    order_by=[prefix + name])
Esempio n. 4
0
def unique_together_left_join(queryset,
                              model,
                              link_field_name,
                              filter_field_name,
                              filter_value,
                              parent_model=None):
    link_field = copy(model._meta.get_field(link_field_name).rel)
    filter_field = model._meta.get_field(filter_field_name)

    def restrictions(where_class, alias, related_alias):
        cond = where_class()
        cond.add(
            filter_field.get_lookup('exact')(filter_field.get_col(alias),
                                             filter_value), 'AND')
        return cond

    link_field.get_extra_restriction = restrictions

    if parent_model is not None:
        parent_alias = parent_model._meta.db_table
    else:
        parent_alias = queryset.query.get_initial_alias()
    return queryset.query.join(
        Join(model._meta.db_table, parent_alias, None, LOUTER, link_field,
             True))
Esempio n. 5
0
    def add_where(self, queryset, host_table, included_table):
        alias = None
        nullable = True
        join = Join(self.through_model._meta.db_table, included_table, None,
                    LOUTER, self.to_field, nullable)

        alias, _ = queryset.query.table_alias(join.table_name, create=True)
        join.table_alias = alias
        queryset.query.alias_map[alias] = join

        where = [
            '{through_table}."{from_column}" = {host_table}."{host_column}"'.
            format(through_table=join.table_alias,
                   from_column=self.from_field.field.column,
                   host_table=host_table,
                   host_column=self.from_field.target_field.column)
        ]

        queryset.query.add_extra(None, None, where, None, None, None)

        return queryset
Esempio n. 6
0
    def __init__(self, model, trigger_alias, where=TriggerWhereNode):
        super(TriggerFilterQuery, self).__init__(model, where)
        self.trigger_alias = trigger_alias
        try:

            class JoinField():
                def get_joining_columns(self):
                    return None

            join = Join(None, None, None, None, JoinField(), False)
        except:
            join = JoinInfo(None, None, None, None, ((None, None), ), False,
                            None)
        self.alias_map = {trigger_alias: join}
Esempio n. 7
0
    def _add_language_filter(self):
        if self._language_filter_tag:  # pragma: no cover
            raise RuntimeError(
                'Queryset is already tagged. This is a bug in hvad')
        self._language_filter_tag = True
        self.query.language_code = self._language_code

        if self._language_code == 'all':
            self._add_select_related()

        elif self._language_fallbacks:
            if self._raw_select_related:
                raise NotImplementedError('Using select_related along with '
                                          'fallbacks() is not supported')
            languages = tuple(get_language() if lang is None else lang
                              for lang in (self._language_code, ) +
                              self._language_fallbacks)
            masteratt = self.model._meta.get_field('master').attname
            alias = self.query.join(
                Join(self.model._meta.db_table, self.query.get_initial_alias(),
                     None, LOUTER,
                     BetterTranslationsField(languages,
                                             master=masteratt), True))

            add_alias_constraints(self, (self.model, alias), id__isnull=True)
            self.query.add_filter(('%s__isnull' % masteratt, False))
            if not self._skip_master_select and getattr(self, '_fields',
                                                        None) is None:
                self.query.add_select_related(('master', ))

        else:
            language_code = self._language_code or get_language()
            self.query.add_filter(('language_code', language_code))
            self._add_select_related()

        # if queryset is about to use the model's default ordering, we
        # override that now with a translated version of the model's ordering
        if self.query.default_ordering and not self.query.order_by:
            ordering = self.shared_model._meta.ordering
            self.query.order_by = self._translate_fieldnames(ordering or [])

        return self
Esempio n. 8
0
    def get(self, request, *args, **kwargs):
        class JoinCols:
            @staticmethod
            def get_joining_columns():
                return (('plan_id', 'plan_id'),)
            @staticmethod
            def get_extra_restriction(where_class, alias, related_alias):
                return None

        cart_items = self.cart_items.filter(option=0)
        cart_items.query.join(Join(
            AdvanceDiscount._meta.db_table, # table_name
            CartItem._meta.db_table,        # parent_alias
            None,                           # table_alias
            INNER,                          # join_type
            JoinCols(),                     # join_field
            False,                          # nullable
        ))
        if cart_items.exists():
            # If option == 0 and there is a discount to buy periods
            # in advance, we will present multiple options to the user.
            return http.HttpResponseRedirect(
                reverse('saas_cart_periods', args=(self.organization,)))
        return super(CartSeatsView, self).get(request, *args, **kwargs)
Esempio n. 9
0
    def join(self, qs=None):
        '''
        Join one queryset together with another using a temporary table. If
        no queryset is used, it will use the current queryset and join that
        to itself.

        `Join` either uses the current queryset and effectively does a self-join to
        create a new limited queryset OR it uses a querset given by the user.

        The model of a given queryset needs to contain a valid foreign key to
        the current queryset to perform a join. A new queryset is then created.
        '''
        to_field = 'id'

        if qs:
            fk = [
                fk for fk in qs.model._meta.fields
                if getattr(fk, 'related_model', None) == self.model
            ]
            fk = fk[0] if fk else None
            model_set = '{}_set'.format(self.model.__name__.lower())
            key = fk or getattr(qs.model, model_set, None)

            if not key:
                raise ValueError('QuerySet is not related to current model')

            try:
                fk_column = key.column
            except AttributeError:
                fk_column = 'id'
                to_field = key.field.column

            qs = qs.only(fk_column)
            # if we give a qs we need to keep the model qs to not lose anything
            new_qs = self
        else:
            fk_column = 'id'
            qs = self.only(fk_column)
            new_qs = self.model.objects.all()

        TABLE_NAME = 'temp_stuff'
        query = self.get_quoted_query(qs.query)
        sql = '''
            DROP TABLE IF EXISTS {table_name};
            DROP INDEX IF EXISTS {table_name}_id;
            CREATE TEMPORARY TABLE {table_name} AS {query};
            CREATE INDEX {table_name}_{fk_column} ON {table_name} ({fk_column});
        '''.format(table_name=TABLE_NAME, fk_column=fk_column, query=str(query))

        with connection.cursor() as cursor:
            cursor.execute(sql)

        class TempModel(models.Model):
            temp_key = models.ForeignKey(
                self.model,
                on_delete=models.DO_NOTHING,
                db_column=fk_column,
                to_field=to_field
            )

            class Meta:
                managed = False
                db_table = TABLE_NAME

        conn = Join(
            table_name=TempModel._meta.db_table,
            parent_alias=new_qs.query.get_initial_alias(),
            table_alias=None,
            join_type='INNER JOIN',
            join_field=self.model.tempmodel_set.rel,
            nullable=False
        )
        new_qs.query.join(conn, reuse=None)
        return new_qs