Question

I have the following models in a flask application with the Peewee ORM:

class Course(db.Model):
    name = CharField()

class Activity(db.Model):
    course = ForeignKeyField(Course)
    name = CharField()

I want to generate a multiple select field, in which each course is an optgroup and each activity is an option in that opgroup with Jinja.

Say I have something like this:

  • Course I
    • Activity I
    • Activity II
    • Activity III
  • Course II
    • Activity IV
    • Activity V

that is, the ForeignKeyField in Activity I, II and III points to Course I, while the ForeignKeyField in Course II points to Activity IV and V.

I have the following view in my Flask application:

@app.route("/activities")
@login_required
def activities():
    activities = Activity.select()
    return render_template("activities.html", 
    activities = activities)

I want to get something like this:

<select>
            <optgroup label="Course I">
              <option>Activity I</option>
              <option>Activity II</option>
              <option>Activity III</option>
            </optgroup>
            <optgroup label="Course II">
              <option>Activity IV</option>
              <option>Activity V</option>
            </optgroup>
 </select>

It is easy to get this without optgroup part in Jinja:

<select>
{% for activity in activities %} 
<option value="{{activity.id}}">{{activity.name}}</option>
{% endfor %}
</select>

but I have no idea how to get the Course.name field in the optgroup and then the activities that have their ForeignKeyField point to this course in the options within this optgroup. The only solution I can come up with is to run through all courses in a for loop and then have another for loop within that for loop to run through all activities and generate a new option if the activity.Course.name field matches course.name, but this is horribly inefficient, since you have to loop through all activities for each course.

Is there an easy and efficient way to generate this select box?

Was it helpful?

Solution

You will want to have a look at the docs for more information, but Pewee supports joins out of the box:

class Course(db.Model):
    name = CharField()

class Activity(db.Model):
    # Note that we have added a backreference
    # to activities on the `Course` model with
    # the `related_name` argument
    # so we can do course.activites
    course = ForeignKeyField(Course, related_name='activities')
    name = CharField()

results = Course.select(Course, Activity).join(Activity)
# results is now a collection of courses, each course
# with all of its activities and it all should have been
# pulled out with one SQL query

Then, in your Jinja template you can simply:

<select name="activities">
{% for course in courses %} 
 <optgroup label="course.name">
    {% for activity in course.activities %} 
    <option value="{{activity.id}}">{{activity.name}}</option>
    {% endfor %}
</optgroup>
{% endfor %}
</select>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top