IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    搭建 Flask Htmlx daisyUI 开发环境

    Hacper\'s Blog发表于 2023-10-28 20:39:01
    love 0

    目的

    想掌握一套 web 全栈开发技术,用于开发、验证小项目,但不想花太大学习成本,需要一套轻量、简洁、易上手的技术栈。

    查找一些资料之后,最终的选择如下

    后端:flask 前端:htmlx+Tailwind CSS+daisyUI

    • htmlx 可以在html中使用属性发送 AJAX 请求,处理服务器响应,可以替换大部分需要 JavaScript 来实现功能。
    • Tailwind CSS 可以让我们在 html 中添加特定的工具类便能完成页面布局,无须单独写css。
    • daisyUI 则是一个基于 Tailwind CSS 的组件库,简化 Tailwind CSS 的使用。

    搭建步骤

    安装 python 和 flask 环境

    1
    2
    
    py -3 -m venv venv
    .\venv\Scripts\Activate.ps1
    
    1
    
    pip install flask flask-assets
    

    安装 Tailwind CSS 和 daisyUI

    1
    2
    
    npm i -D daisyui@latest
    npm install -D @tailwindcss/typography
    

    如果不需要 daisyUI 组件,可以直接使用 Tailwind CSS 的命令行工具,而不需要安装 npm 和 nodejs。

    1
    
    pip install pytailwindcss  
    

    如果要 daisyUI 暂时没办法,没有独立的安装工具。

    下载 htmlx

    下载 htmx.min.js 到 static\src\htmx.min.js 路径

    整合 Tailwind CSS 到 flask

    1
    
    npx tailwindcss init
    

    修改 tailwind.config.js

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    /** @type {import('tailwindcss').Config} */
    module.exports = {
      content: [
        "./templates/**/*.html",
      ],
      theme: {
        extend: {},
      },
      plugins: [require("@tailwindcss/typography"), require("daisyui")],
      // daisyUI config (optional - here are the default values)
      daisyui: {
        themes: false, // true: all themes | false: only light + dark | array: specific themes like this ["light", "dark", "cupcake"]
        darkTheme: "dark", // name of one of the included themes for dark mode
        base: true, // applies background color and foreground color for root element by default
        styled: true, // include daisyUI colors and design decisions for all components
        utils: true, // adds responsive and modifier utility classes
        rtl: false, // rotate style direction from left-to-right to right-to-left. You also need to add dir="rtl" to your html tag and install `tailwindcss-flip` plugin for Tailwind CSS.
        prefix: "", // prefix for daisyUI classnames (components, modifiers and responsive class names. Not colors)
        logs: true, // Shows info about daisyUI version and used config in the console when building your CSS
      },
    }
    

    增加文件 static\src\main.css

    1
    2
    3
    
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    

    然后编写 flask app 应用 app.py,使用 flask-assets 打包生成的 js 和 css文件

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    
    from flask import Flask, render_template, request
    from flask_assets import Environment, Bundle
    
    
    app = Flask(__name__)
    
    assets = Environment(app)
    css = Bundle("src/main.css", output="dist/main.css")
    js = Bundle("src/*.js", output="dist/main.js")
    
    assets.register("css", css)
    assets.register("js", js)
    
    css.build()
    js.build()
    
    
    class User:
        id = 0
        def __init__(self, fname, lname, email ):
            User.id  += 1
            self.id = User.id 
            self.fname = fname
            self.lname = lname 
            self.email = email
    
        def search( self, word ):
            if (word is None):
                return False
            all = self.fname + self.lname + self.email
            return word.lower() in all.lower()
    
    users = [
        User("abe", "vida", "abe@nowhere.com"),
        User("betty", "b", "bb@nowhere.com"),
        User("joe", "robinson", "jrobinson@nowhere.com"),
    
        User("Luis", "Cortes", "Luis@somewhere.com"),
        User("marty", "hinkle", "mhinkle@nowhere.com"),
        User("matthew", "robinson", "mrobinson@nowhere.com"),
    
        User("collin", "western", "cwest@nowhere.com"),
        User("marty", "hinkle II", "mhinkle2@nowhere.com"),
        User("joe", "robinson", "jrobinson@nowhere.com"),
    
        User("juan", "vida", "juanvida@nowhere.com"),
        User("marty", "hinkle III", "mhinkle3@nowhere.com"),
        User("zoe", "omega", "zoe@nowhere.com") 
    ]
    
    
    
    
    @app.route("/")
    def index():
        return render_template("index.html")
    
    @app.route("/search", methods=["POST"])
    def search():
        word = request.form.get("search")
        if (word is None or word == ""):
            return render_template("search.html", users=[])
        else:
            return render_template("search.html", users=filter(lambda u: u.search(word), users))
        
    if __name__ == "__main__":
        app.run(debug=True)
    

    编写模板文件

    templates\base.html

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    <!DOCTYPE html>
    <html lang="en" class="h-full">
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewpoert" content="width=device-width, initial-scale=1.0">
    
        {% assets "css" %}
        <link rel="stylesheet" href="{{ ASSET_URL }}">
        {% endassets %}
    
        {% assets "js" %}
        <script type="text/javascript" src="{{ ASSET_URL }}"></script>
        {% endassets %}
    
        <title>Flask Htmlx daisyUI Demo </title>
    </head>
    
    <body class="h-full">
        {% block content %}
        {% endblock content %}
    </body>
    
    </html>
    

    templates\index.html

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    
    {% extends "base.html" %}
    
    {% block content %}
    <main class="max-w-screen-md mx-auto flex flex-col gap-8">
      <div class="w-full max-w-screen-md mx-auto">
        <header class="w-full flex items-center p-8" hx-boost="true">
          </h1>
          <a href="/" class="btn btn-secondary btn-outline">Home</a>
          </h1>
        </header>
      </div>
      <section>
        <div class="flex flex-col gap-8">
          <section class="hero bg-base-200 px-16 sm:px-32 py-24 sm:rounded-3xl">
            <div class="hero-content text-center">
              <div class="flex flex-col gap-6">
                <h1 class="text-4xl font-bold">
                  Flask Htmlx daisyUI Demo
                </h1>
                <p class="text-lg">
                  This is an simple demo of Flask and it's built in HTTP server app using Htmlx, TailwindCSS and DaisyUI.
                </p>
              </div>
    
            </div>
          </section>
    
          <section class="hero bg-base-200 px-16 sm:px-32 py-24 sm:rounded-3xl">
            <div class="hero-content text-left">
              <div class="flex flex-col gap-6">
                <h1 class="text-4xl font-bold">Search Contacts
                  <span class="htmx-indicator">
                    <img src="{{url_for('static', filename='img/bars.svg')}}" /> Searching...
                  </span>
                </h1>
                <input class="form-control input input-bordered input-md w-full max-w-xs" type="text" name="search"
                  placeholder="Begin Typing To Search Users..." hx-post="/search" hx-trigger="keyup changed delay:250ms"
                  hx-target="#search-results" hx-indicator=".htmx-indicator">
    
                <table class="table table-xs">
                  <thead>
                    <tr>
                      <th>ID
                      </th>
                      <th>First
                        Name</th>
                      <th>Last
                        Name</th>
                      <th>Email
                      </th>
                    </tr>
                  </thead>
    
                  <tbody id="search-results">
                    {% include 'search.html' %}
                  </tbody>
                </table>
              </div>
            </div>
          </section>
    
          <section class="hero bg-base-200 px-16 sm:px-32 py-24 sm:rounded-3xl">
            <div
              class="flex flex-col w-full max-w-md px-4 py-8 bg-white rounded-lg shadow dark:bg-gray-800 sm:px-6 md:px-8 lg:px-10">
              <div class="self-center mb-6 text-xl font-light text-gray-600 sm:text-2xl dark:text-white">
                Login To Your Account
              </div>
              <div class="mt-8">
                <form action="#" autoComplete="off">
                  <div class="flex flex-col mb-2">
                    <div class="flex relative ">
                      <span
                        class="rounded-l-md inline-flex  items-center px-3">
                        <svg width="15" height="15" fill="currentColor" viewBox="0 0 1792 1792"
                          xmlns="http://www.w3.org/2000/svg">
                          <path
                            d="M1792 710v794q0 66-47 113t-113 47h-1472q-66 0-113-47t-47-113v-794q44 49 101 87 362 246 497 345 57 42 92.5 65.5t94.5 48 110 24.5h2q51 0 110-24.5t94.5-48 92.5-65.5q170-123 498-345 57-39 100-87zm0-294q0 79-49 151t-122 123q-376 261-468 325-10 7-42.5 30.5t-54 38-52 32.5-57.5 27-50 9h-2q-23 0-50-9t-57.5-27-52-32.5-54-38-42.5-30.5q-91-64-262-182.5t-205-142.5q-62-42-117-115.5t-55-136.5q0-78 41.5-130t118.5-52h1472q65 0 112.5 47t47.5 113z">
                          </path>
                        </svg>
                      </span>
                      <input type="text" id="sign-in-email"
                        class="input input-bordered input-md w-full max-w-xs"
                        placeholder="Your email" />
                    </div>
                  </div>
                  <div class="flex flex-col mb-6">
                    <div class="flex relative ">
                      <span
                        class="rounded-l-md inline-flex  items-center px-3">
                        <svg width="15" height="15" fill="currentColor" viewBox="0 0 1792 1792"
                          xmlns="http://www.w3.org/2000/svg">
                          <path
                            d="M1376 768q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h32v-320q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45q0-106-75-181t-181-75-181 75-75 181v320h736z">
                          </path>
                        </svg>
                      </span>
                      <input type="password" id="sign-in-email"
                        class="input input-bordered input-md w-full max-w-xs"
                        placeholder="Your password" />
                    </div>
                  </div>
                  <div class="flex items-center mb-6 -mt-4">
                    <div class="flex ml-auto">
                      <a href="#"
                        class="link link-hover">
                        Forgot Your Password?
                      </a>
                    </div>
                  </div>
                  <div class="flex w-full">
                    <button type="submit" class="btn btn-secondary btn-outline">
                      Login
                    </button>
                  </div>
                </form>
              </div>
              <div class="flex items-center justify-center mt-6">
                <a href="#" target="_blank"
                  class="link link-hover">
                  <span class="ml-2">
                    You don&#x27;t have an account?
                  </span>
                </a>
              </div>
            </div>
    
          </section>
        </div>
      </section>
    </main>
    {% endblock content %}
    

    templates\search.html

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    {% if users %}
    {% for user in users %}
    <tr>
        <td>
            {{user.id}}</td>
        <td>
            {{user.fname}}</td>
        <td>
            {{user.lname}}</td>
        <td>
            {{user.email}}</td>
    </tr>
    {% endfor %}
    {% endif %}
    

    使用 Tailwind CSS 生成 CSS

    1
    
    npx tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
    

    运行 app

    1
    
    (venv)$ python app.py
    

    访问 http://127.0.0.1:5000/ 查看效果

    资料

    • Rapid Prototyping with Flask, htmx, and Tailwind CSS
    • htmx.org
    • flask
    • daisyui


沪ICP备19023445号-2号
友情链接