diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e20c92f1d..444e269e1 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.3 +current_version = 1.0.4 commit = False tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? diff --git a/CHANGES.rst b/CHANGES.rst index 85c45bf36..eedf3b636 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,15 @@ -Version 1.0.3 (2107-05-16) +Version 1.0.4 (2017-05-19) -------------------------- -Improves compatibility with Django REST Framework schema generation. +Quick fix for verbose_field_name issue from 1.0.3 (#722) -See the `1.0.2 Milestone`__ for full details. + +Version 1.0.3 (2017-05-16) +-------------------------- + +Improves compatibility with Django REST Framework schema generation. + +See the `1.0.3 Milestone`__ for full details. __ https://github.com/carltongibson/django-filter/milestone/13?closed=1 diff --git a/README.rst b/README.rst index da3c4e4c2..f1be14ca2 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,8 @@ Django Filter ============= -Django-filter is a reusable Django application for allowing users to filter -querysets dynamically. +Django-filter is a reusable Django application allowing users to declaratively +add dynamic ``QuerySet`` filtering from URL parameters. Full documentation on `read the docs`_. @@ -68,22 +68,28 @@ And then in your view you could do: filter = ProductFilter(request.GET, queryset=Product.objects.all()) return render(request, 'my_app/template.html', {'filter': filter}) -Django-filters additionally supports specifying ``FilterSet`` fields using -a dictionary to specify filters with lookup types: + +Usage with Django REST Framework +-------------------------------- + +Django-filter provides a custom ``FilterSet`` and filter backend for use with +Django REST Framework. + +To use this adjust your import to use +``django_filters.rest_framework.FilterSet``. .. code-block:: python - import django_filters + from django_filters import rest_framework as filters - class ProductFilter(django_filters.FilterSet): + class ProductFilter(filters.FilterSet): class Meta: model = Product - fields = {'name': ['exact', 'icontains'], - 'price': ['exact', 'gte', 'lte'], - } + fields = ('category', 'in_stock') + + +For more details see the `DRF integration docs`_. -The filters will be available as ``'name'``, ``'name__icontains'``, -``'price'``, ``'price__gte'``, and ``'price__lte'`` in the above example. Support ------- @@ -93,3 +99,4 @@ If you have questions about usage or development you can join the .. _`read the docs`: https://django-filter.readthedocs.io/en/develop/ .. _`mailing list`: http://groups.google.com/group/django-filter +.. _`DRF integration docs`: https://django-filter.readthedocs.io/en/develop/guide/rest_framework.html diff --git a/django_filters/__init__.py b/django_filters/__init__.py index 2f73c4ffa..9d57b0fb2 100644 --- a/django_filters/__init__.py +++ b/django_filters/__init__.py @@ -11,7 +11,7 @@ except ImportError: rest_framework = None -__version__ = '1.0.3' +__version__ = '1.0.4' def parse_version(version): diff --git a/django_filters/utils.py b/django_filters/utils.py index eaec56578..4f6f62690 100644 --- a/django_filters/utils.py +++ b/django_filters/utils.py @@ -174,7 +174,10 @@ def verbose_field_name(model, field_name): names = [] for part in parts: if isinstance(part, ForeignObjectRel): - names.append(part.related_name.replace('_', ' ')) + if part.related_name: + names.append(part.related_name.replace('_', ' ')) + else: + return '[invalid name]' else: names.append(force_text(part.verbose_name)) diff --git a/docs/conf.py b/docs/conf.py index 8419a5481..790cf9043 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '1.0.3' +version = '1.0.4' # The full version, including alpha/beta/rc tags. -release = '1.0.3' +release = '1.0.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index ea35e5845..44c190cb3 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ readme = f.read() f.close() -version = '1.0.3' +version = '1.0.4' if sys.argv[-1] == 'publish': if os.system("pip freeze | grep wheel"): diff --git a/tests/test_utils.py b/tests/test_utils.py index 6bb69b8c4..a164b1f45 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -17,12 +17,16 @@ verbose_field_name, verbose_lookup_expr, label_for_filter, raw_validation, ) -from .models import User -from .models import Article -from .models import Book -from .models import HiredWorker -from .models import Business -from .models import NetworkSetting +from .models import ( + User, + Article, + Book, + HiredWorker, + Business, + NetworkSetting, + Company, + Account, +) class GetFieldPartsTests(TestCase): @@ -253,6 +257,26 @@ def test_lazy_text(self): verbose_name = verbose_field_name(User, 'username') self.assertEqual(verbose_name, 'username') + def test_forwards_fk(self): + verbose_name = verbose_field_name(Article, 'author') + self.assertEqual(verbose_name, 'author') + + def test_backwards_fk(self): + # https://github.com/carltongibson/django-filter/issues/716 + + # related_name is set + verbose_name = verbose_field_name(Company, 'locations') + self.assertEqual(verbose_name, 'locations') + + # related_name not set. Auto-generated relation is `article_set` + # _meta.get_field raises FieldDoesNotExist + verbose_name = verbose_field_name(User, 'article_set') + self.assertEqual(verbose_name, '[invalid name]') + + # WRONG NAME! Returns ManyToOneRel with related_name == None. + verbose_name = verbose_field_name(User, 'article') + self.assertEqual(verbose_name, '[invalid name]') + class VerboseLookupExprTests(TestCase):