def test_many_to_many_fields_not_supported(self, migration_apps): """ Test that if an item in the yaml includes a many-to-many field, the function raises NotImplementedError as this is not supported yet. """ yaml_content = """ - model: datahub.core.test.support.book pk: 1 fields: name: name authors: - 1 - 2 published_on: '2010-01-01' """ mocked_read = mock.mock_open(read_data=yaml_content) PersonFactory.create_batch(2, pk=factory.Iterator([1, 2])) with pytest.raises(NotImplementedError) as excinfo: with mock.patch('datahub.core.migration_utils.open', mocked_read, create=True): load_yaml_data_in_migration(migration_apps, 'path-to-file.yaml') assert str(excinfo.value) == 'Many-to-many fields not supported'
def test_with_default_outer_field(self, expression): """ Test that a Person query set can annotated with the name of the most recently published book. This considers a single many-to-one relationship between Book and Person. """ person = PersonFactory() book_data = [ {'name': 'oldest', 'published_on': date(2010, 1, 1)}, {'name': 'in the middle', 'published_on': date(2013, 1, 1)}, {'name': 'newest', 'published_on': date(2015, 1, 1)}, ] shuffle(book_data) for item_data in book_data: BookFactory( proofreader=person, authors=[], **item_data, ) queryset = Person.objects.annotate( name_of_latest_book=get_top_related_expression_subquery( Book.proofreader.field, expression, ('-published_on',), ), ) assert queryset.first().name_of_latest_book == 'newest'
def test_ignores_blank_first_name(self): """Tests that a blank first_name is ignored.""" person = PersonFactory(first_name='') queryset = Person.objects.annotate( name=get_full_name_expression(), ) assert queryset.first().name == person.last_name
def test_aggregates_as_array(self, names, distinct): """ Test that the first names of all authors for each book can be aggregated into an array for various cases, and with distinct on and off. """ authors = PersonFactory.create_batch( len(names), first_name=factory.Iterator( sample(names, len(names)), ), ) BookFactory(authors=authors) queryset = Book.objects.annotate( author_names=get_array_agg_subquery( Book.authors.through, 'book', 'person__first_name', distinct=distinct, ), ) actual_author_names = queryset.first().author_names if distinct: assert Counter(actual_author_names) == Counter(set(names)) else: assert Counter(actual_author_names) == Counter(names)
def test_as_annotation(self): """Test that the function can be used as an annotation.""" person = PersonFactory() queryset = Person.objects.annotate(data=JSONBBuildObject( first_name='first_name', last_name='last_name'), ) assert queryset.first().data == { 'first_name': person.first_name, 'last_name': person.last_name, }
def test_get_empty_string_if_null_expression(value, expected): """Tests if None can be replaced with an empty string.""" PersonFactory() query = Person.objects.annotate( possibly_null_value=Value(value, output_field=CharField(null=True)), some_property=get_empty_string_if_null_expression('possibly_null_value'), ).values('some_property') person = query.first() assert person['some_property'] == expected
def test_full_name_annotation(self, include_country, country): """Tests that a Person query set can be annotated with full names.""" person = PersonFactory(country=country) bracketed_field_name = 'country' if include_country else None queryset = Person.objects.annotate(name=get_full_name_expression( bracketed_field_name=bracketed_field_name), ) expected_name = f'{person.first_name} {person.last_name}' if country and include_country: expected_name += f' ({person.country})' assert queryset.first().name == expected_name
def test_get_string_agg_subquery(num_authors): """ Test that get_string_agg_subquery() can be used to concatenate the first names of all authors for each book into one field. """ authors = PersonFactory.create_batch(num_authors) BookFactory(authors=authors) queryset = Book.objects.annotate(author_names=get_string_agg_subquery( Book, 'authors__first_name'), ) author_names_str = queryset.first().author_names actual_author_names = sorted( author_names_str.split(', ')) if author_names_str else [] expected_author_names = sorted(author.first_name for author in authors) assert actual_author_names == expected_author_names
def test_with_max_aggregate_expression(self, num_books): """ Test that Max() can be used to calculate the maximum published-on date for the books a person has proofread. """ proofreader = PersonFactory() books = BookFactory.create_batch(num_books, proofreader=proofreader) queryset = Person.objects.annotate( max_published=get_aggregate_subquery(Person, Max('proofread_books__published_on')), ).filter( pk=proofreader.pk, ) actual_max_published = queryset.first().max_published expected_max_published = max(book.published_on for book in books) if num_books else None assert actual_max_published == expected_max_published
def test_can_annotate_queryset(self, names, distinct, expected_result): """ Test that the first names of all authors for each book can be concatenated into one field as a query set annotation for various cases. """ authors = PersonFactory.create_batch( len(names), first_name=factory.Iterator( sample(names, len(names)), ), ) BookFactory(authors=authors) queryset = Book.objects.annotate( author_names=get_string_agg_subquery(Book, 'authors__first_name', distinct=distinct), ) actual_author_names = queryset.first().author_names assert actual_author_names == expected_result
def test_orders_results_when_ordering_specified(self, ordering, expected_names): """Test that the values are ordered corrected when an ordering is specified.""" names = ['Barbara', 'Claire', 'Samantha'] authors = PersonFactory.create_batch( len(names), first_name=factory.Iterator(sample(names, len(names)), ), ) BookFactory(authors=authors) queryset = Book.objects.annotate(author_names=get_array_agg_subquery( Book.authors.through, 'book', 'person__first_name', ordering=ordering, ), ) actual_author_names = queryset.first().author_names assert actual_author_names == expected_names
def test_aggregates_as_filtered_array(self, names, desired_names): """ Test that the desired first names of authors for each book can be aggregated into an array for various cases. """ authors = PersonFactory.create_batch( len(names), first_name=factory.Iterator(sample(names, len(names)), ), ) BookFactory(authors=authors) queryset = Book.objects.annotate(author_names=get_array_agg_subquery( Book.authors.through, 'book', 'person__first_name', filter=Q(person__first_name__in=desired_names), ), ) actual_author_names = queryset.first() assert set(actual_author_names.author_names) == set(desired_names)
def test_with_no_related_objects(self): """ Test that, if a Person query set is annotated with the name of the most recently published proofread books, and there are no such books, the annotation value is None. This considers a single many-to-one relationship between Book and Person. """ created_person = PersonFactory() queryset = Person.objects.annotate( name_of_latest_book=get_top_related_expression_subquery( Book.proofreader.field, 'name', ('-published_on', ), ), ) returned_person = queryset.first() assert returned_person == created_person assert returned_person.name_of_latest_book is None
def test_annotation( self, first_name, last_name, country, fields, bracketed_field, expected_value, ): """ Tests that a Person query set can be annotated using get_bracketed_concat_expression(). """ PersonFactory(first_name=first_name, last_name=last_name, country=country) queryset = Person.objects.annotate( name=get_bracketed_concat_expression( *fields, expression_to_bracket=bracketed_field, ), ) assert queryset.first().name == expected_value
def test_get_top_related_expression_subquery(expression): """ Test that get_top_related_expression_subquery() can be used to get the name of the most recently published book. """ person = PersonFactory() book_data = [ { 'name': 'oldest', 'published_on': date(2010, 1, 1) }, { 'name': 'in the middle', 'published_on': date(2013, 1, 1) }, { 'name': 'newest', 'published_on': date(2015, 1, 1) }, ] shuffle(book_data) for item_data in book_data: BookFactory( proofreader=person, authors=[], **item_data, ) queryset = Person.objects.annotate( name_of_latest_book=get_top_related_expression_subquery( Book.proofreader.field, expression, ('-published_on', ), ), ) assert queryset.first().name_of_latest_book == 'newest'
def test_full_name_annotation(self): """Tests that a Person query set can be annotated with full names.""" person = PersonFactory() queryset = Person.objects.annotate(name=get_full_name_expression(), ) expected_name = f'{person.first_name} {person.last_name}' assert queryset.first().name == expected_name
def test_only_excludes_referenced_objects(self): """Test that only referenced objects are excluded.""" unreferenced_person = PersonFactory() BookFactory() queryset = get_unreferenced_objects_query(Person) assert list(queryset) == [unreferenced_person]
def test_loading(self, migration_apps): """ Test that loading a yaml file updates the existing data. """ yaml_content = """ # person with pk=1, last_name should change - model: datahub.core.test.support.person pk: 1 fields: first_name: Existing last_name: Person with changed surname # person with pk=3, first_name should change, last_name shouldn't change - model: datahub.core.test.support.person pk: 3 fields: first_name: Another existing # person with pk=10, a new record should be created - model: datahub.core.test.support.person pk: 10 fields: first_name: New last_name: Person # book with pk=1, fk to person (proofreader) should change - model: datahub.core.test.support.book pk: 1 fields: name: Book name proofreader: 3 published_on: '2010-01-01' """ mocked_read = mock.mock_open(read_data=yaml_content) people = PersonFactory.create_batch( 3, pk=factory.Iterator([1, 2, 3]), first_name='Existing', last_name='Person', ) BookFactory( pk=1, name='Previous book name', proofreader=people[0], published_on=datetime.date(2010, 1, 1), authors=[], ) with mock.patch('datahub.core.migration_utils.open', mocked_read, create=True): load_yaml_data_in_migration(migration_apps, 'path-to-file.yaml') qs = Person.objects.order_by('id').values('id', 'first_name', 'last_name') assert list(qs) == [ {'id': 1, 'first_name': 'Existing', 'last_name': 'Person with changed surname'}, {'id': 2, 'first_name': 'Existing', 'last_name': 'Person'}, {'id': 3, 'first_name': 'Another existing', 'last_name': 'Person'}, {'id': 10, 'first_name': 'New', 'last_name': 'Person'}, ] qs = Book.objects.order_by('id').values('id', 'name', 'proofreader', 'published_on') assert list(qs) == [ { 'id': 1, 'name': 'Book name', 'proofreader': 3, 'published_on': datetime.date(2010, 1, 1), }, ]