Skip to content

Django 111 admin update #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ media/css/*.r*.css
*DS_Store
*.egg-info
dist/
example/static
example/static
build/
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
recursive-include doc_src *
recursive-include docs *
recursive-include formfield *.html *.png *.gif *js *.css *jpg *jpeg *svg *py

include LICENSE
include README
Expand Down
2 changes: 1 addition & 1 deletion example/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from django.core.management import execute_from_command_line

Expand Down
25 changes: 25 additions & 0 deletions example/sample_app/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2018-08-14 18:48
from __future__ import unicode_literals

from django.db import migrations, models
import formfield.fields


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Person',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('meta_info', formfield.fields.ModelFormField(blank=True, null=True)),
],
),
]
Empty file.
2 changes: 1 addition & 1 deletion example/sample_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PersonMetaForm(forms.Form):
age = forms.IntegerField()
sex = forms.ChoiceField(required=False, choices=((1, 'male'), (2, 'female')))
name = forms.CharField()
date = forms.DateTimeField(required=False)
date = forms.DateTimeField(required=False, widget=forms.DateTimeInput(attrs={'type': 'datetime-local'}))

subform = FormField(SubPersonMetaForm)

Expand Down
16 changes: 16 additions & 0 deletions example/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
WSGI config for example project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

application = get_wsgi_application()
5 changes: 3 additions & 2 deletions formfield/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

__version_info__ = {
'major': 0,
'minor': 4,
'micro': 0,
'minor': 5,
'micro': 2,
'releaselevel': 'final',
'serial': 1
}
Expand All @@ -25,6 +25,7 @@ def get_version():
vers.append('%(releaselevel)s%(serial)i' % __version_info__)
return ''.join(vers)


__version__ = get_version()

try:
Expand Down
38 changes: 25 additions & 13 deletions formfield/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@ def __init__(self, *args, **kwargs):

super(JSONField, self).__init__(*args, **kwargs)

def to_python(self, value):
def from_db_value(self, value, *args, **kwargs):
if isinstance(value, six.string_types):
try:
return json.loads(value, **self.load_kwargs)
except ValueError:
pass

return value

def to_python(self, value):
if isinstance(value, six.string_types):
try:
return json.loads(value, **self.load_kwargs)
Expand All @@ -37,7 +45,6 @@ def to_python(self, value):
return value

def get_db_prep_value(self, value, *args, **kwargs):

if isinstance(value, six.string_types):
return value

Expand All @@ -51,7 +58,7 @@ def value_to_string(self, obj):
class FormField(forms.MultiValueField):
"""The form field we can use in forms"""

def __init__(self, form, **kwargs):
def __init__(self, form, *args, **kwargs):
import inspect
if inspect.isclass(form) and issubclass(form, forms.Form):
form_class = form
Expand All @@ -75,7 +82,8 @@ def __init__(self, form, **kwargs):

self.max_length = kwargs.pop('max_length', None)

super(FormField, self).__init__(**kwargs)
fields = []
super(FormField, self).__init__(fields, *args, **kwargs)

self.fields = [f.field for f in self.form]

Expand All @@ -87,6 +95,7 @@ def compress(self, data_list):
if data_list:
return dict(
(f.name, data_list[i]) for i, f in enumerate(self.form))

return data

def clean(self, value):
Expand All @@ -97,7 +106,17 @@ def clean(self, value):
raise ValidationError(
'Error found in Form Field: Nothing to validate')

data = dict((bf.name, value[i]) for i, bf in enumerate(self.form))
if isinstance(value, dict):
# MultiValueField iterates back through each field. A nested FormField
# Will get called twice: once for the parent form and once from the superclass
# The second time, the value is a dict, and needs to be re-converted to
# avoid errors
data = value
value_list = [data[x.name] for x in self.form]
value = value_list
else:
data = dict((bf.name, value[i]) for i, bf in enumerate(self.form))

self.form = form = self.form.__class__(data)
if not form.is_valid():
error_dict = list(form.errors.items())
Expand Down Expand Up @@ -127,11 +146,4 @@ def __init__(self, *args, **kwargs):
def formfield(self, form_class=FormField, **kwargs):
# Need to supply form to FormField
return super(ModelFormField, self).formfield(form_class=form_class,
form=self.form, **kwargs)

try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^formfield\.fields\.JSONField"])
add_introspection_rules([], ["^formfield\.fields\.ModelFormField"])
except ImportError:
pass
form=self.form, **kwargs)
5 changes: 5 additions & 0 deletions formfield/jinja2/django/forms/widgets/formfield.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ul class="formfield">{% for widget in widget.subwidgets -%}
<li class="formfield-field">
{{widget.label}}{% include widget.template_name %}{{widget.help_text}}
</li>{%- endfor %}
</ul>
7 changes: 7 additions & 0 deletions formfield/templates/django/forms/widgets/formfield.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% spaceless %}
<ul class="formfield">{% for widget in widget.subwidgets %}
<li class="formfield-field">
{{widget.label}}{% include widget.template_name %}{{widget.help_text}}
</li>{% endfor %}
</ul>
{% endspaceless %}
52 changes: 48 additions & 4 deletions formfield/widgets.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django import forms
from django.utils.safestring import mark_safe


class FormFieldWidget(forms.MultiWidget):
"""
This widget will render each field found in the supplied form.
"""
template_name = 'django/forms/widgets/formfield.html'

def __init__(self, fields, attrs=None):
self.fields = fields
# Retreive each field widget for the form
Expand Down Expand Up @@ -43,14 +46,24 @@ def format_label(self, field, counter):
"""
Format the label for each field
"""
return '<label for="id_formfield_%s" %s>%s</label>' % (
counter, field.field.required and 'class="required"', field.label)
if field.field.widget.is_hidden:
return ''
if field.field.required:
required = 'class="required"'
else:
required = ''
return mark_safe(
'<label for="id_formfield_%s" %s>%s</label>' % (
counter, required, field.label.title()))

def format_help_text(self, field, counter):
"""
Format the help text for the bound field
"""
return '<p class="help">%s</p>' % field.help_text
if field.help_text and not field.field.widget.is_hidden:
return mark_safe('<p class="help">%s</p>' % field.help_text)
else:
return ''

def format_output(self, rendered_widgets):
"""
Expand All @@ -62,6 +75,37 @@ def format_output(self, rendered_widgets):
help_text = self.format_help_text(field, i)
ret.append(u'<li>%s %s %s</li>' % (
label, rendered_widgets[i], field.help_text and help_text))

ret.append(u'</ul>')
return ''.join(ret)

def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
if self.is_localized:
for widget in self.widgets:
widget.is_localized = self.is_localized
# value is a list of values, each corresponding to a widget
# in self.widgets.
if not isinstance(value, list):
value = self.decompress(value)

final_attrs = context['widget']['attrs']
final_attrs.pop('type', None)
id_ = final_attrs.get('id')
subwidgets = []
for i, widget in enumerate(self.widgets):
widget_name = '%s_%s' % (name, i)
try:
widget_value = value[i]
except IndexError:
widget_value = None
if id_:
widget_attrs = final_attrs.copy()
widget_attrs['id'] = '%s_%s' % (id_, i)
else:
widget_attrs = final_attrs
subwidget = widget.get_context(widget_name, widget_value, widget_attrs)['widget']
subwidget['label'] = self.format_label(self.fields[i], i)
subwidget['help_text'] = self.format_help_text(self.fields[i], i)
subwidgets.append(subwidget)
context['widget']['subwidgets'] = subwidgets
return context
13 changes: 13 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[bdist_wheel]
universal=1

[flake8]
ignore = D203
exclude =
transactions/migrations,
.git,
.tox,
docs/conf.py,
build,
dist
max-line-length = 119
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_readme():
author_email='[email protected]',
description=DESC,
long_description=get_readme(),
packages=find_packages(),
packages=find_packages(exclude=['example*', 'tests*', 'docs', 'build', ]),
include_package_data=True,
install_requires=read_file('requirements.txt'),
classifiers=[
Expand Down