def test_returns_object_when_one_match(self): """Test that the object is returned when there is exactly one match.""" expected_book = BookFactory(name='Summer') BookFactory.create_batch(2, name='Spring') book = get_queryset_object(Book.objects.all(), name='Summer') assert book == expected_book
def test_raises_exception_when_not_one_match(self, get_name, expected_exception): """Test that exceptions are raised when no matches or multiple matches are found.""" book_names = ['Spring', 'Spring', 'Summer'] BookFactory.create_batch( len(book_names), name=factory.Iterator(book_names), ) with pytest.raises(expected_exception): get_queryset_object(Book.objects.all(), name=get_name)
def test_get_choices_as_case_expression(genre): """ Test that get_choices_as_case_expression() generates display names for a field with choices the same way that model_obj.get_FIELD_display() does. """ book = BookFactory(genre=genre) queryset = Book.objects.annotate(genre_name=get_choices_as_case_expression( Book, 'genre'), ) annotated_book = queryset.first() assert annotated_book.genre_name == book.get_genre_display()
def test_relation_filter_mapping(self): """Test that relation_exclusion_filter_mapping excludes related objects as expected.""" book_1 = BookFactory(name='book 1') BookFactory(name='book 2') # Proofreaders for book 1 are not considered as referenced, and hence should appear in # the query results queryset = get_unreferenced_objects_query( Person, relation_exclusion_filter_mapping={ Person._meta.get_field('proofread_books'): Q(name=book_1.name), }, ) assert list(queryset) == [book_1.proofreader]
def test_with_custom_outer_field(self): """ Test that a PersonListItem query set can be annotated with the name of the most recently published book for the person in the list item. This involves two relationships: - a many-to-one relationship between Book and Person - a many-to-one relationship between PersonListItem and Person A custom value is used for the outer_field argument. """ person_list_item = PersonListItemFactory() 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_list_item.person, authors=[], **item_data) queryset = PersonListItem.objects.annotate( name_of_latest_book=get_top_related_expression_subquery( Book.proofreader.field, 'name', ('-published_on',), outer_field='person__pk', ), ) assert queryset.first().name_of_latest_book == 'newest'
def test_none_for_none_relation(self): """Tests that None is returned for an unset foreign key.""" BookFactory(proofreader=None) queryset = Book.objects.annotate( proofreader_name=get_full_name_expression('proofreader'), ) assert queryset.first().proofreader_name is None
def test_get_context_with_valid_value(self): """Tests get_context() when a valid value is supplied.""" book = BookFactory() widget = RawIdWidget(Book) change_route_name = admin_urlname(Book._meta, 'change') change_url = reverse(change_route_name, args=(book.pk, )) changelist_route_name = admin_urlname(Book._meta, 'changelist') changelist_url = reverse(changelist_route_name) assert widget.get_context('test-widget', str(book.pk), {}) == { 'link_label': str(book), 'link_title': 'Look up book', 'link_url': change_url, 'related_url': f'{changelist_url}?_to_field=id', 'widget': { 'attrs': { 'class': 'vUUIDField', }, 'is_hidden': False, 'name': 'test-widget', 'required': False, 'template_name': 'admin/widgets/foreign_key_raw_id.html', 'type': 'text', 'value': str(book.pk), }, }
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_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_get_front_end_url_expression(monkeypatch): """Test that get_front_end_url_expression() generates URLs correctly.""" monkeypatch.setitem(settings.DATAHUB_FRONTEND_URL_PREFIXES, 'book', 'http://test') book = BookFactory() queryset = Book.objects.annotate( url=get_front_end_url_expression('book', 'pk'), ) assert queryset.first().url == f'http://test/{book.pk}'
def test_admin_change_link_target_blank(self): """ Test that admin_change_url() returns an anchor tag with target="_blank" to the change page for an object. """ book = BookFactory.build(pk=uuid4()) assert admin_change_link(book, True) == ( f'<a href="/admin/support/book/{book.pk}/change/" target="_blank">{book}</a>' )
def test_full_name_related_annotation(self): """ Tests that a Book query set can be annotated with the full name of the proofreader of each book. """ book = BookFactory() proofreader = book.proofreader queryset = Book.objects.annotate( proofreader_name=get_full_name_expression('proofreader'), ) expected_name = f'{proofreader.first_name} {proofreader.last_name}' assert queryset.first().proofreader_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_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_full_name_related_annotation(self, include_country, country): """ Tests that a Book query set can be annotated with the full name of the proofreader of each book. """ book = BookFactory(proofreader__country=country) proofreader = book.proofreader bracketed_field_name = 'country' if include_country else None queryset = Book.objects.annotate( proofreader_name=get_full_name_expression( person_field_name='proofreader', bracketed_field_name=bracketed_field_name, ), ) expected_name = f'{proofreader.first_name} {proofreader.last_name}' if country and include_country: expected_name += f' ({proofreader.country})' assert queryset.first().proofreader_name == expected_name
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_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_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_generates_urls_for_saved_objects(self): """Test that a valid change URL is generated.""" book = BookFactory() assert get_change_url(book) == ( f'/admin/support/book/{book.pk}/change/')
def test_admin_change_url(): """Test that admin_change_url() returns the URL to the change page for an object.""" book = BookFactory.build(pk=uuid4()) assert admin_change_url(book) == f'/admin/support/book/{book.pk}/change/'
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), }, ]
def test_generates_links_for_saved_objects(self): """Test that a valid change link is generated.""" book = BookFactory() assert get_change_link(book) == ( f'<a href="/admin/support/book/{book.pk}/change/">{book.name}</a>')
def test_returns_empty_string_if_no_pk(self): """Test that if the object has no pk, an empty link is returned.""" book = BookFactory.build() assert get_change_link(book) == ''
def test_object_name_returned_for_existing_object(self): """Test the object name is returned for an existing object.""" book = BookFactory() assert _get_object_name_for_pk(Book, book.pk) == str(book)