Flask/WTForms/Bootstrap/CSV

JOOYEUN SEOยท2024๋…„ 10์›” 23์ผ
0

100 Days of Python

๋ชฉ๋ก ๋ณด๊ธฐ
62/76
post-thumbnail

๐Ÿ—‚๏ธ Day62 ํ”„๋กœ์ ํŠธ: ์ปคํ”ผ์™€ ์™€์ดํŒŒ์ด

๊ธฐ๊ธฐ ์ถฉ์ „๊ณผ ์™€์ดํŒŒ์ด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ ์นดํŽ˜ ๋ชฉ๋ก์„ ์ •๋ฆฌํ•˜๋Š” ์›น์‚ฌ์ดํŠธ

๐Ÿ” ์œ ์˜ ์‚ฌํ•ญ

  • ํ™ˆํŽ˜์ด์ง€๋Š” css/styles.css ํŒŒ์ผ ์‚ฌ์šฉ
  • /cafes ๊ฒฝ๋กœ๋Š” cafes.html ํŒŒ์ผ์„ ๋ Œ๋”๋ง
    • cafes.html ํŒŒ์ผ์€ cafe-data.csv์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ํ‘œ๋ฅผ ํฌํ•จ
    • ํžŒํŠธ: cafes๋ผ๋Š” ๊ฐ์ฒด๋Š” /cafes ๊ฒฝ๋กœ์—์„œ cafes.html๋กœ ์ „๋‹ฌ๋จ
  • ์œ„์น˜ URL์€ ํ‘œ ์•ˆ์—์„œ ์ „์ฒด ๋งํฌ ๋Œ€์‹  ์•ต์ปค ํƒœ๊ทธ๋กœ ๋ Œ๋”๋ง
    ('Maps Link'๋ผ๋Š” ๋งํฌ ํ…์ŠคํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•˜๊ณ  href๊ฐ€ ์‹ค์ œ ๋งํฌ)
    • ํžŒํŠธ: ๋ชจ๋“  ์œ„์น˜ ๋งํฌ์˜ ์ฒ˜์Œ ๋„ค ๊ธ€์ž๋Š” 'http'๋กœ ํ‘œ์‹œ
  • ํ™ˆํŽ˜์ด์ง€์˜ 'Show Me!' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด cafes.html ํŽ˜์ด์ง€๋กœ ์ด๋™
  • ๋ฒ„ํŠผ์ด ์—†๋Š” '/add' ๋น„๋ฐ€ ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ URL์— ์ž…๋ ฅํ•  ๊ฒฝ์šฐ add.html ํŒŒ์ผ๋กœ ์ด๋™
  • ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ํšจํ•œ URL์ธ์ง€ ํ™•์ธํ•˜๋Š” ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๊ทœ์น™์„ ์œ„์น˜ URL ํ•„๋“œ์— ํฌํ•จ
  • ์‚ฌ์šฉ์ž๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ add.html์— ์–‘์‹์„ ์ œ์ถœํ•  ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ cafe-data.csv์— ์ถ”๊ฐ€
  • ํ”Œ๋ผ์Šคํฌ ๋ถ€ํŠธ์Šคํฌ๋žฉ์œผ๋กœ add.html ํŽ˜์ด์ง€์— quick_form ๋งŒ๋“ค๊ธฐ

๐Ÿ“„ cafe-data.csv

Cafe Name,Location,Open,Close,Coffee,Wifi,Power
Lighthaus,https://goo.gl/maps/2EvhB4oq4gyUXKXx9,11AM, 3:30PM,โ˜•โ˜•โ˜•โ˜•๏ธ,๐Ÿ’ช๐Ÿ’ช,๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ
Esters,https://goo.gl/maps/13Tjc36HuPWLELaSA,8AM,3PM,โ˜•โ˜•โ˜•โ˜•,๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช,๐Ÿ”Œ
โ€ฆ

โŒจ๏ธ main.py

from flask import Flask, render_template, url_for
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from werkzeug.utils import redirect
from wtforms import StringField, URLField, SelectField, TimeField, SubmitField
from wtforms.validators import DataRequired, URL
import csv

app = Flask(__name__)
app.config['SECRET_KEY'] = '8BYkEfBA6O6donzWlSihBXox7C0sKR6b'
Bootstrap(app)

class CafeForm(FlaskForm):
    cafe = StringField('Cafe name', validators=[DataRequired()])
    location = URLField('Cafe Location on Google Maps (URL)',
                        validators=[DataRequired(), URL(message='Invalid URL format')])
    open = TimeField('Opening Time', validators=[DataRequired()])
    close = TimeField('Closing Time', validators=[DataRequired()])
    coffee_rating = SelectField('Coffee rating',
                                choices=[("โ˜•", "โ˜•"), ("โ˜•โ˜•", "โ˜•โ˜•"), ("โ˜•โ˜•โ˜•", "โ˜•โ˜•โ˜•"), ("โ˜•โ˜•โ˜•โ˜•", "โ˜•โ˜•โ˜•โ˜•"), ("โ˜•โ˜•โ˜•โ˜•โ˜•", "โ˜•โ˜•โ˜•โ˜•โ˜•")],
                                validators=[DataRequired()])
    wifi_rating = SelectField('Wifi Strength Rating',
                              choices=[("โœ˜", "โœ˜"), ("๐Ÿ’ช", "๐Ÿ’ช"), ("๐Ÿ’ช๐Ÿ’ช", "๐Ÿ’ช๐Ÿ’ช"), ("๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช", "๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช"), ("๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช", "๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช"), ("๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช", "๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช๐Ÿ’ช")],
                              validators=[DataRequired()])
    power_rating = SelectField('Power Socket Availability',
                               choices=[("โœ˜", "โœ˜"), ("๐Ÿ”Œ", "๐Ÿ”Œ"), ("๐Ÿ”Œ๐Ÿ”Œ", "๐Ÿ”Œ๐Ÿ”Œ"), ("๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ", "๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ"), ("๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ", "๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ"), ("๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ", "๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ")],
                               validators=[DataRequired()])
    submit = SubmitField(label='Submit')


@app.route("/")
def home():
    return render_template("index.html")


@app.route('/add', methods=["GET", "POST"])
def add_cafe():
    form = CafeForm()
    if form.validate_on_submit():
        with open("cafe-data.csv", mode="a", encoding='utf-8') as csv_file:
            csv_file.write(f"\n{form.cafe.data},"
                           f"{form.location.data},"
                           f"{form.open.data},"
                           f"{form.close.data},"
                           f"{form.coffee_rating.data},"
                           f"{form.wifi_rating.data},"
                           f"{form.power_rating.data}")
        return redirect(url_for('cafes'))
    return render_template('add.html', form=form)


@app.route('/cafes')
def cafes():
    with open('cafe-data.csv', newline='', encoding='utf-8') as csv_file:
        csv_data = csv.reader(csv_file, delimiter=',')
        list_of_rows = []
        for row in csv_data:
            list_of_rows.append(row)
    return render_template('cafes.html', cafes=list_of_rows)


if __name__ == '__main__':
    app.run(debug=True)

๐Ÿ—๏ธ base.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />

    {% block styles %}
      <!-- Load Bootstrap-Flask CSS here -->
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
      <!-- Link to the styles.css here to apply styling to all the child templates.-->
      <link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet" />
    {% endblock %}
    <title>{% block title %}{% endblock %}</title>
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

๐Ÿ—๏ธ index.html

{% extends 'base.html' %}
{% block title %}Coffee and Wifi{% endblock %}

{% block content %}
<div class="jumbotron text-center">
    <div class="container">
  <h1 class="display-4">โ˜•๏ธ Coffee & Wifi ๐Ÿ’ป</h1>
  <p class="lead">Want to work in a cafe but need power and wifi?</p>
  <hr class="my-4">
  <p>You've found the right place! Checkout my collection of cafes with data on power socket availability, wifi speed and coffee quality.</p>
  <a class="btn btn-warning btn-lg" href="{{ url_for('cafes') }}" role="button">Show Me!</a>
</div>
    </div>
{% endblock %}

๐Ÿ—๏ธ cafes.html

{% extends 'base.html' %}
{% block title %}Restaurants{% endblock %}

{% block content %}

<div class="container">
  <div class="row">
    <div class="col-sm-12">

      <h1>All Cafes</h1>

	  <table class="table table-dark table-striped table-hover">
        <!-- cafe-data.csv ์˜ ์ •๋ณด๋กœ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ํ‘œ ์ฑ„์šฐ๊ธฐ-->
        {% for row in cafes %}
          <tr>
            {% for item in row %}
              {% if item[:4] == 'http' %}
                <td><a href="{{ item }}">Maps Link</a></td>
              {% else %}
                <td>{{ item }}</td>
              {% endif %}
            {% endfor %}
          </tr>
        {% endfor %}
  	  </table>
      <p><a href="{{ url_for('home') }}">Return to index page</a></p>

    </div>
  </div>
</div>

{% endblock %}

๐Ÿ—๏ธ add.html

{% extends 'base.html' %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Add A New Cafe{% endblock %}
{% block content %}
<div class="container">
  <div class="row">
    <div class="col-sm-12 col-md-8">

      <h1>Add a new cafe into the database</h1>

      <!-- This is where your WTForm will go -->
      {{ wtf.quick_form(form, novalidate=True) }}

	  <p class="space-above"><a href="{{ url_for('cafes') }}">See all cafes</a></p>

    </div>
  </div>
</div>

{% endblock %}




โ–ท Angela Yu, [Python ๋ถ€ํŠธ์บ ํ”„ : 100๊ฐœ์˜ ํ”„๋กœ์ ํŠธ๋กœ Python ๊ฐœ๋ฐœ ์™„์ „ ์ •๋ณต], Udemy, https://www.udemy.com/course/best-100-days-python/?couponCode=ST3MT72524

0๊ฐœ์˜ ๋Œ“๊ธ€