コード例 #1
0
 def test_build_filter(self):
     path = 'a__b__c'
     value = 1337
     query_path = QueryPath(path)
     condition = query_path.build_filter(value)
     assert isinstance(condition, Q)
     assert len(condition.children) == 1
     assert condition.children[0] == (path, value)
コード例 #2
0
class TestQueryablePropertyReference(object):
    @pytest.mark.parametrize('relation_path, expected_result', [
        (QueryPath(), QueryPath('dummy')),
        (QueryPath('application'), QueryPath('application__dummy')),
    ])
    def test_full_path(self, relation_path, expected_result):
        prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                      'dummy')
        ref = QueryablePropertyReference(prop, prop.model, relation_path)
        assert ref.full_path == expected_result

    def test_descriptor(self):
        prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                      'dummy')
        ref = QueryablePropertyReference(prop, prop.model, QueryPath())
        assert ref.descriptor == ApplicationWithClassBasedProperties.dummy

    @pytest.mark.parametrize('lookups, relation_path, expected_filter', [
        (QueryPath(), QueryPath(), 'version_count__exact'),
        (QueryPath('lt'), QueryPath(), 'version_count__lt'),
        (QueryPath('date__year'), QueryPath('application'),
         'application__version_count__date__year'),
    ])
    def test_get_filter(self, lookups, relation_path, expected_filter):
        prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                      'version_count')
        ref = QueryablePropertyReference(prop, prop.model, relation_path)
        q = ref.get_filter(lookups, 1337)
        assert isinstance(q, Q)
        assert q.children == [(expected_filter, 1337)]

    def test_get_filter_exception(self):
        prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                      'dummy')
        ref = QueryablePropertyReference(prop, prop.model, QueryPath())
        with pytest.raises(QueryablePropertyError):
            ref.get_filter(QueryPath(), None)

    def test_get_annotation(self):
        prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                      'version_count')
        ref = QueryablePropertyReference(prop, prop.model, QueryPath())
        assert isinstance(ref.get_annotation(), Count)

    def test_get_annotation_exception(self):
        prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                      'dummy')
        ref = QueryablePropertyReference(prop, prop.model, QueryPath())
        with pytest.raises(QueryablePropertyError):
            ref.get_annotation()
コード例 #3
0
 def test_base_condition(self, path):
     prop = RelatedExistenceCheckProperty(path)
     condition = prop._base_condition
     assert isinstance(condition, Q)
     assert len(condition.children) == 1
     assert condition.children[0] == (
         six.text_type(QueryPath(path) + 'isnull'), False)
コード例 #4
0
def refs():
    model = ApplicationWithClassBasedProperties
    return {
        prop_name:
        QueryablePropertyReference(get_queryable_property(model, prop_name),
                                   model, QueryPath())
        for prop_name in ('major_sum', 'version_count')
    }
コード例 #5
0
 def test_constructor(self, path, expected_result):
     query_path = QueryPath(path)
     assert query_path == expected_result
コード例 #6
0
class TestQueryPath(object):
    @pytest.mark.parametrize('path, expected_result', [
        ([], QueryPath()),
        (('a', 'b'), QueryPath(('a', 'b'))),
        ('a__b', QueryPath(('a', 'b'))),
    ])
    def test_constructor(self, path, expected_result):
        query_path = QueryPath(path)
        assert query_path == expected_result

    @pytest.mark.parametrize('query_path, addition, expected_result', [
        (QueryPath(), QueryPath(['a']), QueryPath(('a', ))),
        (QueryPath('a'), ('b', 'c'), QueryPath(('a', 'b', 'c'))),
        (QueryPath('a'), ['b'], QueryPath(('a', 'b'))),
        (QueryPath(('a', 'b')), 'c__d', QueryPath(('a', 'b', 'c', 'd'))),
    ])
    def test_add(self, query_path, addition, expected_result):
        result = query_path + addition
        assert isinstance(result, QueryPath)
        assert result == expected_result

    @pytest.mark.parametrize('query_path, item, expected_result', [
        (QueryPath('a__b'), 0, 'a'),
        (QueryPath('a__b'), slice(0, 1), QueryPath('a')),
        (QueryPath('a__b'), slice(5, 10), QueryPath()),
    ])
    def test_get_item(self, query_path, item, expected_result):
        result = query_path[item]
        assert isinstance(result, expected_result.__class__)
        assert result == expected_result

    def test_string_representation(self):
        query_path = QueryPath(('a', 'b', 'c'))
        assert six.text_type(query_path) == 'a__b__c'

    def test_representation(self):
        query_path = QueryPath(('a', 'b'))
        assert repr(query_path) == '<QueryPath: a__b>'

    def test_build_filter(self):
        path = 'a__b__c'
        value = 1337
        query_path = QueryPath(path)
        condition = query_path.build_filter(value)
        assert isinstance(condition, Q)
        assert len(condition.children) == 1
        assert condition.children[0] == (path, value)
コード例 #7
0
 def test_unsuccessful(self, model, query_path):
     assert resolve_queryable_property(model,
                                       query_path) == (None, QueryPath())
コード例 #8
0
class TestResolveQueryableProperty(object):
    @pytest.mark.parametrize(
        'model, query_path, expected_property, expected_lookups',
        [
            # No relation involved
            (VersionWithClassBasedProperties, QueryPath('version'),
             get_queryable_property(VersionWithClassBasedProperties,
                                    'version'), QueryPath()),
            (VersionWithDecoratorBasedProperties, QueryPath('version'),
             get_queryable_property(VersionWithDecoratorBasedProperties,
                                    'version'), QueryPath()),
            (VersionWithClassBasedProperties,
             QueryPath('version__lower__exact'),
             get_queryable_property(VersionWithClassBasedProperties,
                                    'version'), QueryPath('lower__exact')),
            (VersionWithDecoratorBasedProperties,
             QueryPath('version__lower__exact'),
             get_queryable_property(VersionWithDecoratorBasedProperties,
                                    'version'), QueryPath('lower__exact')),
            # FK forward relation
            (VersionWithClassBasedProperties,
             QueryPath('application__version_count'),
             get_queryable_property(ApplicationWithClassBasedProperties,
                                    'version_count'), QueryPath()),
            (VersionWithDecoratorBasedProperties,
             QueryPath('application__version_count'),
             get_queryable_property(ApplicationWithDecoratorBasedProperties,
                                    'version_count'), QueryPath()),
            (VersionWithClassBasedProperties,
             QueryPath('application__major_sum__gt'),
             get_queryable_property(ApplicationWithClassBasedProperties,
                                    'major_sum'), QueryPath('gt')),
            (VersionWithDecoratorBasedProperties,
             QueryPath('application__major_sum__gt'),
             get_queryable_property(ApplicationWithDecoratorBasedProperties,
                                    'major_sum'), QueryPath('gt')),
            # FK reverse relation
            (ApplicationWithClassBasedProperties,
             QueryPath('versions__major_minor'),
             get_queryable_property(VersionWithClassBasedProperties,
                                    'major_minor'), QueryPath()),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('versions__major_minor'),
             get_queryable_property(VersionWithDecoratorBasedProperties,
                                    'major_minor'), QueryPath()),
            (ApplicationWithClassBasedProperties,
             QueryPath('versions__version__lower__contains'),
             get_queryable_property(VersionWithClassBasedProperties,
                                    'version'), QueryPath('lower__contains')),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('versions__version__lower__contains'),
             get_queryable_property(VersionWithDecoratorBasedProperties,
                                    'version'), QueryPath('lower__contains')),
            # M2M forward relation
            (ApplicationWithClassBasedProperties,
             QueryPath('categories__circular'),
             get_queryable_property(CategoryWithClassBasedProperties,
                                    'circular'), QueryPath()),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('categories__circular'),
             get_queryable_property(CategoryWithDecoratorBasedProperties,
                                    'circular'), QueryPath()),
            (ApplicationWithClassBasedProperties,
             QueryPath('categories__circular__exact'),
             get_queryable_property(CategoryWithClassBasedProperties,
                                    'circular'), QueryPath('exact')),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('categories__circular__exact'),
             get_queryable_property(CategoryWithDecoratorBasedProperties,
                                    'circular'), QueryPath('exact')),
            # M2M reverse relation
            (CategoryWithClassBasedProperties,
             QueryPath('applications__major_sum'),
             get_queryable_property(ApplicationWithClassBasedProperties,
                                    'major_sum'), QueryPath()),
            (CategoryWithDecoratorBasedProperties,
             QueryPath('applications__major_sum'),
             get_queryable_property(ApplicationWithDecoratorBasedProperties,
                                    'major_sum'), QueryPath()),
            (CategoryWithClassBasedProperties,
             QueryPath('applications__version_count__lt'),
             get_queryable_property(ApplicationWithClassBasedProperties,
                                    'version_count'), QueryPath('lt')),
            (CategoryWithDecoratorBasedProperties,
             QueryPath('applications__version_count__lt'),
             get_queryable_property(ApplicationWithDecoratorBasedProperties,
                                    'version_count'), QueryPath('lt')),
            # Multiple relations
            (CategoryWithClassBasedProperties,
             QueryPath(
                 'applications__versions__application__categories__circular'),
             get_queryable_property(CategoryWithClassBasedProperties,
                                    'circular'), QueryPath()),
            (CategoryWithDecoratorBasedProperties,
             QueryPath(
                 'applications__versions__application__categories__circular'),
             get_queryable_property(CategoryWithDecoratorBasedProperties,
                                    'circular'), QueryPath()),
            (VersionWithClassBasedProperties,
             QueryPath('application__categories__circular__in'),
             get_queryable_property(CategoryWithClassBasedProperties,
                                    'circular'), QueryPath('in')),
            (VersionWithDecoratorBasedProperties,
             QueryPath('application__categories__circular__in'),
             get_queryable_property(CategoryWithDecoratorBasedProperties,
                                    'circular'), QueryPath('in')),
        ])
    def test_successful(self, model, query_path, expected_property,
                        expected_lookups):
        expected_ref = QueryablePropertyReference(
            expected_property, expected_property.model,
            query_path[:-len(expected_lookups) - 1])
        assert resolve_queryable_property(model,
                                          query_path) == (expected_ref,
                                                          expected_lookups)

    @pytest.mark.parametrize(
        'model, query_path',
        [
            # No relation involved
            (VersionWithClassBasedProperties, QueryPath('non_existent')),
            (VersionWithDecoratorBasedProperties, QueryPath('non_existent')),
            (VersionWithClassBasedProperties, QueryPath('major')),
            (VersionWithDecoratorBasedProperties, QueryPath('major')),
            # FK forward relation
            (VersionWithClassBasedProperties,
             QueryPath('application__non_existent__exact')),
            (VersionWithDecoratorBasedProperties,
             QueryPath('application__non_existent__exact')),
            (VersionWithClassBasedProperties, QueryPath('application__name')),
            (VersionWithDecoratorBasedProperties,
             QueryPath('application__name')),
            # FK reverse relation
            (ApplicationWithClassBasedProperties,
             QueryPath('versions__non_existent')),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('versions__non_existent')),
            (ApplicationWithClassBasedProperties,
             QueryPath('versions__minor__gt')),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('versions__minor__gt')),
            # M2M forward relation
            (ApplicationWithClassBasedProperties,
             QueryPath('categories__non_existent')),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('categories__non_existent')),
            (ApplicationWithClassBasedProperties,
             QueryPath('categories__name')),
            (ApplicationWithDecoratorBasedProperties,
             QueryPath('categories__name')),
            # M2M reverse relation
            (CategoryWithClassBasedProperties,
             QueryPath('applications__non_existent')),
            (CategoryWithDecoratorBasedProperties,
             QueryPath('applications__non_existent')),
            (CategoryWithClassBasedProperties,
             QueryPath('applications__name')),
            (CategoryWithDecoratorBasedProperties,
             QueryPath('applications__name')),
            # Non existent relation
            (VersionWithClassBasedProperties,
             QueryPath('non_existent_relation__non_existent__in')),
            (VersionWithDecoratorBasedProperties,
             QueryPath('non_existent_relation__non_existent__in')),
        ])
    def test_unsuccessful(self, model, query_path):
        assert resolve_queryable_property(model,
                                          query_path) == (None, QueryPath())
コード例 #9
0
 def test_get_annotation_exception(self):
     prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                   'dummy')
     ref = QueryablePropertyReference(prop, prop.model, QueryPath())
     with pytest.raises(QueryablePropertyError):
         ref.get_annotation()
コード例 #10
0
 def test_get_annotation(self):
     prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                   'version_count')
     ref = QueryablePropertyReference(prop, prop.model, QueryPath())
     assert isinstance(ref.get_annotation(), Count)
コード例 #11
0
 def test_descriptor(self):
     prop = get_queryable_property(ApplicationWithClassBasedProperties,
                                   'dummy')
     ref = QueryablePropertyReference(prop, prop.model, QueryPath())
     assert ref.descriptor == ApplicationWithClassBasedProperties.dummy
コード例 #12
0
class TestModelAttributeGetter(object):
    @pytest.mark.parametrize('path, expected_query_path', [
        ('attr', QueryPath('attr')),
        ('attr1.attr2', QueryPath('attr1__attr2')),
        ('attr1.attr2.attr3', QueryPath('attr1__attr2__attr3')),
        (['attr1', 'attr2'], QueryPath('attr1__attr2')),
        (('attr1', 'attr2', 'attr3'), QueryPath('attr1__attr2__attr3')),
    ])
    def test_initializer(self, path, expected_query_path):
        getter = ModelAttributeGetter(path)
        assert getter.query_path == expected_query_path

    @pytest.mark.django_db
    @pytest.mark.parametrize('attribute_name, expected_value', [
        ('major', 1),
        ('changes', None),
    ])
    def test_get_attribute(self, versions, attribute_name, expected_value):
        getter = ModelAttributeGetter(())
        assert getter._get_attribute(versions[0],
                                     attribute_name) == expected_value

    def test_get_attribute_catch_attribute_error_on_none(self):
        getter = ModelAttributeGetter(())
        assert getter._get_attribute(None, 'non_existent') is MISSING_OBJECT

    @pytest.mark.django_db
    def test_get_attribute_bubble_attribute_error(self, versions):
        getter = ModelAttributeGetter(())
        with pytest.raises(AttributeError):
            getter._get_attribute(versions[0], 'non_existent')

    @pytest.mark.django_db
    def test_get_attribute_catch_object_does_not_exist(self, applications,
                                                       versions):
        obj = versions[0].__class__.objects.get(
            pk=versions[0].pk)  # Refresh from DB
        applications[0].delete()
        getter = ModelAttributeGetter(())
        assert getter._get_attribute(obj, 'application') is MISSING_OBJECT

    @pytest.mark.django_db
    @pytest.mark.parametrize('path, expected_value', [
        ('major', 1),
        ('changes', None),
        ('application.name', 'My cool App'),
    ])
    def test_get_value(self, versions, path, expected_value):
        obj = versions[0]
        getter = ModelAttributeGetter(path)
        assert getter.get_value(obj) == expected_value

    @pytest.mark.django_db
    def test_get_value_missing_object(self, applications, versions):
        obj = versions[0].__class__.objects.get(
            pk=versions[0].pk)  # Refresh from DB
        applications[0].delete()
        getter = ModelAttributeGetter('application.name')
        assert getter.get_value(obj) is MISSING_OBJECT

    @pytest.mark.django_db
    @pytest.mark.usefixtures('versions')
    def test_get_values(self, categories):
        getter = ModelAttributeGetter('applications.versions.major')
        assert Counter(getter.get_values(categories[0])) == Counter({
            2: 2,
            1: 6
        })

    @pytest.mark.django_db
    @pytest.mark.usefixtures('versions')
    def test_get_values_missing_object(self, categories):
        getter = ModelAttributeGetter('applications.versions.major')
        VersionWithClassBasedProperties.objects.all().delete()
        assert getter.get_values(categories[0]) == []
        ApplicationWithClassBasedProperties.objects.all().delete()
        assert getter.get_values(categories[0]) == []

    @pytest.mark.parametrize('path, lookup, value, expected_query_name', [
        ('attr', 'exact', 1337, 'attr__exact'),
        ('attr1.attr2', 'in', ('test', 'value'), 'attr1__attr2__in'),
        ('attr1.attr2.attr3', 'gt', 1337, 'attr1__attr2__attr3__gt'),
    ])
    def test_build_filter(self, path, lookup, value, expected_query_name):
        getter = ModelAttributeGetter(path)
        condition = getter.build_filter(lookup, value)
        assert isinstance(condition, Q)
        assert len(condition.children) == 1
        assert condition.children[0] == (expected_query_name, value)
コード例 #13
0
 def test_representation(self):
     query_path = QueryPath(('a', 'b'))
     assert repr(query_path) == '<QueryPath: a__b>'
コード例 #14
0
 def test_string_representation(self):
     query_path = QueryPath(('a', 'b', 'c'))
     assert six.text_type(query_path) == 'a__b__c'
コード例 #15
0
class TestRelatedExistenceCheckProperty(object):
    @pytest.mark.parametrize('path, kwargs, expected_query_path', [
        ('my_field', {}, QueryPath('my_field__isnull')),
        ('my_relation__my_field', {
            'negated': True
        }, QueryPath('my_relation__my_field__isnull')),
        ('my_field', {
            'cached': True
        }, QueryPath('my_field__isnull')),
    ])
    def test_initializer(self, path, kwargs, expected_query_path):
        prop = RelatedExistenceCheckProperty(path, **kwargs)
        assert prop.query_path == expected_query_path
        assert prop.negated is kwargs.get('negated', False)
        assert prop.cached is kwargs.get('cached',
                                         RelatedExistenceCheckProperty.cached)

    @pytest.mark.parametrize('path', ['my_field', 'my_relation__my_field'])
    def test_base_condition(self, path):
        prop = RelatedExistenceCheckProperty(path)
        condition = prop._base_condition
        assert isinstance(condition, Q)
        assert len(condition.children) == 1
        assert condition.children[0] == (
            six.text_type(QueryPath(path) + 'isnull'), False)

    @pytest.mark.parametrize('negated', [False, True])
    def test_getter(self, monkeypatch, categories, applications, negated):
        monkeypatch.setattr(
            get_queryable_property(CategoryWithClassBasedProperties,
                                   'has_versions'), 'negated', negated)
        assert categories[0].has_versions is not negated
        assert categories[1].has_versions is not negated
        applications[1].versions.all().delete()
        assert categories[0].has_versions is not negated
        assert categories[1].has_versions is negated

    @pytest.mark.parametrize('negated', [False, True])
    def test_getter_based_on_non_relation_field(self, monkeypatch,
                                                applications, negated):
        monkeypatch.setattr(
            get_queryable_property(ApplicationWithClassBasedProperties,
                                   'has_version_with_changelog'), 'negated',
            negated)
        assert applications[0].has_version_with_changelog is not negated
        assert applications[1].has_version_with_changelog is not negated
        applications[0].versions.filter(major=2).delete()
        assert applications[0].has_version_with_changelog is negated
        assert applications[1].has_version_with_changelog is not negated

    @pytest.mark.parametrize('negated', [False, True])
    def test_filter(self, monkeypatch, categories, applications, negated):
        monkeypatch.setattr(
            get_queryable_property(CategoryWithClassBasedProperties,
                                   'has_versions'), 'negated', negated)
        queryset = CategoryWithClassBasedProperties.objects.all()
        assert set(queryset.filter(has_versions=not negated)) == set(
            categories[:2])
        assert not queryset.filter(has_versions=negated).exists()
        applications[1].versions.all().delete()
        assert queryset.get(has_versions=not negated) == categories[0]
        assert queryset.get(has_versions=negated) == categories[1]

    @pytest.mark.parametrize('negated', [False, True])
    def test_filter_based_on_non_relation_field(self, monkeypatch, categories,
                                                applications, negated):
        monkeypatch.setattr(
            get_queryable_property(ApplicationWithClassBasedProperties,
                                   'has_version_with_changelog'), 'negated',
            negated)
        app_queryset = ApplicationWithClassBasedProperties.objects.all()
        category_queryset = CategoryWithClassBasedProperties.objects.all()
        assert set(app_queryset.filter(
            has_version_with_changelog=not negated)) == set(applications[:2])
        assert not app_queryset.filter(
            has_version_with_changelog=negated).exists()
        assert (set(
            category_queryset.filter(
                applications__has_version_with_changelog=not negated)) == set(
                    categories[:2]))
        assert not category_queryset.filter(
            applications__has_version_with_changelog=negated).exists()
        applications[1].versions.filter(major=2).delete()
        assert app_queryset.get(
            has_version_with_changelog=not negated) == applications[0]
        assert app_queryset.get(
            has_version_with_changelog=negated) == applications[1]
        assert category_queryset.get(
            applications__has_version_with_changelog=not negated
        ) == categories[0]
        assert category_queryset.get(
            applications__has_version_with_changelog=negated) == categories[1]

    @pytest.mark.skipif(
        DJANGO_VERSION < (1, 8),
        reason="Expression-based annotations didn't exist before Django 1.8")
    def test_annotation(self, categories, applications):
        queryset = CategoryWithClassBasedProperties.objects.all()
        assert list(queryset.order_by('has_versions', 'pk')) == categories[:2]
        applications[1].versions.all().delete()
        assert list(queryset.order_by('has_versions',
                                      'pk')) == [categories[1], categories[0]]

    @pytest.mark.skipif(
        DJANGO_VERSION < (1, 8),
        reason="Expression-based annotations didn't exist before Django 1.8")
    def test_annotation_based_on_non_relation_field(self, categories,
                                                    applications):
        app_queryset = ApplicationWithClassBasedProperties.objects.all()
        category_queryset = CategoryWithClassBasedProperties.objects.all()
        assert list(app_queryset.order_by(
            '-has_version_with_changelog',
            '-pk')) == [applications[1], applications[0]]
        assert list(
            category_queryset.order_by(
                '-applications__has_version_with_changelog',
                '-pk')) == [categories[1], categories[0], categories[0]]
        applications[1].versions.filter(major=2).delete()
        assert list(app_queryset.order_by('-has_version_with_changelog',
                                          '-pk')) == applications[:2]
        assert list(
            category_queryset.order_by(
                '-applications__has_version_with_changelog',
                '-pk')) == [categories[0], categories[1], categories[0]]