Skip to content

Commit

Permalink
Merge pull request #502 from MongoEngine/number_fields
Browse files Browse the repository at this point in the history
New model form generator: Support of DecimalField,  FloatField, IntField
  • Loading branch information
insspb committed Aug 12, 2022
2 parents 0cbca28 + a27e008 commit 1c9258c
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 37 deletions.
140 changes: 137 additions & 3 deletions docs/forms.md
Expand Up @@ -97,7 +97,51 @@ Not yet documented. Please help us with new pull request.

### DecimalField

Not yet documented. Please help us with new pull request.
- API: {class}`.db_fields.DecimalField`
- Default form field class: {class}`wtforms.fields.DecimalField`

#### Form generation behaviour

From form generation side this field is pretty standard and do not use any form
generation adjustments.

If database field definition has any of {attr}`min_value` or {attr}`max_value`, then
{class}`~wtforms.validators.NumberRange` validator will be added to form field.

#### Examples

numbers_demo.py in example app contain basic non-requirement example. You can adjust
it to any provided example for test purposes.

##### Not limited DecimalField

```python
"""numbers_demo.py"""
from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

decimal_field_unlimited = db.DecimalField()
```

##### Limited DecimalField

```python
"""numbers_demo.py"""
from decimal import Decimal

from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

decimal_field_limited = db.DecimalField(
min_value=Decimal("1"), max_value=Decimal("200.455")
)
```

### DictField

Expand Down Expand Up @@ -163,11 +207,101 @@ Not yet documented. Please help us with new pull request.

### FloatField

Not yet documented. Please help us with new pull request.
```{versionchanged} 2.0.0
Default form field class changed from: {class}`wtforms.fields.FloatField` to
{class}`~.fields.MongoFloatField`.
```

- API: {class}`.db_fields.FloatField`
- Default form field class: {class}`~.fields.MongoFloatField`

#### Form generation behaviour

For Mongo database {class}`~.db_fields.FloatField` special WTForm field was created.
This field's behaviour is the same, as for {class}`wtforms.fields.FloatField`,
but the widget is replaced to {class}`~wtforms.widgets.NumberInput`, this should make a
look of generated form better. It is possible, that in some cases usage of base,
{class}`wtforms.fields.FloatField` can be required by form design. Both fields are
completely compatible, and replace can be done with {attr}`wtf_field_class` db form
parameter.

If database field definition has any of {attr}`min_value` or {attr}`max_value`, then
{class}`~wtforms.validators.NumberRange` validator will be added to form field.

#### Examples

numbers_demo.py in example app contain basic non-requirement example. You can adjust
it to any provided example for test purposes.

##### Not limited FloatField

```python
"""numbers_demo.py"""
from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

float_field_unlimited = db.FloatField()
```

##### Limited FloatField

```python
"""numbers_demo.py"""
from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

float_field_limited = db.FloatField(min_value=float(1), max_value=200.455)
```

### IntField

Not yet documented. Please help us with new pull request.
- API: {class}`.db_fields.IntField`
- Default form field class: {class}`wtforms.fields.IntegerField`

#### Form generation behaviour

From form generation side this field is pretty standard and do not use any form
generation adjustments.

If database field definition has any of {attr}`min_value` or {attr}`max_value`, then
{class}`~wtforms.validators.NumberRange` validator will be added to form field.

#### Examples

numbers_demo.py in example app contain basic non-requirement example. You can adjust
it to any provided example for test purposes.

##### Not limited IntField

```python
"""numbers_demo.py"""
from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

integer_field_unlimited = db.IntField()
```

##### Limited IntField

```python
"""numbers_demo.py"""
from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

integer_field_limited = db.IntField(min_value=1, max_value=200)
```

### ListField

Expand Down
5 changes: 5 additions & 0 deletions example_app/app.py
Expand Up @@ -4,6 +4,7 @@

from example_app import views
from example_app.models import db
from example_app.numbers_demo import numbers_demo_view
from example_app.strings_demo import strings_demo_view
from flask_mongoengine.panels import mongo_command_logger

Expand Down Expand Up @@ -45,6 +46,10 @@
app.add_url_rule(
"/strings_demo/<pk>/", view_func=strings_demo_view, methods=["GET", "POST"]
)
app.add_url_rule("/numbers_demo", view_func=numbers_demo_view, methods=["GET", "POST"])
app.add_url_rule(
"/numbers_demo/<pk>/", view_func=numbers_demo_view, methods=["GET", "POST"]
)

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
46 changes: 46 additions & 0 deletions example_app/numbers_demo.py
@@ -0,0 +1,46 @@
"""Numbers and related fields demo model."""

from decimal import Decimal

from flask import render_template, request

from example_app.models import db


class NumbersDemoModel(db.Document):
"""Documentation example model."""

simple_sting_name = db.StringField()
float_field_unlimited = db.FloatField()
decimal_field_unlimited = db.DecimalField()
integer_field_unlimited = db.IntField()
float_field_limited = db.FloatField(min_value=float(1), max_value=200.455)
decimal_field_limited = db.DecimalField(
min_value=Decimal("1"), max_value=Decimal("200.455")
)
integer_field_limited = db.IntField(min_value=1, max_value=200)


NumbersDemoForm = NumbersDemoModel.to_wtf_form()


def numbers_demo_view(pk=None):
"""Return all fields demonstration."""
form = NumbersDemoForm()
obj = None
if pk:
obj = NumbersDemoModel.objects.get(pk=pk)
form = NumbersDemoForm(obj=obj)

if request.method == "POST" and form.validate_on_submit():
if pk:
form.populate_obj(obj)
obj.save()
else:
form.save()
page_num = int(request.args.get("page") or 1)
page = NumbersDemoModel.objects.paginate(page=page_num, per_page=100)

return render_template(
"numbers_demo.html", page=page, form=form, model=NumbersDemoModel
)
1 change: 1 addition & 0 deletions example_app/templates/layout.html
Expand Up @@ -19,6 +19,7 @@
<li><a href="{{ url_for("index") }}">Home</a></li>
<li><a href="{{ url_for("pagination") }}">Pagination</a></li>
<li><a href="{{ url_for("strings_demo_view") }}">Strings demo</a></li>
<li><a href="{{ url_for("numbers_demo_view") }}">Numbers demo</a></li>
</ul>
</nav>
<div>
Expand Down
41 changes: 41 additions & 0 deletions example_app/templates/numbers_demo.html
@@ -0,0 +1,41 @@
{% extends "layout.html" %}
{% from "_formhelpers.html" import render_field %}
{% from "_formhelpers.html" import render_navigation %}

{% block body %}

<div>
<table>
<thead>
<tr>
{% for field in model._fields_ordered %}
<th>{{ model[field].name }}</th>
{% endfor %}
<th>Edit</th>
</tr>
</thead>
<tbody>
{% for page_object in page.items %}
<tr>
{% for field in page_object._fields_ordered %}
<td>{{ page_object[field] }}</td>
{% endfor %}
<td><a href="{{ url_for('numbers_demo_view', pk=page_object.pk) }}">edit</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div>
{{ render_navigation(page, "numbers_demo_view") }}
</div>
<div>
<form method="POST">
{% for field in form %}
{{ render_field(field, style='font-weight: bold') }}
{% endfor %}
<input type="submit" value="{% if form.instance %}Edit{% else %}Create{% endif %}">
</form>
</div>

{% endblock %}
2 changes: 2 additions & 0 deletions example_app/views.py
Expand Up @@ -4,6 +4,7 @@
from mongoengine.context_managers import switch_db

from example_app import models
from example_app.numbers_demo import NumbersDemoModel
from example_app.strings_demo import StringsDemoModel


Expand Down Expand Up @@ -49,6 +50,7 @@ def delete_data():
with switch_db(models.Todo, "default"):
models.Todo.objects().delete()
StringsDemoModel.objects().delete()
NumbersDemoModel.objects().delete()
with switch_db(models.Todo, "secondary"):
models.Todo.objects().delete()

Expand Down

0 comments on commit 1c9258c

Please sign in to comment.