diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..ab55a88 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 120 +exclude = .git, .venv, __pycache__, migrations, tests +ignore = E203, E501, W605, W504, E265, W503 diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml new file mode 100644 index 0000000..ad71443 --- /dev/null +++ b/.github/workflows/pipeline.yml @@ -0,0 +1,30 @@ +name: Python package + +on: + push: + branches: '**' #[ $default-branch ] + pull_request: + branches: '**' + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false +# matrix: +# python-version: ["3.3.7", "3.4.10", "3.5.10"] + + steps: + - uses: actions/checkout@v4 +# - name: Set up Python ${{ matrix.python-version }} +# uses: actions/setup-python@v3 +# with: +# python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 tox + - name: Run tests and flake8 with tox + run: | + tox diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5ad3879 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: debug-statements + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: 24.8.0 + hooks: + - id: black + - repo: https://github.com/PyCQA/flake8 + rev: 7.1.1 + hooks: + - id: flake8 diff --git a/CHANGES.rst b/CHANGES.rst index 51c4d3e..df62551 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,8 +6,8 @@ Changelog * Fix formset rendering in Django 1.9. `#17`_ * Add support for Django 1.9's ``get_bound_field``. `#18`_ -.. _#17: https://github.com/gregmuellegger/django-superform/pull/17 -.. _#18: https://github.com/gregmuellegger/django-superform/pull/18 +.. _#17: https://github.com/jazzband/django-superform/pull/17 +.. _#18: https://github.com/jazzband/django-superform/pull/18 0.3.1 ----- diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..85843e8 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,5 @@ +.. image:: https://jazzband.co/static/img/jazzband.svg + :target: https://jazzband.co/ + :alt: Jazzband + +This is a `Jazzband `_ project. By contributing, you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. diff --git a/README.rst b/README.rst index 0484db1..132cea1 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ django-superform **Less sucking formsets.** -|build| |package| |gitter| +|build| |package| |jazzband| Documentation_ | Changelog_ | Requirements_ | Installation_ @@ -126,7 +126,7 @@ Development - Clone django-superform:: - git clone git@github.com:gregmuellegger/django-superform.git + git clone git@github.com:jazzband/django-superform.git - ``cd`` into the repository:: @@ -157,13 +157,10 @@ Full documentation is available on Read the Docs: https://django-superform.readt .. |build| image:: https://travis-ci.org/gregmuellegger/django-superform.svg?branch=master :alt: Build Status - :scale: 100% :target: https://travis-ci.org/gregmuellegger/django-superform .. |package| image:: https://badge.fury.io/py/django-superform.svg :alt: Package Version - :scale: 100% :target: http://badge.fury.io/py/django-superform -.. |gitter| image:: https://badges.gitter.im/JoinChat.svg - :alt: Gitter Chat, discuss django-superform with others - :scale: 100% - :target: https://gitter.im/gregmuellegger/django-superform +.. |jazzband| image:: https://jazzband.co/static/img/badge.svg + :target: https://jazzband.co + :alt: Jazzband diff --git a/django_superform/__init__.py b/django_superform/__init__.py index 17a8f76..506065c 100644 --- a/django_superform/__init__.py +++ b/django_superform/__init__.py @@ -12,16 +12,29 @@ See http://django-superform.readthedocs.org/en/latest/ for complete docs. """ from .fields import ( - FormField, ModelFormField, ForeignKeyFormField, FormSetField, - ModelFormSetField, InlineFormSetField) + FormField, + ModelFormField, + ForeignKeyFormField, + FormSetField, + ModelFormSetField, + InlineFormSetField, +) from .forms import SuperForm, SuperModelForm from .widgets import FormWidget, FormSetWidget -__version__ = '0.4.0.dev1' +__version__ = "0.4.0.dev1" __all__ = ( - 'FormField', 'ModelFormField', 'ForeignKeyFormField', 'FormSetField', - 'ModelFormSetField', 'InlineFormSetField', 'SuperForm', 'SuperModelForm', - 'FormWidget', 'FormSetWidget') + "FormField", + "ModelFormField", + "ForeignKeyFormField", + "FormSetField", + "ModelFormSetField", + "InlineFormSetField", + "SuperForm", + "SuperModelForm", + "FormWidget", + "FormSetWidget", +) diff --git a/django_superform/fields.py b/django_superform/fields.py index 8f5297e..fa4a291 100644 --- a/django_superform/fields.py +++ b/django_superform/fields.py @@ -17,8 +17,15 @@ class BaseCompositeField(object): # order. creation_counter = 0 - def __init__(self, required=True, widget=None, label=None, help_text='', - localize=False, disabled=False): + def __init__( + self, + required=True, + widget=None, + label=None, + help_text="", + localize=False, + disabled=False, + ): self.required = required self.label = label self.help_text = help_text @@ -52,7 +59,7 @@ class CompositeField(BaseCompositeField): This field cannot be used directly, use a subclass of it. """ - prefix_name = 'composite' + prefix_name = "composite" def __init__(self, *args, **kwargs): super(CompositeField, self).__init__(*args, **kwargs) @@ -68,10 +75,11 @@ def get_prefix(self, form, name): """ Return the prefix that is used for the formset. """ - return '{form_prefix}{prefix_name}-{field_name}'.format( - form_prefix=form.prefix + '-' if form.prefix else '', + return "{form_prefix}{prefix_name}-{field_name}".format( + form_prefix=form.prefix + "-" if form.prefix else "", prefix_name=self.prefix_name, - field_name=name) + field_name=name, + ) def get_initial(self, form, name): """ @@ -80,7 +88,7 @@ def get_initial(self, form, name): given. """ - if hasattr(form, 'initial'): + if hasattr(form, "initial"): return form.initial.get(name, None) return None @@ -89,8 +97,8 @@ def get_kwargs(self, form, name): Return the keyword arguments that are used to instantiate the formset. """ kwargs = { - 'prefix': self.get_prefix(form, name), - 'initial': self.get_initial(form, name), + "prefix": self.get_prefix(form, name), + "initial": self.get_initial(form, name), } kwargs.update(self.default_kwargs) return kwargs @@ -148,7 +156,7 @@ class RegistrationForm(SuperForm): The first method (using ``kwargs``) will take precedence. """ - prefix_name = 'form' + prefix_name = "form" widget = FormWidget def __init__(self, form_class, kwargs=None, **field_kwargs): @@ -176,7 +184,8 @@ def get_form(self, form, name): composite_form = form_class( data=form.data if form.is_bound else None, files=form.files if form.is_bound else None, - **kwargs) + **kwargs + ) return composite_form @@ -246,8 +255,8 @@ def get_kwargs(self, form, name): """ kwargs = super(ModelFormField, self).get_kwargs(form, name) instance = self.get_instance(form, name) - kwargs.setdefault('instance', instance) - kwargs.setdefault('empty_permitted', not self.required) + kwargs.setdefault("instance", instance) + kwargs.setdefault("empty_permitted", not self.required) return kwargs def shall_save(self, form, name, composite_form): @@ -279,20 +288,20 @@ def save(self, form, name, composite_form, commit): class ForeignKeyFormField(ModelFormField): - def __init__(self, form_class, kwargs=None, field_name=None, blank=None, - **field_kwargs): - super(ForeignKeyFormField, self).__init__(form_class, kwargs, - **field_kwargs) + def __init__( + self, form_class, kwargs=None, field_name=None, blank=None, **field_kwargs + ): + super(ForeignKeyFormField, self).__init__(form_class, kwargs, **field_kwargs) self.field_name = field_name self.blank = blank def get_kwargs(self, form, name): kwargs = super(ForeignKeyFormField, self).get_kwargs(form, name) - if 'instance' not in kwargs: - kwargs.setdefault('instance', self.get_instance(form, name)) - if 'empty_permitted' not in kwargs: + if "instance" not in kwargs: + kwargs.setdefault("instance", self.get_instance(form, name)) + if "empty_permitted" not in kwargs: if self.allow_blank(form, name): - kwargs['empty_permitted'] = True + kwargs["empty_permitted"] = True return kwargs def get_field_name(self, form, name): @@ -323,16 +332,17 @@ def save(self, form, name, composite_form, commit): if composite_form.empty_permitted and not composite_form.has_changed(): saved_obj = composite_form.instance else: - saved_obj = super(ForeignKeyFormField, self).save(form, name, - composite_form, - commit) + saved_obj = super(ForeignKeyFormField, self).save( + form, name, composite_form, commit + ) setattr(form.instance, self.get_field_name(form, name), saved_obj) if commit: form.instance.save() else: raise NotImplementedError( - 'ForeignKeyFormField cannot yet be used with non-commiting ' - 'form saves.') + "ForeignKeyFormField cannot yet be used with non-commiting " + "form saves." + ) return saved_obj @@ -345,7 +355,7 @@ class FormSetField(CompositeField): are used when the ``formset_class`` is instantiated. """ - prefix_name = 'formset' + prefix_name = "formset" widget = FormSetWidget def __init__(self, formset_class, kwargs=None, **field_kwargs): @@ -373,7 +383,8 @@ def get_formset(self, form, name): formset = formset_class( form.data if form.is_bound else None, form.files if form.is_bound else None, - **kwargs) + **kwargs + ) return formset @@ -434,8 +445,14 @@ class Meta: extra=1) """ - def __init__(self, parent_model=None, model=None, formset_class=None, - kwargs=None, **factory_kwargs): + def __init__( + self, + parent_model=None, + model=None, + formset_class=None, + kwargs=None, + **factory_kwargs + ): """ You need to either provide the ``formset_class`` or the ``model`` argument. @@ -448,25 +465,27 @@ def __init__(self, parent_model=None, model=None, formset_class=None, # Make sure that all standard arguments will get passed through to the # parent's __init__ method. field_kwargs = {} - for arg in ['required', 'widget', 'label', 'help_text', 'localize']: + for arg in ["required", "widget", "label", "help_text", "localize"]: if arg in factory_kwargs: field_kwargs[arg] = factory_kwargs.pop(arg) self.parent_model = parent_model self.model = model self.formset_factory_kwargs = factory_kwargs - super(InlineFormSetField, self).__init__(formset_class, kwargs=kwargs, - **field_kwargs) + super(InlineFormSetField, self).__init__( + formset_class, kwargs=kwargs, **field_kwargs + ) if ( - self.formset_class is None and - 'form' not in self.formset_factory_kwargs and - 'fields' not in self.formset_factory_kwargs and - 'exclude' not in self.formset_factory_kwargs): + self.formset_class is None + and "form" not in self.formset_factory_kwargs + and "fields" not in self.formset_factory_kwargs + and "exclude" not in self.formset_factory_kwargs + ): raise ValueError( - 'You need to either specify the `formset_class` argument or ' - 'one of `form`/`fields`/`exclude` arguments ' - 'when creating a {0}.' - .format(self.__class__.__name__)) + "You need to either specify the `formset_class` argument or " + "one of `form`/`fields`/`exclude` arguments " + "when creating a {0}.".format(self.__class__.__name__) + ) def get_model(self, form, name): return self.model @@ -487,10 +506,11 @@ def get_formset_class(self, form, name): formset_class = inlineformset_factory( self.get_parent_model(form, name), self.get_model(form, name), - **self.formset_factory_kwargs) + **self.formset_factory_kwargs + ) return formset_class def get_kwargs(self, form, name): kwargs = super(InlineFormSetField, self).get_kwargs(form, name) - kwargs.setdefault('instance', form.instance) + kwargs.setdefault("instance", form.instance) return kwargs diff --git a/django_superform/forms.py b/django_superform/forms.py index 13fe99e..c573062 100644 --- a/django_superform/forms.py +++ b/django_superform/forms.py @@ -105,16 +105,17 @@ def __new__(mcs, name, bases, attrs): current_fields.append((key, value)) attrs.pop(key) current_fields.sort(key=lambda x: x[1].creation_counter) - attrs['declared_composite_fields'] = OrderedDict(current_fields) + attrs["declared_composite_fields"] = OrderedDict(current_fields) new_class = super(DeclerativeCompositeFieldsMetaclass, mcs).__new__( - mcs, name, bases, attrs) + mcs, name, bases, attrs + ) # Walk through the MRO. declared_fields = OrderedDict() for base in reversed(new_class.__mro__): # Collect fields from base class. - if hasattr(base, 'declared_composite_fields'): + if hasattr(base, "declared_composite_fields"): declared_fields.update(base.declared_composite_fields) # Field shadowing. @@ -129,16 +130,14 @@ def __new__(mcs, name, bases, attrs): class SuperFormMetaclass( - DeclerativeCompositeFieldsMetaclass, - DeclarativeFieldsMetaclass): + DeclerativeCompositeFieldsMetaclass, DeclarativeFieldsMetaclass +): """ Metaclass for :class:`~django_superform.forms.SuperForm`. """ -class SuperModelFormMetaclass( - DeclerativeCompositeFieldsMetaclass, - ModelFormMetaclass): +class SuperModelFormMetaclass(DeclerativeCompositeFieldsMetaclass, ModelFormMetaclass): """ Metaclass for :class:`~django_superform.forms.SuperModelForm`. """ @@ -199,16 +198,16 @@ def get_composite_field_value(self, name): Return the form/formset instance for the given field name. """ field = self.composite_fields[name] - if hasattr(field, 'get_form'): + if hasattr(field, "get_form"): return self.forms[name] - if hasattr(field, 'get_formset'): + if hasattr(field, "get_formset"): return self.formsets[name] def _init_composite_field(self, name, field): - if hasattr(field, 'get_form'): + if hasattr(field, "get_form"): form = field.get_form(self, name) self.forms[name] = form - if hasattr(field, 'get_formset'): + if hasattr(field, "get_formset"): formset = field.get_formset(self, name) self.formsets[name] = formset @@ -309,7 +308,7 @@ def save(self, commit=True): def _extend_save_m2m(self, name, composites): additional_save_m2m = [] for composite in composites: - if hasattr(composite, 'save_m2m'): + if hasattr(composite, "save_m2m"): additional_save_m2m.append(composite.save_m2m) if not additional_save_m2m: @@ -321,9 +320,10 @@ def additional_saves(): # The save() method was called before save_forms()/save_formsets(), so # we will already have save_m2m() available. - if hasattr(self, 'save_m2m'): + if hasattr(self, "save_m2m"): _original_save_m2m = self.save_m2m else: + def _original_save_m2m(): return None @@ -350,11 +350,11 @@ def save_forms(self, commit=True): saved_composites = [] for name, composite in self.forms.items(): field = self.composite_fields[name] - if hasattr(field, 'save'): + if hasattr(field, "save"): field.save(self, name, composite, commit=commit) saved_composites.append(composite) - self._extend_save_m2m('save_forms_m2m', saved_composites) + self._extend_save_m2m("save_forms_m2m", saved_composites) def save_formsets(self, commit=True): """ @@ -365,15 +365,16 @@ def save_formsets(self, commit=True): saved_composites = [] for name, composite in self.formsets.items(): field = self.composite_fields[name] - if hasattr(field, 'save'): + if hasattr(field, "save"): field.save(self, name, composite, commit=commit) saved_composites.append(composite) - self._extend_save_m2m('save_formsets_m2m', saved_composites) + self._extend_save_m2m("save_formsets_m2m", saved_composites) -class SuperModelForm(six.with_metaclass(SuperModelFormMetaclass, - SuperModelFormMixin, forms.ModelForm)): +class SuperModelForm( + six.with_metaclass(SuperModelFormMetaclass, SuperModelFormMixin, forms.ModelForm) +): """ The ``SuperModelForm`` works like a Django ``ModelForm`` but has the capabilities of nesting like :class:`~django_superform.forms.SuperForm`. @@ -382,8 +383,7 @@ class SuperModelForm(six.with_metaclass(SuperModelFormMetaclass, """ -class SuperForm(six.with_metaclass(SuperFormMetaclass, - SuperFormMixin, forms.Form)): +class SuperForm(six.with_metaclass(SuperFormMetaclass, SuperFormMixin, forms.Form)): """ The base class for all super forms. The goal of a superform is to behave just like a normal django form but is able to take composite fields, like diff --git a/django_superform/widgets.py b/django_superform/widgets.py index f5173ce..692fb57 100644 --- a/django_superform/widgets.py +++ b/django_superform/widgets.py @@ -14,7 +14,7 @@ class TemplateWidget(forms.Widget): value_context_name = None def __init__(self, *args, **kwargs): - template_name = kwargs.pop('template_name', None) + template_name = kwargs.pop("template_name", None) if template_name is not None: self.template_name = template_name super(TemplateWidget, self).__init__(*args, **kwargs) @@ -25,39 +25,38 @@ def get_context_data(self): def get_context(self, name, value, attrs=None): context = { - 'name': name, - 'hidden': self.is_hidden, - 'required': self.is_required, + "name": name, + "hidden": self.is_hidden, + "required": self.is_required, # In our case ``value`` is the form or formset instance. - 'value': value, + "value": value, } if self.value_context_name: context[self.value_context_name] = value if self.is_hidden: - context['hidden'] = True + context["hidden"] = True context.update(self.get_context_data()) - context['attrs'] = self.build_attrs(attrs) + context["attrs"] = self.build_attrs(attrs) return context def render(self, name, value, attrs=None, **kwargs): - template_name = kwargs.pop('template_name', None) + template_name = kwargs.pop("template_name", None) if template_name is None: template_name = self.template_name context = self.get_context(name, value, attrs=attrs or {}, **kwargs) return loader.render_to_string( - template_name, - dictionary=context, - context_instance=self.context_instance) + template_name, dictionary=context, context_instance=self.context_instance + ) class FormWidget(TemplateWidget): - template_name = 'superform/formfield.html' - value_context_name = 'form' + template_name = "superform/formfield.html" + value_context_name = "form" class FormSetWidget(TemplateWidget): - template_name = 'superform/formsetfield.html' - value_context_name = 'formset' + template_name = "superform/formsetfield.html" + value_context_name = "formset" diff --git a/docs/conf.py b/docs/conf.py index abba278..e122762 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,220 +29,227 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'django-superform' -copyright = str(date.today().year) + u', Gregor Müllegger' +project = "django-superform" +copyright = str(date.today().year) + ", Gregor Müllegger" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. + def read(*parts): - return codecs.open(path.join(path.dirname(__file__), *parts), - encoding='utf-8').read() + return codecs.open( + path.join(path.dirname(__file__), *parts), encoding="utf-8" + ).read() + def find_version(*file_paths): version_file = read(*file_paths) - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", - version_file, re.M) + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") + version_tuple = find_version( - path.join(project_base_path, 'django_superform', '__init__.py')).split('.') + path.join(project_base_path, "django_superform", "__init__.py") +).split(".") # # The short X.Y version. -version = '.'.join(version_tuple[:2]) +version = ".".join(version_tuple[:2]) # The full version, including alpha/beta/rc tags. -release = '.'.join(version_tuple) +release = ".".join(version_tuple) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -#html_theme = 'alabaster' +# html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'django-superformdoc' +htmlhelp_basename = "django-superformdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'django-superform.tex', u'django-superform Documentation', - u'Gregor Müllegger', 'manual'), + ( + "index", + "django-superform.tex", + "django-superform Documentation", + "Gregor Müllegger", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -250,12 +257,17 @@ def find_version(*file_paths): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'django-superform', u'django-superform Documentation', - [u'Gregor Müllegger'], 1) + ( + "index", + "django-superform", + "django-superform Documentation", + ["Gregor Müllegger"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -264,19 +276,25 @@ def find_version(*file_paths): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'django-superform', u'django-superform Documentation', - u'Gregor Müllegger', 'django-superform', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "django-superform", + "django-superform Documentation", + "Gregor Müllegger", + "django-superform", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/requirements.txt b/requirements.txt index 4817391..1641ec9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django>=1.8,<1.9 Sphinx==1.3.1 +pre-commit -r tests/requirements.txt diff --git a/setup.py b/setup.py index 4e683b0..3cd1ea2 100644 --- a/setup.py +++ b/setup.py @@ -7,52 +7,50 @@ def read(*parts): - return codecs.open(path.join(path.dirname(__file__), *parts), - encoding='utf-8').read() + return codecs.open( + path.join(path.dirname(__file__), *parts), encoding="utf-8" + ).read() def find_version(*file_paths): version_file = read(*file_paths) - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", - version_file, re.M) + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") setup( - name='django-superform', - version=find_version('django_superform', '__init__.py'), - author=u'Gregor Müllegger', - author_email='gregor@muellegger.de', + name="django-superform", + version=find_version("django_superform", "__init__.py"), + author="Gregor Müllegger", + author_email="gregor@muellegger.de", packages=find_packages(), include_package_data=True, - url='https://github.com/gregmuellegger/django-superform', - license='BSD licence, see LICENSE file', - description='So much easier handling of formsets.', - long_description=u'\n\n'.join(( - read('README.rst'), - read('CHANGES.rst'))), + url="https://github.com/gregmuellegger/django-superform", + license="BSD licence, see LICENSE file", + description="So much easier handling of formsets.", + long_description="\n\n".join((read("README.rst"), read("CHANGES.rst"))), classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Web Environment', - 'Framework :: Django', - 'Framework :: Django :: 1.4', - 'Framework :: Django :: 1.6', - 'Framework :: Django :: 1.7', - 'Framework :: Django :: 1.8', - 'Framework :: Django :: 1.9', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', + "Development Status :: 4 - Beta", + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Django :: 1.4", + "Framework :: Django :: 1.6", + "Framework :: Django :: 1.7", + "Framework :: Django :: 1.8", + "Framework :: Django :: 1.9", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", ], zip_safe=False, ) diff --git a/tests/models.py b/tests/models.py index 45f61db..e53560b 100644 --- a/tests/models.py +++ b/tests/models.py @@ -15,11 +15,11 @@ class Post(models.Model): """ title = models.CharField(max_length=50) - series = models.ForeignKey('Series', null=True, blank=True) + series = models.ForeignKey("Series", null=True, blank=True) class Image(models.Model): - post = models.ForeignKey('Post', related_name='images') + post = models.ForeignKey("Post", related_name="images") name = models.CharField(max_length=50) position = models.PositiveIntegerField(default=0) @@ -28,4 +28,4 @@ class Image(models.Model): image_url = models.URLField() class Meta: - ordering = ('position',) + ordering = ("position",) diff --git a/tests/settings.py b/tests/settings.py index f61e178..e58a932 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,10 +1,11 @@ import warnings -warnings.simplefilter('always') + +warnings.simplefilter("always") DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": ":memory:", }, } @@ -12,12 +13,12 @@ USE_L10N = True INSTALLED_APPS = [ - 'django_superform', - 'tests', + "django_superform", + "tests", ] MIDDLEWARE_CLASSES = () -STATIC_URL = '/static/' +STATIC_URL = "/static/" -SECRET_KEY = '0' +SECRET_KEY = "0" diff --git a/tests/test_boundfield.py b/tests/test_boundfield.py index 7450ad8..947ed45 100644 --- a/tests/test_boundfield.py +++ b/tests/test_boundfield.py @@ -32,78 +32,78 @@ class AccountForm(SuperForm): class CompositeBoundFieldTests(TestCase): def test_it_is_nonzero_for_empty_formsets(self): form = AccountForm() - bf = form['emails'] + bf = form["emails"] self.assertTrue(isinstance(bf, CompositeBoundField)) self.assertEqual(len(bf), 0) self.assertEqual(bool(bf), True) def test_it_is_nonzero_for_filled_formsets(self): - form = AccountForm(initial={ - 'emails': [{'email': 'admin@example.com'}] - }) - bf = form['emails'] + form = AccountForm(initial={"emails": [{"email": "admin@example.com"}]}) + bf = form["emails"] self.assertTrue(isinstance(bf, CompositeBoundField)) self.assertEqual(len(bf), 1) self.assertEqual(bool(bf), True) def test_it_is_nonzero_for_forms(self): form = AccountForm() - bf = form['nested_form'] + bf = form["nested_form"] self.assertTrue(isinstance(bf, CompositeBoundField)) self.assertEqual(bool(bf), True) def test_it_iterates_over_form_fields(self): form = AccountForm() - composite_bf = form['nested_form'] + composite_bf = form["nested_form"] for nested_bf in composite_bf: self.assertTrue(isinstance(nested_bf, BoundField)) - self.assertEqual(nested_bf.name, 'name') + self.assertEqual(nested_bf.name, "name") def test_it_iterates_over_formset_forms(self): form = AccountForm() - composite_bf = form['multiple_names'] + composite_bf = form["multiple_names"] for nested_form in composite_bf: self.assertTrue(isinstance(nested_form, NameForm)) def test_it_allows_key_access_to_form_fields(self): form = AccountForm() - composite_f = form['nested_form'] - nested_bf = composite_f['name'] + composite_f = form["nested_form"] + nested_bf = composite_f["name"] self.assertTrue(isinstance(nested_bf, BoundField)) - self.assertEqual(nested_bf.name, 'name') + self.assertEqual(nested_bf.name, "name") def test_it_raises_keyerror_for_nonexistent_form_field(self): form = AccountForm() - composite_bf = form['nested_form'] + composite_bf = form["nested_form"] with self.assertRaises(KeyError): - composite_bf['nope'] + composite_bf["nope"] # Also raises KeyError when using an integer lookup. with self.assertRaises(KeyError): composite_bf[0] def test_it_allows_index_access_to_formset_forms(self): - form = AccountForm(initial={ - 'emails': [ - {'email': 'foo@example.com'}, - {'email': 'bar@example.com'}, - ] - }) - composite_bf = form['emails'] + form = AccountForm( + initial={ + "emails": [ + {"email": "foo@example.com"}, + {"email": "bar@example.com"}, + ] + } + ) + composite_bf = form["emails"] form1 = composite_bf[0] self.assertTrue(isinstance(form1, EmailForm)) - self.assertEqual(form1.initial, {'email': 'foo@example.com'}) + self.assertEqual(form1.initial, {"email": "foo@example.com"}) form2 = composite_bf[1] self.assertTrue(isinstance(form2, EmailForm)) - self.assertEqual(form2.initial, {'email': 'bar@example.com'}) + self.assertEqual(form2.initial, {"email": "bar@example.com"}) def test_it_raises_indexerror_for_nonexistent_formset_forms(self): form = AccountForm() - composite_bf = form['emails'] + composite_bf = form["emails"] with self.assertRaises(IndexError): composite_bf[0] # Raises TypeError when using a string lookup as an integer is a hard # requirement. with self.assertRaises(TypeError): - composite_bf['name'] + composite_bf["name"] diff --git a/tests/test_formfield.py b/tests/test_formfield.py index bf14171..49c7e42 100644 --- a/tests/test_formfield.py +++ b/tests/test_formfield.py @@ -9,7 +9,7 @@ class AddressForm(forms.Form): city = forms.CharField() def __init__(self, *args, **kwargs): - self.custom_attribute = kwargs.pop('custom_kwarg', None) + self.custom_attribute = kwargs.pop("custom_kwarg", None) super(AddressForm, self).__init__(*args, **kwargs) @@ -22,91 +22,88 @@ class RegistrationForm(SuperForm): first_name = forms.CharField() last_name = forms.CharField() address = FormField(AddressForm) - address_custom_kwarg = FormField(AddressForm, kwargs={'custom_kwarg': True}) - address_initial = FormField(AddressForm, kwargs={ - 'initial': { - 'street': 'Homebase 42', - 'city': 'Supertown' - } - }) + address_custom_kwarg = FormField(AddressForm, kwargs={"custom_kwarg": True}) + address_initial = FormField( + AddressForm, kwargs={"initial": {"street": "Homebase 42", "city": "Supertown"}} + ) more_addresses = FormField(MultiAddressForm) class FormFieldTests(TestCase): def test_is_in_composite_fields(self): superform = RegistrationForm() - self.assertTrue('address' in superform.composite_fields) + self.assertTrue("address" in superform.composite_fields) def test_is_in_forms_attr(self): superform = RegistrationForm() - self.assertTrue('address' in superform.forms) - self.assertTrue(isinstance(superform.forms['address'], AddressForm)) + self.assertTrue("address" in superform.forms) + self.assertTrue(isinstance(superform.forms["address"], AddressForm)) def test_form_prefix(self): superform = RegistrationForm() - form = superform.forms['address'] - self.assertEqual(form.prefix, 'form-address') + form = superform.forms["address"] + self.assertEqual(form.prefix, "form-address") def test_field_name(self): superform = RegistrationForm() - boundfield = superform.forms['address']['street'] - self.assertEqual(boundfield.html_name, 'form-address-street') + boundfield = superform.forms["address"]["street"] + self.assertEqual(boundfield.html_name, "form-address-street") - superform = RegistrationForm(prefix='registration') - boundfield = superform.forms['address']['street'] - self.assertEqual(boundfield.html_name, 'registration-form-address-street') + superform = RegistrationForm(prefix="registration") + boundfield = superform.forms["address"]["street"] + self.assertEqual(boundfield.html_name, "registration-form-address-street") def test_form_kwargs(self): superform = RegistrationForm() - form = superform.forms['address_custom_kwarg'] + form = superform.forms["address_custom_kwarg"] self.assertEqual(form.custom_attribute, True) def test_form_kwargs_initial(self): superform = RegistrationForm() - form = superform.forms['address_initial'] - self.assertEqual(form['street'].value(), 'Homebase 42') - self.assertEqual(form['city'].value(), 'Supertown') + form = superform.forms["address_initial"] + self.assertEqual(form["street"].value(), "Homebase 42") + self.assertEqual(form["city"].value(), "Supertown") def test_initial_pass_through(self): - superform = RegistrationForm(initial={ - 'first_name': 'Patricia', - 'address': {'street': 'Default Road'} - }) - form = superform.forms['address'] - self.assertEqual(superform['first_name'].value(), 'Patricia') - self.assertEqual(superform['last_name'].value(), None) - self.assertEqual(form['street'].value(), 'Default Road') - self.assertEqual(form['city'].value(), None) + superform = RegistrationForm( + initial={"first_name": "Patricia", "address": {"street": "Default Road"}} + ) + form = superform.forms["address"] + self.assertEqual(superform["first_name"].value(), "Patricia") + self.assertEqual(superform["last_name"].value(), None) + self.assertEqual(form["street"].value(), "Default Road") + self.assertEqual(form["city"].value(), None) def test_initial_pass_through_with_multiple_layers(self): - superform = RegistrationForm(initial={ - 'more_addresses': { - 'address1': { - 'street': 'Fooway', - 'city': 'Testcity' - }, - 'address2': { - 'street': 'Barboulevard', + superform = RegistrationForm( + initial={ + "more_addresses": { + "address1": {"street": "Fooway", "city": "Testcity"}, + "address2": { + "street": "Barboulevard", + }, } } - }) - address1 = superform.forms['more_addresses'].forms['address1'] - address2 = superform.forms['more_addresses'].forms['address2'] - self.assertEqual(address1['street'].value(), 'Fooway') - self.assertEqual(address1['city'].value(), 'Testcity') - self.assertEqual(address2['street'].value(), 'Barboulevard') - self.assertEqual(address2['city'].value(), None) + ) + address1 = superform.forms["more_addresses"].forms["address1"] + address2 = superform.forms["more_addresses"].forms["address2"] + self.assertEqual(address1["street"].value(), "Fooway") + self.assertEqual(address1["city"].value(), "Testcity") + self.assertEqual(address2["street"].value(), "Barboulevard") + self.assertEqual(address2["city"].value(), None) def test_rendering_form_field(self): - form = MultiAddressForm(initial={ - 'address1': { - 'street': 'Fooway', - }, - 'address2': { - 'street': 'Barboulevard', + form = MultiAddressForm( + initial={ + "address1": { + "street": "Fooway", + }, + "address2": { + "street": "Barboulevard", + }, } - }) - template = Template('{{ form.address1 }} {{ form.address2 }}') - rendered = template.render(Context({'form': form})) + ) + template = Template("{{ form.address1 }} {{ form.address2 }}") + rendered = template.render(Context({"form": form})) assert 'value="Fooway"' in rendered assert 'value="Barboulevard"' in rendered diff --git a/tests/test_formsetfield.py b/tests/test_formsetfield.py index 89734ea..c9e63e7 100644 --- a/tests/test_formsetfield.py +++ b/tests/test_formsetfield.py @@ -6,38 +6,38 @@ from .models import Post, Image -ImageFormSet = modelformset_factory(Image, fields=['name']) +ImageFormSet = modelformset_factory(Image, fields=["name"]) class PostForm(SuperModelForm): class Meta: model = Post - fields = ['title'] + fields = ["title"] - images_inlineformset = InlineFormSetField(Post, Image, fields=['name']) + images_inlineformset = InlineFormSetField(Post, Image, fields=["name"]) images_modelformset = ModelFormSetField(ImageFormSet) def __init__(self, *args, **kwargs): super(PostForm, self).__init__(*args, **kwargs) - self.formsets['images_modelformset'].queryset = self.instance.images.all() + self.formsets["images_modelformset"].queryset = self.instance.images.all() class TestFormSetField(TestCase): def test_inline_formset_field(self): post = Post.objects.create() - images = [post.images.create(name='image1'), post.images.create(name='image2')] + _ = [post.images.create(name="image1"), post.images.create(name="image2")] form = PostForm(instance=post) - t = Template('{{ form.images_inlineformset }}') - c = Context({'form': form}) - assert 'value="image1"' in t.render(c) - assert 'value="image2"' in t.render(c) + t = Template("{{ form.images_inlineformset }}") + c = Context({"form": form}) + assert 'value="image1"' in t.render(c) + assert 'value="image2"' in t.render(c) def test_model_formset_field(self): post = Post.objects.create() - images = [post.images.create(name='image1'), post.images.create(name='image2')] + _ = [post.images.create(name="image1"), post.images.create(name="image2")] form = PostForm(instance=post) - t = Template('{{ form.images_modelformset }}') - c = Context({'form': form}) - assert 'value="image1"' in t.render(c) - assert 'value="image2"' in t.render(c) + t = Template("{{ form.images_modelformset }}") + c = Context({"form": form}) + assert 'value="image1"' in t.render(c) + assert 'value="image2"' in t.render(c) diff --git a/tests/test_global_exports.py b/tests/test_global_exports.py index 7129564..0dbaa50 100644 --- a/tests/test_global_exports.py +++ b/tests/test_global_exports.py @@ -1,14 +1,14 @@ EXPECTED_EXPORTS = ( - 'ForeignKeyFormField', - 'FormField', - 'FormSetField', - 'FormSetWidget', - 'FormWidget', - 'InlineFormSetField', - 'ModelFormField', - 'ModelFormSetField', - 'SuperForm', - 'SuperModelForm', + "ForeignKeyFormField", + "FormField", + "FormSetField", + "FormSetWidget", + "FormWidget", + "InlineFormSetField", + "ModelFormField", + "ModelFormSetField", + "SuperForm", + "SuperModelForm", ) diff --git a/tests/test_media.py b/tests/test_media.py index 6192015..3398441 100644 --- a/tests/test_media.py +++ b/tests/test_media.py @@ -9,7 +9,7 @@ class InputWithCSS(forms.TextInput): class Media(object): css = { - 'all': ['http://example.com/email_widget_style.css'], + "all": ["http://example.com/email_widget_style.css"], } @@ -17,7 +17,7 @@ class InputWithJS(forms.TextInput): """Another test widget with media directives.""" class Media(object): - js = ['http://example.com/check_username_available.js'] + js = ["http://example.com/check_username_available.js"] class EmailForm(forms.Form): @@ -33,10 +33,10 @@ class FormWithNestedMedia(SuperForm): class Media(object): css = { - 'all': ['/static/all.css'], - 'print': ['/static/print.css'], + "all": ["/static/all.css"], + "print": ["/static/print.css"], } - js = ['/static/1.js'] + js = ["/static/1.js"] class FormMediaTests(TestCase): @@ -45,15 +45,15 @@ def test_aggregate_media(self): form = FormWithNestedMedia() expected_css = { - 'all': [ - 'http://example.com/email_widget_style.css', - '/static/all.css', + "all": [ + "http://example.com/email_widget_style.css", + "/static/all.css", ], - 'print': ['/static/print.css'], + "print": ["/static/print.css"], } expected_js = [ - 'http://example.com/check_username_available.js', - '/static/1.js', + "http://example.com/check_username_available.js", + "/static/1.js", ] self.assertEqual(form.media._css, expected_css) diff --git a/tests/test_modelformfield.py b/tests/test_modelformfield.py index 4ee592a..9273bb9 100644 --- a/tests/test_modelformfield.py +++ b/tests/test_modelformfield.py @@ -3,7 +3,7 @@ from django.test import TestCase from django_superform import SuperModelForm, ModelFormField -from .models import Series, Post, Image +from .models import Series, Post class UseFirstModelFormField(ModelFormField): @@ -17,7 +17,7 @@ def get_instance(self, form, name): class SeriesForm(forms.ModelForm): class Meta: model = Series - fields = ('title',) + fields = ("title",) class PostForm(SuperModelForm): @@ -25,7 +25,7 @@ class PostForm(SuperModelForm): class Meta: model = Post - fields = ('title',) + fields = ("title",) class UnrequiredSeriesPostForm(SuperModelForm): @@ -33,7 +33,7 @@ class UnrequiredSeriesPostForm(SuperModelForm): class Meta: model = Post - fields = ('title',) + fields = ("title",) class UseExistingSeriesPostForm(SuperModelForm): @@ -41,39 +41,41 @@ class UseExistingSeriesPostForm(SuperModelForm): class Meta: model = Post - fields = ('title',) + fields = ("title",) class FormFieldTests(TestCase): def test_is_in_composite_fields(self): superform = PostForm() - self.assertTrue('series' in superform.composite_fields) + self.assertTrue("series" in superform.composite_fields) def test_is_in_forms_attr(self): superform = PostForm() - self.assertTrue('series' in superform.forms) - self.assertTrue(isinstance(superform.forms['series'], SeriesForm)) + self.assertTrue("series" in superform.forms) + self.assertTrue(isinstance(superform.forms["series"], SeriesForm)) def test_form_prefix(self): superform = PostForm() - form = superform.forms['series'] - self.assertEqual(form.prefix, 'form-series') + form = superform.forms["series"] + self.assertEqual(form.prefix, "form-series") def test_form_save(self): - superform = PostForm({ - 'title': 'New blog post', - 'form-series-title': 'Unrelated series', - }) + superform = PostForm( + { + "title": "New blog post", + "form-series-title": "Unrelated series", + } + ) superform.save() self.assertEqual(Series.objects.count(), 1) series = Series.objects.get() - self.assertEqual(series.title, 'Unrelated series') + self.assertEqual(series.title, "Unrelated series") self.assertEqual(Post.objects.count(), 1) post = Post.objects.get() - self.assertEqual(post.title, 'New blog post') + self.assertEqual(post.title, "New blog post") # The relation between post and series IS NOT done by the # ModelFormField. It only takes care of nesting. The relation can be @@ -81,51 +83,59 @@ def test_form_save(self): self.assertEqual(post.series, None) def test_override_get_instance(self): - existing_series = Series.objects.create(title='Existing series') + existing_series = Series.objects.create(title="Existing series") superform = UseExistingSeriesPostForm() - self.assertEqual(superform.forms['existing_series'].instance, - existing_series) - self.assertEqual(superform.forms['existing_series']['title'].value(), - 'Existing series') + self.assertEqual(superform.forms["existing_series"].instance, existing_series) + self.assertEqual( + superform.forms["existing_series"]["title"].value(), "Existing series" + ) def test_save_existing_instance(self): - existing_series = Series.objects.create(title='Existing series') - - superform = UseExistingSeriesPostForm({ - 'title': 'Blog post', - 'form-existing_series-title': 'Changed name', - }) + existing_series = Series.objects.create(title="Existing series") + + superform = UseExistingSeriesPostForm( + { + "title": "Blog post", + "form-existing_series-title": "Changed name", + } + ) superform.save() changed_series = Series.objects.get() self.assertEqual(changed_series.pk, existing_series.pk) - self.assertEqual(changed_series.title, 'Changed name') + self.assertEqual(changed_series.title, "Changed name") def test_required(self): - form = PostForm({ - 'title': 'New blog post', - 'form-series-title': '', - }) + form = PostForm( + { + "title": "New blog post", + "form-series-title": "", + } + ) form.full_clean() self.assertFalse(form.is_valid()) - self.assertTrue('required' in str(form.errors['series']['title'])) + self.assertTrue("required" in str(form.errors["series"]["title"])) # Test when key is not in data. - form = PostForm({ - 'title': 'New blog post', - }) + form = PostForm( + { + "title": "New blog post", + } + ) form.full_clean() self.assertFalse(form.is_valid()) - self.assertTrue('required' in str(form.errors['series']['title'])) + self.assertTrue("required" in str(form.errors["series"]["title"])) def test_not_required(self): - form = UnrequiredSeriesPostForm({ - 'title': 'New blog post', - 'form-series-title': '', - }) + form = UnrequiredSeriesPostForm( + { + "title": "New blog post", + "form-series-title": "", + } + ) form.full_clean() self.assertTrue(form.is_valid()) @@ -135,10 +145,12 @@ def test_not_required(self): self.assertEqual(Series.objects.count(), 0) def test_form_render(self): - form = PostForm(initial={ - 'series': { - 'title': 'my title', - }, - }) - rendered = Template('{{ form.series }}').render(Context({'form': form})) + form = PostForm( + initial={ + "series": { + "title": "my title", + }, + } + ) + rendered = Template("{{ form.series }}").render(Context({"form": form})) assert 'value="my title"' in rendered diff --git a/tests/test_superform.py b/tests/test_superform.py index 9deb282..24da9f1 100644 --- a/tests/test_superform.py +++ b/tests/test_superform.py @@ -31,69 +31,74 @@ class SubclassedAccountForm(AccountForm): class SuperFormTests(TestCase): def test_base_composite_fields(self): - self.assertEqual(list(AccountForm.base_fields.keys()), ['username']) - - self.assertTrue(hasattr(AccountForm, 'base_composite_fields')) - self.assertEqual(list(AccountForm.base_composite_fields.keys()), ['emails', 'nested_form']) - self.assertTrue(hasattr(SubclassedAccountForm, 'base_composite_fields')) - self.assertEqual(list(SubclassedAccountForm.base_composite_fields.keys()), ['emails', 'nested_form', 'nested_form_2']) - - field = AccountForm.base_composite_fields['emails'] + self.assertEqual(list(AccountForm.base_fields.keys()), ["username"]) + + self.assertTrue(hasattr(AccountForm, "base_composite_fields")) + self.assertEqual( + list(AccountForm.base_composite_fields.keys()), ["emails", "nested_form"] + ) + self.assertTrue(hasattr(SubclassedAccountForm, "base_composite_fields")) + self.assertEqual( + list(SubclassedAccountForm.base_composite_fields.keys()), + ["emails", "nested_form", "nested_form_2"], + ) + + field = AccountForm.base_composite_fields["emails"] self.assertIsInstance(field, FormSetField) - field = AccountForm.base_composite_fields['nested_form'] + field = AccountForm.base_composite_fields["nested_form"] self.assertIsInstance(field, FormField) - self.assertFalse(hasattr(AccountForm, 'forms')) - self.assertFalse(hasattr(AccountForm, 'formsets')) + self.assertFalse(hasattr(AccountForm, "forms")) + self.assertFalse(hasattr(AccountForm, "formsets")) def test_fields_in_instantiated_forms(self): form = AccountForm() - self.assertTrue(hasattr(form, 'composite_fields')) - self.assertTrue(hasattr(form, 'forms')) - self.assertTrue(hasattr(form, 'formsets')) + self.assertTrue(hasattr(form, "composite_fields")) + self.assertTrue(hasattr(form, "forms")) + self.assertTrue(hasattr(form, "formsets")) - self.assertEqual(list(form.forms.keys()), ['nested_form']) - nested_form = form.forms['nested_form'] + self.assertEqual(list(form.forms.keys()), ["nested_form"]) + nested_form = form.forms["nested_form"] self.assertIsInstance(nested_form, NameForm) - self.assertEqual(list(form.formsets.keys()), ['emails']) - formset = form.formsets['emails'] + self.assertEqual(list(form.formsets.keys()), ["emails"]) + formset = form.formsets["emails"] self.assertIsInstance(formset, EmailFormSet) class FormSetsInSuperFormsTests(TestCase): def setUp(self): self.formset_data = { - 'formset-emails-INITIAL_FORMS': 0, - 'formset-emails-TOTAL_FORMS': 1, - 'formset-emails-MAX_NUM_FORMS': 3, + "formset-emails-INITIAL_FORMS": 0, + "formset-emails-TOTAL_FORMS": 1, + "formset-emails-MAX_NUM_FORMS": 3, } def test_prefix(self): form = AccountForm() - formset = form.formsets['emails'] - self.assertEqual(formset.prefix, 'formset-emails') + formset = form.formsets["emails"] + self.assertEqual(formset.prefix, "formset-emails") def test_validation(self): data = self.formset_data.copy() form = AccountForm(data) self.assertEqual(form.is_valid(), False) - self.assertTrue(form.errors['username']) - self.assertFalse('emails' in form.errors) + self.assertTrue(form.errors["username"]) + self.assertFalse("emails" in form.errors) - self.assertTrue(form.errors['nested_form']) - self.assertTrue(form.errors['nested_form']['name']) - self.assertIsInstance(form.errors['nested_form'], ErrorDict) + self.assertTrue(form.errors["nested_form"]) + self.assertTrue(form.errors["nested_form"]["name"]) + self.assertIsInstance(form.errors["nested_form"], ErrorDict) data = self.formset_data.copy() - data['formset-emails-INITIAL_FORMS'] = 1 + data["formset-emails-INITIAL_FORMS"] = 1 form = AccountForm(data) self.assertEqual(form.is_valid(), False) - self.assertTrue(form.errors['username']) - self.assertTrue(form.errors['emails']) - self.assertIsInstance(form.errors['emails'], ErrorList) + self.assertTrue(form.errors["username"]) + self.assertTrue(form.errors["emails"]) + self.assertIsInstance(form.errors["emails"], ErrorList) def test_empty_form_has_no_errors(self): """Empty forms have no errors.""" @@ -105,28 +110,24 @@ def test_empty_form_has_no_errors(self): def test_formset_errors(self): """Formset errors get propagated properly.""" data = { - 'formset-emails-INITIAL_FORMS': 0, - 'formset-emails-TOTAL_FORMS': 1, - 'formset-emails-MAX_NUM_FORMS': 3, - 'formset-emails-0-email': 'foobar', - 'username': 'TestUser', - 'form-nested_form-name': 'Some Name', + "formset-emails-INITIAL_FORMS": 0, + "formset-emails-TOTAL_FORMS": 1, + "formset-emails-MAX_NUM_FORMS": 3, + "formset-emails-0-email": "foobar", + "username": "TestUser", + "form-nested_form-name": "Some Name", } form = AccountForm(data) if django.VERSION >= (1, 5): - expected_errors = ErrorList([ - ErrorDict({ - u'email': ErrorList([u'Enter a valid email address.']) - }) - ]) + expected_errors = ErrorList( + [ErrorDict({"email": ErrorList(["Enter a valid email address."])})] + ) else: # Fix for Django 1.4 - expected_errors = ErrorList([ - ErrorDict({ - u'email': ErrorList([u'Enter a valid e-mail address.']) - }) - ]) + expected_errors = ErrorList( + [ErrorDict({"email": ErrorList(["Enter a valid e-mail address."])})] + ) self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['emails'], expected_errors) + self.assertEqual(form.errors["emails"], expected_errors) diff --git a/tests/test_widgets.py b/tests/test_widgets.py index fd43d0c..45b87d3 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -4,53 +4,57 @@ class TemplateWidgetTests(TestCase): def test_it_takes_template_name_argument(self): - widget = TemplateWidget(template_name='foo.html') - self.assertEqual(widget.template_name, 'foo.html') + widget = TemplateWidget(template_name="foo.html") + self.assertEqual(widget.template_name, "foo.html") def test_it_puts_hidden_variable_in_context(self): widget = TemplateWidget() - widget_context = widget.get_context('foo', None) - self.assertEqual(widget_context['hidden'], False) + widget_context = widget.get_context("foo", None) + self.assertEqual(widget_context["hidden"], False) class HiddenWidget(TemplateWidget): is_hidden = True hidden_widget = HiddenWidget() - hidden_widget_context = hidden_widget.get_context('foo', None) - self.assertEqual(hidden_widget_context['hidden'], True) + hidden_widget_context = hidden_widget.get_context("foo", None) + self.assertEqual(hidden_widget_context["hidden"], True) def test_it_recognizes_value_context_name(self): class DifferentValueNameWidget(TemplateWidget): - value_context_name = 'strange_name' + value_context_name = "strange_name" value = object() widget = DifferentValueNameWidget() - context = widget.get_context('foo', value) + context = widget.get_context("foo", value) - self.assertTrue(context['strange_name'] is value) + self.assertTrue(context["strange_name"] is value) # The name 'value' is always available, regardless of the # value_context_name. - self.assertTrue(context['value'] is value) + self.assertTrue(context["value"] is value) def test_it_renders_template_from_attribute(self): class TemplateAttributeWidget(TemplateWidget): - template_name = '_print_name.html' + template_name = "_print_name.html" widget = TemplateAttributeWidget() - with self.assertTemplateUsed('_print_name.html'): - result = widget.render(name='A_NAME', value=None, attrs=None) - self.assertEqual(result.strip(), 'A_NAME') + with self.assertTemplateUsed("_print_name.html"): + result = widget.render(name="A_NAME", value=None, attrs=None) + self.assertEqual(result.strip(), "A_NAME") def test_it_render_takes_template_name_argument(self): class TemplateAttributeWidget(TemplateWidget): - template_name = '_foo.html' + template_name = "_foo.html" widget = TemplateAttributeWidget() - with self.assertTemplateUsed('_print_name.html'): - with self.assertTemplateNotUsed('_foo.html'): - result = widget.render(name='A_NAME', value=None, attrs=None, - template_name='_print_name.html') - self.assertEqual(result.strip(), 'A_NAME') + with self.assertTemplateUsed("_print_name.html"): + with self.assertTemplateNotUsed("_foo.html"): + result = widget.render( + name="A_NAME", + value=None, + attrs=None, + template_name="_print_name.html", + ) + self.assertEqual(result.strip(), "A_NAME")