Polls Tutorial¶
Building a Polls App (Tutorial)¶
This tutorial walks you through creating a simple Polls application using vanilla Shopyo. You will learn how to create modules, define models, create forms, and handle views.
Polls Tutorial Prerequisites¶
Create a Project:
mkdir polls_project cd polls_project shopyo new cd polls_project shopyo initialise
Polls Implementation¶
Create the Polls Module: Use the shopyo startapp command to create a new module named polls.
shopyo startapp polls
Define Models: Open modules/polls/models.py and define the Question and Option models.
from init import db from shopyo.api.models import PkModel class Question(PkModel): __tablename__ = "questions" text = db.Column(db.String(200), nullable=False) options = db.relationship("Option", backref="question", lazy=True, cascade="all, delete-orphan") class Option(PkModel): __tablename__ = "options" text = db.Column(db.String(100), nullable=False) votes = db.Column(db.Integer, default=0) question_id = db.Column(db.Integer, db.ForeignKey("questions.id"), nullable=False)
Define Forms: Open modules/polls/forms.py and create a form for adding polls.
from flask_wtf import FlaskForm from wtforms import StringField, SubmitField from wtforms.validators import DataRequired class PollForm(FlaskForm): text = StringField("Question", validators=[DataRequired()]) option1 = StringField("Option 1", validators=[DataRequired()]) option2 = StringField("Option 2", validators=[DataRequired()]) submit = SubmitField("Create Poll")
Create Views: Open modules/polls/view.py and implement the logic.
from flask import redirect, url_for, request from shopyo.api.module import ModuleHelp from shopyo.api.html import notify_success from modules.polls.models import Question, Option from modules.polls.forms import PollForm from init import db mhelp = ModuleHelp(__file__, __name__) blueprint = mhelp.blueprint @blueprint.route("/") def index(): questions = Question.query.all() context = {"questions": questions} return mhelp.render("index.html", **context) @blueprint.route("/create", methods=["GET", "POST"]) def create(): form = PollForm() if form.validate_on_submit(): question = Question(text=form.text.data) db.session.add(question) db.session.commit() opt1 = Option(text=form.option1.data, question_id=question.id) opt2 = Option(text=form.option2.data, question_id=question.id) db.session.add_all([opt1, opt2]) db.session.commit() notify_success("Poll created!") return redirect(url_for("polls.index")) return mhelp.render("create.html", form=form) @blueprint.route("/<int:question_id>/vote", methods=["POST"]) def vote(question_id): option_id = request.form.get("option_id") if option_id: option = Option.query.get_or_404(option_id) option.votes += 1 db.session.commit() notify_success("Vote cast!") return redirect(url_for("polls.results", question_id=question_id)) @blueprint.route("/<int:question_id>/results") def results(question_id): question = Question.query.get_or_404(question_id) return mhelp.render("results.html", question=question)
Create Templates: Create the following files in modules/polls/templates/polls/.
index.html:
{% extends "shopyo_base/main_base.html" %} {% block content %} <div class="container"> <h2>Active Polls</h2> <a href="{{ url_for('polls.create') }}" class="btn btn-primary mb-3">Create Poll</a> <ul class="list-group"> {% for question in questions %} <li class="list-group-item"> <a href="{{ url_for('polls.results', question_id=question.id) }}">{{ question.text }}</a> <form action="{{ url_for('polls.vote', question_id=question.id) }}" method="post" class="mt-2"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/> {% for option in question.options %} <div class="form-check"> <input class="form-check-input" type="radio" name="option_id" value="{{ option.id }}" id="opt{{ option.id }}" required> <label class="form-check-label" for="opt{{ option.id }}"> {{ option.text }} </label> </div> {% endfor %} <button type="submit" class="btn btn-sm btn-success mt-2">Vote</button> </form> </li> {% endfor %} </ul> </div> {% endblock %}create.html:
{% extends "shopyo_base/main_base.html" %} {% block content %} <div class="container"> <h2>Create Poll</h2> <form method="POST"> {{ form.hidden_tag() }} <div class="form-group"> {{ form.text.label }} {{ form.text(class="form-control") }} </div> <div class="form-group"> {{ form.option1.label }} {{ form.option1(class="form-control") }} </div> <div class="form-group"> {{ form.option2.label }} {{ form.option2(class="form-control") }} </div> {{ form.submit(class="btn btn-primary") }} </form> </div> {% endblock %}results.html:
{% extends "shopyo_base/main_base.html" %} {% block content %} <div class="container"> <h2>Results: {{ question.text }}</h2> <ul class="list-group"> {% for option in question.options %} <li class="list-group-item d-flex justify-content-between align-items-center"> {{ option.text }} <span class="badge badge-primary badge-pill">{{ option.votes }} votes</span> </li> {% endfor %} </ul> <a href="{{ url_for('polls.index') }}" class="btn btn-secondary mt-3">Back</a> </div> {% endblock %}Run Migrations:
shopyo db migrate shopyo db upgrade
Run the App:
flask run --debug
Go to http://localhost:5000/polls to see your app in action.
Polls Demo Source Code¶
You can view the source code at shopyo/demo/polls_demo.