Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ django-pyodbc-azure
.. image:: http://img.shields.io/pypi/l/django-pyodbc-azure.svg?style=flat
:target: http://opensource.org/licenses/BSD-3-Clause

.. image:: https://ci.appveyor.com/api/projects/status/i9hfnl2gfeiq82qb?svg=true
:target: https://ci.appveyor.com/project/denisenkom/django-pyodbc-azure

.. image:: https://codecov.io/gh/denisenkom/django-pyodbc-azure/branch/master/graph/badge.svg
 :target: https://codecov.io/gh/denisenkom/django-pyodbc-azure

*django-pyodbc-azure* is a modern fork of
`django-pyodbc <https://code.google.com/archive/p/django-pyodbc/>`__, a
`Django <https://www.djangoproject.com/>`__ Microsoft SQL Server external
Expand Down
52 changes: 52 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
version: 1.0.{build}

os: Windows Server 2012 R2

environment:
HOST: localhost
SQLUSER: sa
SQLPASSWORD: Password12!
DATABASE: test
matrix:
- PYTHON: "C:\\Python36"
DJANGOVER: 1.11.3
SQLINSTANCE: SQL2016
- PYTHON: "C:\\Python36"
DJANGOVER: 1.10.7
SQLINSTANCE: SQL2016
- PYTHON: "C:\\Python36"
DJANGOVER: 1.9.13
SQLINSTANCE: SQL2016
#- PYTHON: "C:\\Python36"
# DJANGOVER: 1.11.3
# SQLINSTANCE: SQL2014
- PYTHON: "C:\\Python36"
DJANGOVER: 1.11.3
SQLINSTANCE: SQL2012SP1

install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- python --version
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
- pip install django==%DJANGOVER%
- pip install enum34
- pip install python-memcached <= 1.53
- pip install mock codecov
- pip install -e .

build_script:
- python setup.py sdist

before_test:
# setup SQL Server
- ps: |
$instanceName = $env:SQLINSTANCE
Start-Service "MSSQL`$$instanceName"
Start-Service "SQLBrowser"
- sqlcmd -S "(local)\%SQLINSTANCE%" -Q "Use [master]; CREATE DATABASE test;"
- sqlcmd -S "(local)\%SQLINSTANCE%" -h -1 -Q "set nocount on; Select @@version"


test_script:
- coverage run tests/runtests.py --noinput --settings=test_mssql --debug-sql
- codecov
Empty file added tests/aggregation/__init__.py
Empty file.
44 changes: 44 additions & 0 deletions tests/aggregation/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.db import models


class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
friends = models.ManyToManyField('self', blank=True)

def __str__(self):
return self.name


class Publisher(models.Model):
name = models.CharField(max_length=255)
num_awards = models.IntegerField()
duration = models.DurationField(blank=True, null=True)

def __str__(self):
return self.name


class Book(models.Model):
isbn = models.CharField(max_length=9)
name = models.CharField(max_length=255)
pages = models.IntegerField()
rating = models.FloatField()
price = models.DecimalField(decimal_places=2, max_digits=6)
authors = models.ManyToManyField(Author)
contact = models.ForeignKey(Author, models.CASCADE, related_name='book_contact_set')
publisher = models.ForeignKey(Publisher, models.CASCADE)
pubdate = models.DateField()

def __str__(self):
return self.name


class Store(models.Model):
name = models.CharField(max_length=255)
books = models.ManyToManyField(Book)
original_opening = models.DateTimeField()
friday_night_closing = models.TimeField()

def __str__(self):
return self.name
81 changes: 81 additions & 0 deletions tests/aggregation/test_filter_argument.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import datetime
from decimal import Decimal

from django.db.models import Case, Count, F, Q, Sum, When
from django.test import TestCase

from .models import Author, Book, Publisher


class FilteredAggregateTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.a1 = Author.objects.create(name='test', age=40)
cls.a2 = Author.objects.create(name='test2', age=60)
cls.a3 = Author.objects.create(name='test3', age=100)
cls.p1 = Publisher.objects.create(name='Apress', num_awards=3, duration=datetime.timedelta(days=1))
cls.b1 = Book.objects.create(
isbn='159059725', name='The Definitive Guide to Django: Web Development Done Right',
pages=447, rating=4.5, price=Decimal('30.00'), contact=cls.a1, publisher=cls.p1,
pubdate=datetime.date(2007, 12, 6),
)
cls.b2 = Book.objects.create(
isbn='067232959', name='Sams Teach Yourself Django in 24 Hours',
pages=528, rating=3.0, price=Decimal('23.09'), contact=cls.a2, publisher=cls.p1,
pubdate=datetime.date(2008, 3, 3),
)
cls.b3 = Book.objects.create(
isbn='159059996', name='Practical Django Projects',
pages=600, rating=4.5, price=Decimal('29.69'), contact=cls.a3, publisher=cls.p1,
pubdate=datetime.date(2008, 6, 23),
)
cls.a1.friends.add(cls.a2)
cls.a1.friends.add(cls.a3)
cls.b1.authors.add(cls.a1)
cls.b1.authors.add(cls.a3)
cls.b2.authors.add(cls.a2)
cls.b3.authors.add(cls.a3)

def test_filtered_aggregates(self):
agg = Sum('age', filter=Q(name__startswith='test'))
self.assertEqual(Author.objects.aggregate(age=agg)['age'], 200)

def test_double_filtered_aggregates(self):
agg = Sum('age', filter=Q(Q(name='test2') & ~Q(name='test')))
self.assertEqual(Author.objects.aggregate(age=agg)['age'], 60)

def test_excluded_aggregates(self):
agg = Sum('age', filter=~Q(name='test2'))
self.assertEqual(Author.objects.aggregate(age=agg)['age'], 140)

def test_related_aggregates_m2m(self):
agg = Sum('friends__age', filter=~Q(friends__name='test'))
self.assertEqual(Author.objects.filter(name='test').aggregate(age=agg)['age'], 160)

def test_related_aggregates_m2m_and_fk(self):
q = Q(friends__book__publisher__name='Apress') & ~Q(friends__name='test3')
agg = Sum('friends__book__pages', filter=q)
self.assertEqual(Author.objects.filter(name='test').aggregate(pages=agg)['pages'], 528)

def test_plain_annotate(self):
agg = Sum('book__pages', filter=Q(book__rating__gt=3))
qs = Author.objects.annotate(pages=agg).order_by('pk')
self.assertSequenceEqual([a.pages for a in qs], [447, None, 1047])

def test_filtered_aggregate_on_annotate(self):
pages_annotate = Sum('book__pages', filter=Q(book__rating__gt=3))
age_agg = Sum('age', filter=Q(total_pages__gte=400))
aggregated = Author.objects.annotate(total_pages=pages_annotate).aggregate(summed_age=age_agg)
self.assertEqual(aggregated, {'summed_age': 140})

def test_case_aggregate(self):
agg = Sum(
Case(When(friends__age=40, then=F('friends__age'))),
filter=Q(friends__name__startswith='test'),
)
self.assertEqual(Author.objects.aggregate(age=agg)['age'], 80)

def test_sum_star_exception(self):
msg = 'Star cannot be used with filter. Please specify a field.'
with self.assertRaisesMessage(ValueError, msg):
Count('*', filter=Q(age=40))
Loading