class FieldFactory(object):
    """
    Creates the correct field class based on the type of the passed field
    """

    def __new__(cls, field, *args, **kwargs):
        """
        Determines which type of field class to instantiate based on the field argument

        :param field: The field used in determining which type of Field object to return.
            This can be a string of the field name, a dict of {'alias': field},
            or a ``Field``
        :type table: str or dict or :class:`Field <querybuilder.fields.Field>`

        :return: The Field instance if a valid type was determined, otherwise None
        :rtype: :class:`Field <querybuilder.fields.Field>` or None
        """
        field_type = type(field)
        if field_type is dict:
            kwargs.update(alias=list(field.keys())[0])
            field = list(field.values())[0]
            field_type = type(field)

        if isinstance(field, string_types):
            return SimpleField(field, **kwargs)
        elif isinstance(field, Field):
            for key, value in kwargs.items():
                setattr(field, key, value)
            return field
        return None class CountField(AggregateField):
    """
    Count aggregation
    """
    function_name = 'Count' class MaxField(AggregateField):
    """
    Maximum aggregation
    """
    function_name = 'Max' class MinField(AggregateField):
    """
    Minimum aggregation
    """
    function_name = 'Min' class SumField(AggregateField):
    """
    Summation aggregation
    """
    function_name = 'Sum' class AvgField(AggregateField):
    """
    Average aggregation
    """
    function_name = 'Avg' def set_value_for_keypath(item, keypath, value, create_if_needed=False, delimeter='.'):
    """
    Sets the value for a keypath in a dictionary
    if the keypath exists. This modifies the
    original dictionary.
    """

    if len(keypath) == 0:
        return None

    keys = keypath.split(delimeter)
    if len(keys) > 1:
        key = keys[0]

        if create_if_needed:
            item[key] = item.get(key, {})

        if key in item:
            if set_value_for_keypath(item[key], delimeter.join(keys[1:]), value,
                                     create_if_needed=create_if_needed, delimeter=delimeter):
                return item

        return None

    if create_if_needed:
        item[keypath] = item.get(keypath, {})

    if keypath in item:
        item[keypath] = value
        return item
    else:
        return None class TableFactory(object):
    """
    Creates the correct table class based on the type of the passed table
    """
    def __new__(cls, table, *args, **kwargs):
        """
        Determines which type of table class to instantiate based on the table argument

        :param table: The table used in determining which type of Table object to return.
            This can be a string of the table name, a dict of {'alias': table},
            a ``Table`` instance, a django model class, or a Query instance
        :type table: str or dict or :class:`Table <querybuilder.tables.Table>` or
            :class:`ModelBase <django:django.db.models.base.ModelBase>`
            or :class:`Query <querybuilder.query.Query>`

        :returns: The Table instance if a valid type was determined, otherwise None
        :rtype: :class:`Table <querybuilder.tables.Table>` or None
        """
        # Determine the type of the table
        table_type = type(table)
        if table_type is dict:
            kwargs.update(alias=list(table.keys())[0])
            table = list(table.values())[0]
            table_type = type(table)
        if isinstance(table, string_types):
            return SimpleTable(table, **kwargs)
        elif issubclass(table_type, ModelBase):
            return ModelTable(table, **kwargs)
        elif table_type is querybuilder.query.Query:
            return QueryTable(table, **kwargs)
        elif isinstance(table, Table):
            for key, value in kwargs.items():
                setattr(table, key, value)
            return table
        return None class ModelTable(Table):
    """
    A table that is created by passing a django model for the table field. This allows
    fields to be extract and for joins to be made without specifying a condition.
    """

    def init_defaults(self):
        """
        Sets a model instance variable to the table value and sets the name to the
        table name as determined from the model class
        """
        super(ModelTable, self).init_defaults()
        self.model = self.table
        self.name = self.model._meta.db_table

    def before_add_field(self, field):
        """
        If extract_fields is set to True, then '*' fields will be removed and each
        individual field will read from the model meta data and added.
        """
        if self.extract_fields and field.name == '*':
            field.ignore = True
            fields = [model_field.column for model_field in self.model._meta.fields]
            self.add_fields(fields) class QueryTable(Table):
    """
    A table that contains a Query object. This is used for inner queries in more complex
    queries, usually involving window functions or some sort of aggregation.
    """

    def init_defaults(self):
        """
        Sets a query instance variable to the table value
        """
        super(QueryTable, self).init_defaults()
        self.query = self.table
        self.query.is_inner = True

    def get_sql(self):
        return self.get_identifier()

    def get_from_name(self):
        """
        Return the query sql in the FROM clause of the query when building the table sql
        """
        return '({0})'.format(self.query.get_sql())

    def get_with_sql(self):
        return '{0} AS ({1})'.format(self.get_identifier(), self.query.get_sql())
from copy import deepcopy
from django.db import connection as default_django_connection
from django.db.models import Q, AutoField
from django.db.models.query import QuerySet
from django.db.models.constants import LOOKUP_SEP
from django.apps import apps
from querybuilder.fields import FieldFactory, CountField, MaxField, MinField, SumField, AvgField
from querybuilder.helpers import set_value_for_keypath
from querybuilder.tables import TableFactory, ModelTable, QueryTable
import six
            or ``Field`` instance
        :type fields: str or tuple or list or :class:`Field <querybuilder.fields.Field>`

        :param condition: The join condition specifying the fields being joined. If the two tables being
            joined are instances of ``ModelTable`` then the condition should be created automatically.
        :type condition: str

        :param join_type: The type of join (JOIN, LEFT JOIN, INNER JOIN, etc). Defaults to 'JOIN'
        :type join_type: str

        :param schema: This is not implemented, but it will be a string of the db schema name
        :type schema: str

        :param left_table: The left table being joined with. This can be a string of the table
            name, a dict of {'alias': table}, or a ``Table`` instance. Defaults to the first table
            in the query.
        :type left_table: str or dict or :class:`Table <querybuilder.tables.Table>` or None

        :param owner: A reference to the query managing this Join object
        :type owner: :class:`Query <querybuilder.query.Query>`

        :param extract_fields: If True and joining with a ``ModelTable``, then '*'
            fields will be converted to individual fields for each column in the table. Defaults
            to True.
        :type extract_fields: bool

        :param prefix_fields: If True, then the joined table will have each of its field names
            prefixed with the field_prefix. If no field_prefix is specified, a name will be
            generated based on the join field name. This is usually used with nesting results
            in order to create models in python or javascript. Defaults to True.
        :type prefix_fields: bool

        :param field_prefix: The field prefix to be used in front of each field name if prefix_fields
            is set to True. If no field_prefix is set, one will be automatically created based on
            the join field name.
        :type field_prefix: str
        """
        self.owner = owner
        self.left_table = None
        self.right_table = None
        # self.table_join_name = None
        self.prefix_fields = prefix_fields
        self.condition = condition
        self.join_type = join_type
        self.schema = schema

        self.set_left_table(left_table=left_table)
        self.set_right_table(TableFactory(
            table=right_table,
            fields=fields,
            extract_fields=extract_fields,
            prefix_fields=prefix_fields,
            owner=self.owner,
            field_prefix=field_prefix,
        ))

    def get_sql(self):
        """
        Generates the JOIN sql for the join tables and join condition

        :rtype: str
        :return: the JOIN sql for the join tables and join condition
        """
        return '{0} {1} ON {2}'.format(self.join_type, self.right_table.get_sql(), self.get_condition())

    def set_left_table(self, left_table=None):
        """
        Sets the left table for this join clause. If no table is specified, the first table
        in the query will be used

        :type left_table: str or dict or :class:`Table <querybuilder.tables.Table>` or None
        :param left_table: The left table being joined with. This can be a string of the table
            name, a dict of {'alias': table}, or a ``Table`` instance. Defaults to the first table
            in the query.
        """
        if left_table:
            self.left_table = TableFactory(
                table=left_table,
                owner=self.owner,
            )
        else:
            self.left_table = self.get_left_table()

    def get_left_table(self):
        """
        Returns the left table if one was specified, otherwise the first
        table in the query is returned

        :rtype: :class:`Table <querybuilder.tables.Table>`
        :return: the left table if one was specified, otherwise the first table in the query
        """
        if self.left_table:
            return self.left_table
        if len(self.owner.tables):
            return self.owner.tables[0]

    def get_all_related_objects(self, table):
        """
        Fix for django 1.10 to replace deprecated code. Keep support for django 1.7
        """
        # Django 1.7 method
        if hasattr(table.model._meta, 'get_all_related_objects'):
            return table.model._meta.get_all_related_objects()
        else:
            # Django > 1.7
            return [
                f for f in table.model._meta.get_fields()
                if (f.one_to_many or f.one_to_one) and f.auto_created and not f.concrete
            ]

    def set_right_table(self, table):
        """
        Sets the right table for this join clause and try to automatically set the condition
        if one isn't specified
        """
        self.right_table = table
        if self.left_table is None:
            return

        # find table prefix

        if type(self.left_table) is ModelTable and type(self.right_table) is ModelTable: