miguelgrinberg / flasky

Companion code to my O'Reilly book "Flask Web Development", second edition.
MIT License
8.52k stars 4.2k forks source link

flask-bootstrap Customization #526

Closed yehuihe closed 2 years ago

yehuihe commented 2 years ago

Hi all,

I'm really confused of how to customize style use flask-bootstrap. In the book for example forms always use {{ wtf.quick_form(form) }} But I would like to create a more styled form based on plain Bootstrap example https://getbootstrap.com/docs/5.1/examples/sign-in/

After a great effort I still could make my page looks exactly like the ones showed in the example. My looks like

Screenshot from 2021-11-05 18-21-40

Clearly text is not centered. Highlight when i click looks different. Example has effect that when u click the label text shrink.

Here I provided my code

<!--{% extends "base.html" %}-->
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky - Login{% endblock %}

{% block styles %}
{{ super() }}
<!-- Custom styles for this template -->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='signin.css') }}">
{% endblock %}

{% block body_attribs %}class="text-center"{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Login</h1>
</div>
<!--<div class="col-md-4">-->
<main class="form-signin">
    <form class="form" method="post" role="form">
<!--        <img class="mb-4" src="../assets/brand/bootstrap-logo.svg" alt="randori logo" width="72" height="57">-->
        <img class="mb-4" src="{{ url_for('static', filename='brand/bootstrap-logo.svg') }}" alt="" width="72" height="57">
        <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
        {{ form.hidden_tag() }}
        {{ wtf.form_errors(form, hiddens="only") }}
        <div class="form-floating">
<!--            <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">-->
            {{ wtf.form_field(form.email, class="form-control", id="floatingInput", placeholder="name@example.com") }}
        </div>
        <div class="form-floating">
<!--            <input type="password" class="form-control" id="floatingPassword" placeholder="Password">-->
            {{ wtf.form_field(form.password, class="form-control", id="floatingPassword", placeholder="Password") }}
        </div>

        <div class="checkbox mb-3">
<!--            <input type="checkbox" value="remember-me"> Remember me-->
            {{ wtf.form_field(form.remember_me, type="checkbox", class="form-check-input") }}
        </div>
        <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
<!--        <p class="mt-5 mb-3 text-muted">&copy; 2017–2021</p>-->
    </form>
    <br>
    <p>Forgot your password? <a href="{{ url_for('auth.password_reset_request') }}">Click here to reset it</a>.</p>
    <p>New user? <a href="{{ url_for('auth.register') }}">Click here to register</a>.</p>
</main>
<!--</div>-->
{% endblock %}

The following is Bootstrap example source code and it's .css

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
    <meta name="generator" content="Hugo 0.88.1">
    <title>Signin Template · Bootstrap v5.1</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/5.1/examples/sign-in/">

    <!-- Bootstrap core CSS -->
<link href="../assets/dist/css/bootstrap.min.css" rel="stylesheet">

    <style>
      .bd-placeholder-img {
        font-size: 1.125rem;
        text-anchor: middle;
        -webkit-user-select: none;
        -moz-user-select: none;
        user-select: none;
      }

      @media (min-width: 768px) {
        .bd-placeholder-img-lg {
          font-size: 3.5rem;
        }
      }
    </style>

    <!-- Custom styles for this template -->
    <link href="signin.css" rel="stylesheet">
  </head>
  <body class="text-center">

<main class="form-signin">
  <form>
    <img class="mb-4" src="../assets/brand/bootstrap-logo.svg" alt="" width="72" height="57">
    <h1 class="h3 mb-3 fw-normal">Please sign in</h1>

    <div class="form-floating">
      <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">
      <label for="floatingInput">Email address</label>
    </div>
    <div class="form-floating">
      <input type="password" class="form-control" id="floatingPassword" placeholder="Password">
      <label for="floatingPassword">Password</label>
    </div>

    <div class="checkbox mb-3">
      <label>
        <input type="checkbox" value="remember-me"> Remember me
      </label>
    </div>
    <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
    <p class="mt-5 mb-3 text-muted">&copy; 2017–2021</p>
  </form>
</main>

  </body>
</html>

I use the same .css file

html,
body {
  height: 100%;
}

body {
  display: flex;
  align-items: center;
  padding-top: 40px;
  padding-bottom: 40px;
  background-color: #f5f5f5;
}

.form-signin {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: auto;
}

.form-signin .checkbox {
  font-weight: 400;
}

.form-signin .form-floating:focus-within {
  z-index: 2;
}

.form-signin input[type="email"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}

.form-signin input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

I was trying to mimicking his file then translate to jinja2 and flask-bootstrap way of writing. But style still looks a lot like quick_form

Can someone pointing out what i did wrong? I'm really struck on this. Thank you,

yehuihe commented 2 years ago

Oh also I do not want the navbar in signin page. That's why i commented out {% extends "base.html" %}.

miguelgrinberg commented 2 years ago

First of all, you can't remove base.html. If you don't want to use the base layout from the application, then you have to extend bootstrap/base.html instead. This adds the bootstrap CSS and JS files to the page.

Next, you are looking at a bootstrap 5 example, while Flask-Bootstrap works with Bootstrap 3. You will find some differences there, so you should either look for Bootstrap 3 examples to copy from, or else remove Flask-Bootstrap and code your HTML directly, without using any Flask-Bootstrap helpers.

yehuihe commented 2 years ago

Wow thank you. I didn't know that. I find directly code in plain Bootstrap way easier.

I still have question about forms tho. How to connect LoginForm in app/auth/forms in plain HTML? if I use flask-bootstrap I can put for example

        <div class="form-floating">
            {{ wtf.form_field(form.email, class="form-control", id="floatingInput", placeholder="name@example.com") }}
        </div>

So form.email is the field. But in plain HTML from example the corresponding code is

    <div class="form-floating">
      <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">
      <label for="floatingInput">Email address</label>
    </div>

Seems no way to link form.email anywhere

miguelgrinberg commented 2 years ago

You have to set the name attribute in your <input> field, matching the field name in the form class.

yehuihe commented 2 years ago

Hi,

I reopened the issue cause I fixed appearance but still stuck. I'm not using flask-bootstrap currently. Just linking bootstrap css and js in the base.html.

the form i use in login.html

    <form>
        {{ form.hidden_tag() }}
        <img class="mb-4" src="{{ url_for('static', filename='brand/bootstrap-logo.svg') }}" alt="" width="72" height="57">
        <h1 class="h3 mb-3 fw-normal">Please sign in</h1>

        <div class="form-floating">
            <input type="email" name="email" class="form-control" id="floatingInput" placeholder="name@example.com">
            <label for="floatingInput">Email address</label>
        </div>
        <div class="form-floating">
            <input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password">
            <label for="floatingPassword">Password</label>
        </div>

        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" name="remember_me"> Remember me
            </label>
        </div>
        <button class="w-100 btn btn-lg btn-primary" type="submit" name="submit">Sign in</button>
        <p class="mt-5 mb-3 text-muted">&copy; 2017–2021</p>
    </form>

it seems keep issuing GET request with:

127.0.0.1 - - [14/Nov/2021 19:08:46] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [14/Nov/2021 19:08:47] "GET /auth/login HTTP/1.1" 200 - _127.0.0.1 - - [14/Nov/2021 19:08:57] "GET /auth/login?csrftoken=Ijg3NGNjODY4OGYyZDkxZDljNWFjOGNmYWFiZTIzN2Y4NmYxYWY2MGQi.FHLiLw.8AW74HH3eGqNzPDjMSuxYs-OAxs&email=yehui.he%40gmail.com&password=IXLIM8389&submit= HTTP/1.1" 200 -

So it's not really doing anything after hitting submit button. It seems didn't submit.

I do not understand why it isn't issuing PUT as what it suppose to. the forms and views are the same as your book. Just a reminder for the views

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data.lower()).first()
        if user is not None and user.verify_password(form.password.data):
            login_user(user, form.remember_me.data)
            next = request.args.get('next')
            if next is None or not next.startswith('/'):
                next = url_for('main.index')
            return redirect(next)
        flash('Invalid email or password.')
    return render_template('auth/login.html', form=form)

Same as in flask book

miguelgrinberg commented 2 years ago

@yehuihe why do you say that it is supposed to issue PUT requests? That doesn't really make sense, as PUT isn't even an option, there's only GET and POST for web forms. If you want to issue a POST request, make your form <form method="POST">. Without the method attribute, the form is going to submit as GET, which is the default.

yehuihe commented 2 years ago

@miguelgrinberg Finally I got it working. Thank you.

Could u offer a last tip on how to make navbar inherited from base.html disappear? Sometimes I would like a clean page without some component from base

miguelgrinberg commented 2 years ago

You can put the nav bar in a block, and then override the block in the derived template with something else, or as an empty block if you just want to skip the nav bar altogether.

yehuihe commented 2 years ago

Great! It worked. Thank you!