Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoengine, Flask and ReferenceField in WTForms

Hy everybody, I'm realizing a Flask/MongoDB project and since I am new to this world, I've followed the tutorial at this page: http://docs.mongodb.org/ecosystem/tutorial/write-a-tumblelog-application-with-flask-mongoengine/

After that, I've started to code my own application and this is part of the code:

MODELS:

class Generic(db.Document):
    descrizione = db.StringField(max_length=255, required=True)

    meta = {
        'allow_inheritance': True,
        'indexes': [
            {'fields': ['descrizione'], 'unique': True}
        ]
    }

class Category(Generic):
    def __call__(self, *args):
        pass

class User(db.Document):
    email = db.EmailField(max_length=255, required=True)
    nickname = db.StringField(max_length=255, required=True)
    password = db.StringField(max_length=16, required=True)
    categoria = db.ReferenceField('Category', required=True)

    meta = {
        'indexes': [
            {'fields': ['nickname', 'email'], 'unique': True}
        ]
    }

As you can see above, I've a "Category" class which inherits the "Generic" class. The "User" class finally has a ReferenceField to the Category. This way when I create a user, the category field on mongo db is stored as an ObjectID, related to the "generic" collection which has all the categories I've created.

The next step is to create the form to insert new documents into the user collection. In my Views python file I've this cose:

def iscrizione():

    form = model_form(User, only=['email', 'nickname', 'password', 'categoria'])(request.form)

    if request.method == 'GET':
        ctx = {
            'form': form
        }
        return render_template('users/iscrizione.html', **ctx)

The template uses the Jinja macro reported in the tutorial page:

{% macro render(form) -%}
<fieldset>
{% for field in form %}
{% if field.type in ['CSRFTokenField', 'HiddenField'] %}
  {{ field() }}
{% else %}
  <div class="clearfix {% if field.errors %}error{% endif %}">
    {{ field.label }}
<div class="input">
  {% if field.name == "body" %}
    {{ field(rows=10, cols=40) }}
  {% else %}
    {{ field() }}
  {% endif %}
  {% if field.errors or field.help_text %}
    <span class="help-inline">
    {% if field.errors %}
      {{ field.errors|join(' ') }}
    {% else %}
      {{ field.help_text }}
    {% endif %}
    </span>
  {% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</fieldset>
{% endmacro %}

And finally, this is my problem (If you have reached this text, you are my hero)

When I visit the webpage with the rendered form, the macro correctly show the text fields, and for the ReferenceField in my model it show a combo box. The options values in the select combo are perfectly aligned with the object id of the category documents I've created. Choosing one of these and submitting the form, my application correctly creates the new user document.

Unfortunately, the select box labels doesn't show a human readable value, reporting "Category object".

<select id="categoria" name="categoria">
  <option value="530536363c74031c24ee7ab6">Category object</option>
  <option value="5305362d3c74031c24ee7ab5">Category object</option>
  <option value="530535793c74031b73dd07b4">Category object</option>
</select>

How can I manage to show a correct label for the select box?

like image 844
Mattia Galati Avatar asked Feb 21 '14 07:02

Mattia Galati


3 Answers

Finally I've made it! Suppose that the field "categoria" of the User document is a ReferenceField to the "Category" collection. Just add the "label_attr" attribute to "form.categoria" using the field name of the Category model that you want as label.

def iscrizione():
    form = model_form(User, only=['email', 'nickname', 'password', 'categoria'])(request.form)
    form.categoria.label_attr='descrizione'  #<< add this line
    if request.method == 'GET':
        ctx = {
        'form': form
    }
    return render_template('users/iscrizione.html', **ctx)
like image 195
Mattia Galati Avatar answered Oct 05 '22 05:10

Mattia Galati


This can also be made through the field args in the model_form function:

form = model_form(
    User,
    only=['email', 'nickname', 'password', 'categoria'],
    field_args={'categoria': {'label_attr': 'descrizione'}}
)
like image 41
jbejar Avatar answered Oct 05 '22 06:10

jbejar


Maybe its will be useful to someone. You can use standard approach, like define

def __str__(self):
    return self.descrizione

for your Category class

like image 23
Александр Яцков Avatar answered Oct 05 '22 07:10

Александр Яцков