deformcolander

Impreratively creating sequence of mapping schemas in Colander and Deform


I am constructing a page where user can leave reviews for any number of products in Colander and Deform. I have grasped all the required elements, but I have still some issues of connecting the dots. Specifically, how I can imperatively (dynamically) create a sequence of N form items and then bind data for them?

This is my attempt for this far:

reviews =[
        {
            "product": "Shampoo",
            "comment": ""
            "rating": 3,
        },

        {
            "product": "Soap",
            "comment": "",
            "rating:" 3,
        },
    ]

rating = colander.Schema()
rating.add(colander.SchemaNode(colander.Int(), name="rating", missing=colander.null, validator=colander.Range(1, 5)))
rating.add(colander.SchemaNode(colander.String(), name="comment", validator=colander.Length(max=4096), missing=""))
ratings = colander.SequenceSchema(name="ratings", default=reviews, children=[rating])

# schema.add(colander.SchemaNode(colander.Sequence(), rating, name="ratings", default=reviews))
schema = CSRFSchema()
schema.add(ratings)

form = deform.Form(schema)
if request.method == "POST":
    controls = request.POST.items()

    try:
        captured = form.validate(controls)
    except deform.ValidationFailure as e:
        return {'form': e.render()}
else:
    rendered_form = form.render()

return locals()

But this results to error:

ValueError: Prototype for <deform.field.Field object at 4576735072 (schemanode 'ratings')> has no name

Solution

  • Ok - figured it out. The innermost SchemaNode() must be named. One must use colander.SchemaNode(colander.Sequence()) to map out sequence of items.

    reviews =[
            {
                "product": "Shampoo",
                "comment": "",
                "rating": 3,
            },
    
            {
                "product": "Soap",
                "comment": "",
                "rating": 3,
            },
        ]
    
    rating = colander.Schema(name="single_rating")
    rating.add(colander.SchemaNode(colander.Int(), name="rating", missing=colander.null, validator=colander.Range(1, 5)))
    rating.add(colander.SchemaNode(colander.String(), name="comment", validator=colander.Length(max=4096), missing=""))
    ratings = colander.SchemaNode(colander.Sequence(), rating, name="ratings", default=reviews)
    
    # schema.add(colander.SchemaNode(colander.Sequence(), rating, name="ratings", default=reviews))
    schema = CSRFSchema()
    schema.add(ratings)
    
    form = deform.Form(schema)
    if request.method == "POST":
        controls = request.POST.items()
    
        try:
            captured = form.validate(controls)
        except deform.ValidationFailure as e:
            return {'form': e.render()}
    else:
        rendered_form = form.render()
    
    return locals()