Skip to content

Commit

Permalink
Merge pull request #577 from carltongibson/develop
Browse files Browse the repository at this point in the history
Version 1.0.2
  • Loading branch information
Carlton Gibson authored Mar 24, 2017
2 parents 0e65e35 + d2a5bce commit eb9b59d
Show file tree
Hide file tree
Showing 42 changed files with 710 additions and 189 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.0.1
current_version = 1.0.2
commit = False
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dist/
docs/_build
.python-version
.tox
.coverage
18 changes: 13 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
sudo: false

language: python
python: '3.5'
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"

cache: pip

install:
- pip install coverage tox
- pip install coverage detox tox-travis

script:
- coverage erase
- tox
- detox

after_success:
- coverage combine
- coverage report
- coverage combine --append
- coverage report -m
- pip install codecov
- codecov
5 changes: 4 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ recursive-include docs *
recursive-include requirements *
recursive-include tests *
recursive-include django_filters/locale *
prune docs/_build
recursive-include django_filters/templates *.html
prune docs/_build
global-exclude __pycache__
global-exclude *.py[co]
9 changes: 8 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ Full documentation on `read the docs`_.

.. image:: https://travis-ci.org/carltongibson/django-filter.svg?branch=master
:target: https://travis-ci.org/carltongibson/django-filter

.. image:: https://codecov.io/gh/carltongibson/django-filter/branch/develop/graph/badge.svg
:target: https://codecov.io/gh/carltongibson/django-filter

.. image:: https://badge.fury.io/py/django-filter.svg
:target: http://badge.fury.io/py/django-filter


Requirements
------------

* **Python**: 2.7, 3.3, 3.4, 3.5
* **Django**: 1.8, 1.9, 1.10
* **DRF**: 3.4, 3.5
* **DRF**: 3.5

Installation
------------
Expand Down
5 changes: 5 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
status:
project: false
patch: false
changes: false
2 changes: 1 addition & 1 deletion django_filters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
except ImportError:
rest_framework = None

__version__ = '1.0.1'
__version__ = '1.0.2'


def parse_version(version):
Expand Down
15 changes: 13 additions & 2 deletions django_filters/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@

import django
from django.conf import settings

from django.utils.timezone import make_aware as make_aware_orig

# django-crispy-forms is optional
try:
import crispy_forms
except ImportError:
crispy_forms = None

is_crispy = 'crispy_forms' in settings.INSTALLED_APPS and crispy_forms

def is_crispy():
return 'crispy_forms' in settings.INSTALLED_APPS and crispy_forms


# coreapi is optional (Note that uritemplate is a dependency of coreapi)
Expand Down Expand Up @@ -49,3 +51,12 @@ def format_value(widget, value):
if django.VERSION >= (1, 10):
return widget.format_value(value)
return widget._format_value(value)



def make_aware(value, timezone, is_dst):
"""is_dst was added for 1.9"""
if django.VERSION >= (1, 9):
return make_aware_orig(value, timezone, is_dst)
else:
return make_aware_orig(value, timezone)
44 changes: 23 additions & 21 deletions django_filters/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

from __future__ import absolute_import

from django.conf import settings as dj_settings
from django.core.signals import setting_changed
from django.utils.translation import ugettext_lazy as _
Expand Down Expand Up @@ -63,32 +65,30 @@

DEPRECATED_SETTINGS = [
'HELP_TEXT_FILTER',
'HELP_TEXT_EXCLUDE'
'HELP_TEXT_EXCLUDE',
]


def is_callable(value):
# check for callables, except types
return callable(value) and not isinstance(value, type)


class Settings(object):

def __init__(self):
for setting in DEFAULTS:
value = self.get_setting(setting)
setattr(self, setting, value)
def __getattr__(self, name):
if name not in DEFAULTS:
msg = "'%s' object has no attribute '%s'"
raise AttributeError(msg % (self.__class__.__name__, name))

def VERBOSE_LOOKUPS():
"""
VERBOSE_LOOKUPS accepts a dictionary of {terms: verbose expressions}
or a zero-argument callable that returns a dictionary.
"""
def fget(self):
if callable(self._VERBOSE_LOOKUPS):
self._VERBOSE_LOOKUPS = self._VERBOSE_LOOKUPS()
return self._VERBOSE_LOOKUPS
value = self.get_setting(name)

def fset(self, value):
self._VERBOSE_LOOKUPS = value
if is_callable(value):
value = value()

return locals()
VERBOSE_LOOKUPS = property(**VERBOSE_LOOKUPS())
# Cache the result
setattr(self, name, value)
return value

def get_setting(self, setting):
django_setting = 'FILTERS_%s' % setting
Expand All @@ -107,9 +107,11 @@ def change_setting(self, setting, value, enter, **kwargs):
if setting not in DEFAULTS:
return

# if exiting, refetch the value from settings.
value = value if enter else self.get_setting(setting)
setattr(self, setting, value)
# if exiting, delete value to repopulate
if enter:
setattr(self, setting, value)
else:
delattr(self, setting)


settings = Settings()
Expand Down
11 changes: 7 additions & 4 deletions django_filters/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
ALL_FIELDS = '__all__'


class STRICTNESS:
class IGNORE:
EMPTY_VALUES = ([], (), {}, '', None)


class STRICTNESS(object):
class IGNORE(object):
pass

class RETURN_NO_RESULTS:
class RETURN_NO_RESULTS(object):
pass

class RAISE_VALIDATION_ERROR:
class RAISE_VALIDATION_ERROR(object):
pass

# Values of False & True chosen for backward compatability reasons.
Expand Down
8 changes: 6 additions & 2 deletions django_filters/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ def compress(self, data_list):
start_date, stop_date = data_list
if start_date:
start_date = handle_timezone(
datetime.combine(start_date, time.min))
datetime.combine(start_date, time.min),
False
)
if stop_date:
stop_date = handle_timezone(
datetime.combine(stop_date, time.max))
datetime.combine(stop_date, time.max),
False
)
return slice(start_date, stop_date)
return None

Expand Down
4 changes: 1 addition & 3 deletions django_filters/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from django.utils.translation import ugettext_lazy as _

from .conf import settings
from .constants import EMPTY_VALUES
from .fields import (
Lookup, LookupTypeField, BaseCSVField, BaseRangeField, RangeField,
DateRangeField, DateTimeRangeField, TimeRangeField, IsoDateTimeField
Expand Down Expand Up @@ -56,9 +57,6 @@
LOOKUP_TYPES = sorted(QUERY_TERMS)


EMPTY_VALUES = ([], (), {}, '', None)


class Filter(object):
creation_counter = 0
field_class = forms.Field
Expand Down
49 changes: 27 additions & 22 deletions django_filters/filterset.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
from collections import OrderedDict

from django import forms
from django.forms.forms import NON_FIELD_ERRORS
from django.db import models
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields.related import ForeignObjectRel
from django.utils import six

from .conf import settings
from .compat import remote_field, remote_queryset
from .constants import ALL_FIELDS, STRICTNESS
from .constants import ALL_FIELDS, STRICTNESS, EMPTY_VALUES
from .filters import (Filter, CharFilter, BooleanFilter, BaseInFilter, BaseRangeFilter,
ChoiceFilter, DateFilter, DateTimeFilter, TimeFilter, ModelChoiceFilter,
ModelMultipleChoiceFilter, NumberFilter, UUIDFilter, DurationFilter)
Expand All @@ -36,28 +35,30 @@ def get_filter_name(field_name, lookup_expr):
return filter_name


def get_full_clean_override(together):
def full_clean(form):
def _together_valid(form, fieldset):
field_presence = [
form.cleaned_data.get(field) not in EMPTY_VALUES
for field in fieldset
]

def add_error(message):
try:
form.add_error(None, message)
except AttributeError:
form._errors[NON_FIELD_ERRORS] = message
if any(field_presence):
return all(field_presence)
return True

def all_valid(fieldset):
cleaned_data = form.cleaned_data
count = len([i for i in fieldset if cleaned_data.get(i)])
return 0 < count < len(fieldset)

def get_full_clean_override(together):
# coerce together to list of pairs
if isinstance(together[0], (six.string_types)):
together = [together]

def full_clean(form):
super(form.__class__, form).full_clean()
message = 'Following fields must be together: %s'
if isinstance(together[0], (list, tuple)):
for each in together:
if all_valid(each):
return add_error(message % ','.join(each))
elif all_valid(together):
return add_error(message % ','.join(together))

for each in together:
if not _together_valid(form, each):
return form.add_error(None, message % ','.join(each))

return full_clean


Expand Down Expand Up @@ -104,7 +105,11 @@ def get_declared_filters(cls, bases, attrs):
# merge declared filters from base classes
for base in reversed(bases):
if hasattr(base, 'declared_filters'):
filters = list(base.declared_filters.items()) + filters
filters = [
(name, f) for name, f
in base.declared_filters.items()
if name not in attrs
] + filters

return OrderedDict(filters)

Expand Down Expand Up @@ -421,8 +426,8 @@ class FilterSet(six.with_metaclass(FilterSetMetaclass, BaseFilterSet)):
pass


def filterset_factory(model):
meta = type(str('Meta'), (object,), {'model': model, 'fields': ALL_FIELDS})
def filterset_factory(model, fields=ALL_FIELDS):
meta = type(str('Meta'), (object,), {'model': model, 'fields': fields})
filterset = type(str('%sFilterSet' % model._meta.object_name),
(FilterSet,), {'Meta': meta})
return filterset
Binary file added django_filters/locale/es_ES/LC_MESSAGES/django.mo
Binary file not shown.
Loading

0 comments on commit eb9b59d

Please sign in to comment.