diff --git a/.gitignore b/.gitignore index 5c33273..f54a743 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ coverage.xml *.log local_settings.py db.sqlite3 +django-bleach.db # Flask stuff: instance/ diff --git a/django_bleach/models.py b/django_bleach/models.py index ed1590b..74ce071 100644 --- a/django_bleach/models.py +++ b/django_bleach/models.py @@ -2,6 +2,7 @@ from bleach import clean +from . import forms from .utils import get_bleach_default_options @@ -27,6 +28,26 @@ def __init__(self, allowed_tags=None, allowed_attributes=None, if strip_comments: self.bleach_kwargs["strip_comments"] = strip_comments + def formfield(self, **kwargs): + """ + Makes field for ModelForm + """ + + # If field doesn't have any choice return BleachField + if not self.choices: + return forms.BleachField( + label=self.verbose_name, + max_length=self.max_length, + allowed_tags=self.bleach_kwargs.get("tags"), + allowed_attributes=self.bleach_kwargs.get("attributes"), + allowed_styles=self.bleach_kwargs.get("styles"), + allowed_protocols=self.bleach_kwargs.get("protocols"), + strip_tags=self.bleach_kwargs.get("strip"), + strip_comments=self.bleach_kwargs.get("strip_comments"), + ) + + return super(BleachField, self).formfield(**kwargs) + def pre_save(self, model_instance, add): data = getattr(model_instance, self.attname) if data: diff --git a/django_bleach/tests/test_modelformfield.py b/django_bleach/tests/test_modelformfield.py new file mode 100644 index 0000000..af74ff1 --- /dev/null +++ b/django_bleach/tests/test_modelformfield.py @@ -0,0 +1,44 @@ +from django import forms +from django.test import TestCase, override_settings +from django_bleach import forms as bleach_forms +from .test_models import BleachContent + + +class BleachContentModelForm(forms.ModelForm): + class Meta: + model = BleachContent + fields = '__all__' + + +class TestModelFormField(TestCase): + @override_settings(BLEACH_DEFAULT_WIDGET='testproject.forms.CustomBleachWidget') + def setUp(self): + model_form = BleachContentModelForm() + self.form_field = model_form.fields['content'] + self.choice_form_field = model_form.fields['choice'] + self.model_field = BleachContent()._meta.get_field('content') + self.default_widget_class = bleach_forms.get_default_widget() + + def test_formfield_type(self): + """ Check content's form field is instance of BleachField + """ + self.assertIsInstance(self.form_field, bleach_forms.BleachField) + + def test_custom_widget(self): + """ Check content form field's widget is instance of default widget + """ + self.assertIsInstance(self.form_field.widget, self.default_widget_class) + + def test_same_allowed_args(self): + """ Check model and form's allowed arguments (tags, attributes, ...) are same + """ + form_allowed_args: dict = self.form_field.bleach_options + model_allowed_args: dict = self.model_field.bleach_kwargs + + self.assertEqual(model_allowed_args, form_allowed_args) + + def test_with_choices(self): + """ Check if choices specified, use TextField's default widget (Select). + """ + form_field_widget = self.choice_form_field.widget.__class__ + self.assertEqual(form_field_widget, forms.widgets.Select) diff --git a/django_bleach/tests/test_models.py b/django_bleach/tests/test_models.py index d426ee9..5a2fbf8 100644 --- a/django_bleach/tests/test_models.py +++ b/django_bleach/tests/test_models.py @@ -10,6 +10,10 @@ class BleachContent(models.Model): """ Bleach test model""" + CHOICES = ( + ('f', 'first choice'), + ('s', 'second choice') + ) content = BleachField( allowed_attributes=ALLOWED_ATTRIBUTES, allowed_protocols=ALLOWED_PROTOCOLS, @@ -18,6 +22,7 @@ class BleachContent(models.Model): strip_comments=True, strip_tags=True ) + choice = BleachField(choices=CHOICES) class TestBleachModelField(TestCase): diff --git a/django_bleach/tests/test_settings.py b/django_bleach/tests/test_settings.py index 55e623a..6a05bc5 100644 --- a/django_bleach/tests/test_settings.py +++ b/django_bleach/tests/test_settings.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django.core.exceptions import ImproperlyConfigured from django.forms import Textarea -from django.test import TestCase +from django.test import TestCase, override_settings from mock import patch from django_bleach.forms import get_default_widget @@ -53,6 +53,7 @@ def test_strip_comments(self, settings): class TestDefaultWidget(TestCase): """ Test form field widgets """ + @override_settings(BLEACH_DEFAULT_WIDGET='django.forms.widgets.Textarea') def test_default_widget(self): self.assertEqual(get_default_widget(), Textarea) diff --git a/testproject/forms.py b/testproject/forms.py index 112c14a..da343da 100755 --- a/testproject/forms.py +++ b/testproject/forms.py @@ -1,6 +1,7 @@ from django import forms from django_bleach.forms import BleachField +from testproject.models import Person from testproject.constants import ( ALLOWED_ATTRIBUTES, ALLOWED_PROTOCOLS, @@ -10,7 +11,11 @@ class CustomBleachWidget(forms.Textarea): - pass + + def __init__(self, attrs=None): + default_attrs = {'rows': 15, 'cols': 60} + default_attrs.update(attrs or {}) + super().__init__(attrs=default_attrs) class BleachForm(forms.Form): @@ -47,3 +52,10 @@ class BleachForm(forms.Form): allowed_tags=ALLOWED_TAGS, allowed_styles=ALLOWED_STYLES ) + + +class PersonForm(forms.ModelForm): + + class Meta: + model = Person + fields = '__all__' diff --git a/testproject/migrations/0001_initial.py b/testproject/migrations/0001_initial.py new file mode 100644 index 0000000..8ab9bf8 --- /dev/null +++ b/testproject/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.4 on 2021-06-15 12:39 + +from django.db import migrations, models +import django_bleach.models + + +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=20)), + ('biography', django_bleach.models.BleachField(max_length=100, verbose_name='Person biography')), + ], + ), + ] diff --git a/testproject/migrations/__init__.py b/testproject/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/testproject/models.py b/testproject/models.py new file mode 100644 index 0000000..a0a6f64 --- /dev/null +++ b/testproject/models.py @@ -0,0 +1,14 @@ +from django.db import models +from django_bleach.models import BleachField + + +class Person(models.Model): + name = models.CharField(max_length=20) + biography = BleachField( + max_length=100, + verbose_name='Person biography', + allowed_tags=['p', 'a', 'li', 'ul', 'strong'], + allowed_attributes=['class', 'href', 'style'], + allowed_protocols=['http', 'https'], + allowed_styles=['color', 'background-color'] + ) diff --git a/testproject/settings.py b/testproject/settings.py index c91866e..426a27b 100755 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -15,7 +15,7 @@ 'NAME': os.path.join(PROJECT_PATH, 'django-bleach.db') } } - +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DATABASE_SUPPORTS_TRANSACTIONS = True INSTALLED_APPS = [ @@ -68,3 +68,5 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware' ) + +BLEACH_DEFAULT_WIDGET = 'testproject.forms.CustomBleachWidget' diff --git a/testproject/templates/home.html b/testproject/templates/home.html index 4b10859..ec4dda2 100755 --- a/testproject/templates/home.html +++ b/testproject/templates/home.html @@ -1,4 +1,5 @@
+Name | +Biography | +
---|---|
{{person.name}} | +{{person.biography|safe}} | +