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
            # get the cursor to execute the query
            cursor = self.get_cursor()

            # execute the upsert query
            cursor.execute(sql, sql_args)

            if return_rows or return_models:
                return_value.extend(self._fetch_all_as_dict(cursor))

        if return_models:
            ModelClass = self.tables[0].model
            model_objects = [
                ModelClass(**row_dict)
                for row_dict in return_value
            ]

            # Set the state to indicate the object has been loaded from db
            for model_object in model_objects:
                model_object._state.adding = False
                model_object._state.db = 'default'

            return_value = model_objects

        return return_value

    def sql_delete(self):
        """
        Deletes records from the db
        # TODO: implement this
        """
        pass

    def get_count_query(self):
        """
        Copies the query object and alters the field list and order by to do a more efficient count
        """
        query_copy = self.copy()
        if not query_copy.tables:
            raise Exception('No tables specified to do a count')

        for table in query_copy.tables:
            del table.fields[:]

        query_copy.tables[0].add_field(CountField('*'))
        del query_copy.sorters[:]
        return query_copy

    def count(self, field='*'):
        """
        Returns a COUNT of the query by wrapping the query and performing a COUNT
        aggregate of the specified field

        :param field: the field to pass to the COUNT aggregate. Defaults to '*'
        :type field: str

        :return: The number of rows that the query will return
        :rtype: int
        """
        rows = self.get_count_query().select(bypass_safe_limit=True)
        return list(rows[0].values())[0]

    def max(self, field):
        """
        Returns the maximum value of a field in the result set of the query
        by wrapping the query and performing a MAX aggregate of the specified field
        :param field: the field to pass to the MAX aggregate
        :type field: str

        :return: The maximum value of the specified field
        :rtype: int
        """
        q = Query(self.connection).from_table(self, fields=[
            MaxField(field)
        ])
        rows = q.select(bypass_safe_limit=True)
        return list(rows[0].values())[0]

    def min(self, field):
        """
        Returns the minimum value of a field in the result set of the query
        by wrapping the query and performing a MIN aggregate of the specified field
        :param field: the field to pass to the MIN aggregate
        :type field: str

        :return: The minimum value of the specified field
        :rtype: int
        """
        q = Query(self.connection).from_table(self, fields=[
            MinField(field)
        ])
        rows = q.select(bypass_safe_limit=True)
        return list(rows[0].values())[0]

    def sum(self, field):
        """
        Returns the sum of the field in the result set of the query
        by wrapping the query and performing a SUM aggregate of the specified field
        :param field: the field to pass to the SUM aggregate
        :type field: str

        :return: The sum of the specified field
        :rtype: int
        """
        q = Query(self.connection).from_table(self, fields=[
            SumField(field)
        ])
        rows = q.select(bypass_safe_limit=True)
        return list(rows[0].values())[0]

    def avg(self, field):
        """
        Returns the average of the field in the result set of the query
        by wrapping the query and performing an AVG aggregate of the specified field
        :param field: the field to pass to the AVG aggregate
        :type field: str

        :return: The average of the specified field
        :rtype: int
        """
        q = Query(self.connection).from_table(self, fields=[

            AvgField(field)