Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
models.BleachField's form field set to forms.BleachField (#19)
Browse files Browse the repository at this point in the history
* models.BleachField's form field set to forms.BleachField

* BleachField form field label implemented

models.BleachField form field label set to field's verbose_name

* CustomBleachWidget customized and set in settings.py as bleach default widget

* testproject person model, person form and model_form view created

* test_default_widget BLEACH_DEFAULT_WIDGET setting overridden to ensure being independent of settings

* ModelFormField tests added

* forms.py flake8 errors fixed

* testproject views.py request.path removed for redirect to fix CodeQl failur check

* formfield test added with specified choices and test_same_allowed_args improved
  • Loading branch information
Alirezaja1384 authored Jun 21, 2021
1 parent 70f4e21 commit 0dfcd6d
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ coverage.xml
*.log
local_settings.py
db.sqlite3
django-bleach.db

# Flask stuff:
instance/
Expand Down
21 changes: 21 additions & 0 deletions django_bleach/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from bleach import clean

from . import forms
from .utils import get_bleach_default_options


Expand All @@ -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:
Expand Down
44 changes: 44 additions & 0 deletions django_bleach/tests/test_modelformfield.py
Original file line number Diff line number Diff line change
@@ -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)
5 changes: 5 additions & 0 deletions django_bleach/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -18,6 +22,7 @@ class BleachContent(models.Model):
strip_comments=True,
strip_tags=True
)
choice = BleachField(choices=CHOICES)


class TestBleachModelField(TestCase):
Expand Down
3 changes: 2 additions & 1 deletion django_bleach/tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)

Expand Down
14 changes: 13 additions & 1 deletion testproject/forms.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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):
Expand Down Expand Up @@ -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__'
23 changes: 23 additions & 0 deletions testproject/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -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')),
],
),
]
Empty file.
14 changes: 14 additions & 0 deletions testproject/models.py
Original file line number Diff line number Diff line change
@@ -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']
)
4 changes: 3 additions & 1 deletion testproject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -68,3 +68,5 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware'
)

BLEACH_DEFAULT_WIDGET = 'testproject.forms.CustomBleachWidget'
2 changes: 2 additions & 0 deletions testproject/templates/home.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<body>
<h2>Form example</h2>
<form action="." method="post">
{% csrf_token %}

Expand All @@ -9,4 +10,5 @@
<input type="submit" value="Submit" />
</div>
</form>
<a href="{% url 'model_form' %}">Model form example(Note: you need to migrate database to use it)</a>
</body>
48 changes: 48 additions & 0 deletions testproject/templates/model_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{% load bleach_tags %}

<style>
table {
width: 100%;
}

td, th {
border: 1px solid #dddddd;
padding: 8px;
}
</style>

<body>
<h2>Model form example</h2>
<strong>Note: You must migrate project to add person!</strong>

<form action="/model_form" method="post">
{% csrf_token %}

{{ form.errors }}
{{ form.as_p }}

<div>
<input type="submit" value="Submit" />
</div>
</form>

<div>
<h3>People list</h3>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Biography</th>
</tr>
{% for person in people %}
<tr>
<td>{{person.name}}</td>
<td>{{person.biography|safe}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

<a href="{% url 'home' %}">Form example</a>
</body>
5 changes: 3 additions & 2 deletions testproject/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
except ImportError:
from django.conf.urls.defaults import url

from .views import home
from .views import (home, model_form)

urlpatterns = [
url(r'^$', home),
url('^$', home, name='home'),
url('^model_form$', model_form, name='model_form'),
]
24 changes: 23 additions & 1 deletion testproject/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.db import OperationalError

from .forms import BleachForm
from .models import Person
from .forms import BleachForm, PersonForm


def home(request):
Expand All @@ -15,3 +17,23 @@ def home(request):
form = BleachForm()

return render(request, 'home.html', {'form': form})


def model_form(request):

if request.POST:
form = PersonForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('?ok')
else:
form = PersonForm()
try:
people = list(Person.objects.all())
except OperationalError:
people = []

return render(request, 'model_form.html', {
'form': form,
'people': people,
})

0 comments on commit 0dfcd6d

Please sign in to comment.