mirror of
https://github.com/StefBuwalda/cal_counter.git
synced 2025-10-29 10:50:00 +00:00
Initial commit: Flask calorie counter app setup
Add base Flask application with user authentication, SQLAlchemy models for users, units, and food items, admin blueprint, and basic templates. Includes database migration setup, login form, and seed script for initial user creation.
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
# cal_counter
|
# cal_counter
|
||||||
Calorie Counter Webapp
|
Calorie Counter Webapp
|
||||||
|
|
||||||
|
Bello -Iman
|
||||||
|
|||||||
65
app.py
Normal file
65
app.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
from flask import render_template, redirect, url_for
|
||||||
|
from flask_login import (
|
||||||
|
login_required,
|
||||||
|
logout_user,
|
||||||
|
login_user,
|
||||||
|
current_user,
|
||||||
|
)
|
||||||
|
from forms import LoginForm
|
||||||
|
from models import User
|
||||||
|
from application import db, app, login_manager
|
||||||
|
|
||||||
|
# Config
|
||||||
|
app.config["SECRET_KEY"] = "Iman"
|
||||||
|
|
||||||
|
login_manager.login_view = "login" # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@login_manager.user_loader # type: ignore
|
||||||
|
def load_user(user_id: int):
|
||||||
|
return db.session.get(User, user_id)
|
||||||
|
|
||||||
|
|
||||||
|
# Routes
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
return render_template("index.html")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/login", methods=["GET", "POST"])
|
||||||
|
def login():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for("dashboard"))
|
||||||
|
|
||||||
|
form = LoginForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
user = User.query.filter_by(username=form.username.data).first()
|
||||||
|
if user and user.check_password(password=form.password.data):
|
||||||
|
# User found and password correct
|
||||||
|
login_user(user)
|
||||||
|
return redirect(url_for("dashboard"))
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# invalid user
|
||||||
|
return render_template("login.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/dashboard")
|
||||||
|
@login_required
|
||||||
|
def dashboard():
|
||||||
|
return render_template("dashboard.html", name=current_user.username)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/logout")
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
|
||||||
|
# Run
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True)
|
||||||
18
application/__init__.py
Normal file
18
application/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from flask import Flask
|
||||||
|
from flask_login import LoginManager # type: ignore
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_migrate import Migrate
|
||||||
|
from application.admin.routes import admin_bp
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__) # Init Flask app
|
||||||
|
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data.db"
|
||||||
|
|
||||||
|
|
||||||
|
db = SQLAlchemy(app=app) # Init SQLAlchemy
|
||||||
|
migrate = Migrate(app=app, db=db) # Init Migration
|
||||||
|
|
||||||
|
login_manager = LoginManager(app=app) # Init login manager
|
||||||
|
|
||||||
|
# Register blueprints
|
||||||
|
app.register_blueprint(admin_bp)
|
||||||
13
application/admin/routes.py
Normal file
13
application/admin/routes.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
|
admin_bp = Blueprint(
|
||||||
|
"admin",
|
||||||
|
__name__,
|
||||||
|
url_prefix="/admin",
|
||||||
|
template_folder="templates",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin_bp.route("/food_items", methods=["GET"])
|
||||||
|
def food_items():
|
||||||
|
return render_template("food_items.html")
|
||||||
5
application/admin/templates/food_items.html
Normal file
5
application/admin/templates/food_items.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
Hallo
|
||||||
|
{% endblock%}
|
||||||
58
application/templates/base.html
Normal file
58
application/templates/base.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% block title %}My Flask App{% endblock %}</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap 5 CDN -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="{{ url_for('index') }}">Iman was here</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||||
|
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('dashboard') }}">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
{% for message in messages %}
|
||||||
|
<p>{{ message }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
5
application/templates/dashboard.html
Normal file
5
application/templates/dashboard.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
Hallo
|
||||||
|
{% endblock%}
|
||||||
5
application/templates/index.html
Normal file
5
application/templates/index.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
Index page idk
|
||||||
|
{% endblock%}
|
||||||
10
application/templates/login.html
Normal file
10
application/templates/login.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{{form.hidden_tag()}}
|
||||||
|
{{form.username()}}
|
||||||
|
{{form.password()}}
|
||||||
|
{{form.submit()}}
|
||||||
|
</form>
|
||||||
|
{% endblock%}
|
||||||
9
forms.py
Normal file
9
forms.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, PasswordField, SubmitField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
username = StringField("Username", validators=[DataRequired()])
|
||||||
|
password = PasswordField("Password", validators=[DataRequired()])
|
||||||
|
submit = SubmitField("Login")
|
||||||
1
migrations/README
Normal file
1
migrations/README
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Single-database configuration for Flask.
|
||||||
50
migrations/alembic.ini
Normal file
50
migrations/alembic.ini
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic,flask_migrate
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[logger_flask_migrate]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = flask_migrate
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
||||||
113
migrations/env.py
Normal file
113
migrations/env.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import logging
|
||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from alembic import context
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
fileConfig(config.config_file_name)
|
||||||
|
logger = logging.getLogger('alembic.env')
|
||||||
|
|
||||||
|
|
||||||
|
def get_engine():
|
||||||
|
try:
|
||||||
|
# this works with Flask-SQLAlchemy<3 and Alchemical
|
||||||
|
return current_app.extensions['migrate'].db.get_engine()
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
# this works with Flask-SQLAlchemy>=3
|
||||||
|
return current_app.extensions['migrate'].db.engine
|
||||||
|
|
||||||
|
|
||||||
|
def get_engine_url():
|
||||||
|
try:
|
||||||
|
return get_engine().url.render_as_string(hide_password=False).replace(
|
||||||
|
'%', '%%')
|
||||||
|
except AttributeError:
|
||||||
|
return str(get_engine().url).replace('%', '%%')
|
||||||
|
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
config.set_main_option('sqlalchemy.url', get_engine_url())
|
||||||
|
target_db = current_app.extensions['migrate'].db
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def get_metadata():
|
||||||
|
if hasattr(target_db, 'metadatas'):
|
||||||
|
return target_db.metadatas[None]
|
||||||
|
return target_db.metadata
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(
|
||||||
|
url=url, target_metadata=get_metadata(), literal_binds=True
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# this callback is used to prevent an auto-migration from being generated
|
||||||
|
# when there are no changes to the schema
|
||||||
|
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||||
|
def process_revision_directives(context, revision, directives):
|
||||||
|
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||||
|
script = directives[0]
|
||||||
|
if script.upgrade_ops.is_empty():
|
||||||
|
directives[:] = []
|
||||||
|
logger.info('No changes in schema detected.')
|
||||||
|
|
||||||
|
conf_args = current_app.extensions['migrate'].configure_args
|
||||||
|
if conf_args.get("process_revision_directives") is None:
|
||||||
|
conf_args["process_revision_directives"] = process_revision_directives
|
||||||
|
|
||||||
|
connectable = get_engine()
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection,
|
||||||
|
target_metadata=get_metadata(),
|
||||||
|
**conf_args
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
||||||
24
migrations/script.py.mako
Normal file
24
migrations/script.py.mako
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
branch_labels = ${repr(branch_labels)}
|
||||||
|
depends_on = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
${downgrades if downgrades else "pass"}
|
||||||
56
migrations/versions/319d293f3017_.py
Normal file
56
migrations/versions/319d293f3017_.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 319d293f3017
|
||||||
|
Revises: aab8050e9c73
|
||||||
|
Create Date: 2025-06-26 10:35:18.540813
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '319d293f3017'
|
||||||
|
down_revision = 'aab8050e9c73'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('unit',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('symbol', sa.String(length=10), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=50), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name'),
|
||||||
|
sa.UniqueConstraint('symbol')
|
||||||
|
)
|
||||||
|
op.create_table('food_item',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=150), nullable=False),
|
||||||
|
sa.Column('amount', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('unit_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('energy', sa.Float(), nullable=True),
|
||||||
|
sa.Column('protein', sa.Float(), nullable=True),
|
||||||
|
sa.Column('carbs', sa.Float(), nullable=True),
|
||||||
|
sa.Column('sugar', sa.Float(), nullable=True),
|
||||||
|
sa.Column('fats', sa.Float(), nullable=True),
|
||||||
|
sa.Column('saturated_fats', sa.Float(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['unit_id'], ['unit.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.drop_table('units')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('units',
|
||||||
|
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.drop_table('food_item')
|
||||||
|
op.drop_table('unit')
|
||||||
|
# ### end Alembic commands ###
|
||||||
39
migrations/versions/aab8050e9c73_.py
Normal file
39
migrations/versions/aab8050e9c73_.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: aab8050e9c73
|
||||||
|
Revises:
|
||||||
|
Create Date: 2025-06-26 10:24:25.760737
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'aab8050e9c73'
|
||||||
|
down_revision = None
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('units',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('user',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('username', sa.String(length=150), nullable=False),
|
||||||
|
sa.Column('password', sa.String(length=150), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('username')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('user')
|
||||||
|
op.drop_table('units')
|
||||||
|
# ### end Alembic commands ###
|
||||||
45
models.py
Normal file
45
models.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from flask_login import UserMixin # type: ignore
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
from application import db
|
||||||
|
|
||||||
|
|
||||||
|
class User(UserMixin, db.Model):
|
||||||
|
__tablename__ = "user"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
username = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
|
password = db.Column(db.String, nullable=False)
|
||||||
|
|
||||||
|
def __init__(self, username: str, password: str):
|
||||||
|
super().__init__()
|
||||||
|
self.username = username
|
||||||
|
self.password = generate_password_hash(password=password)
|
||||||
|
|
||||||
|
def check_password(self, password: str) -> bool:
|
||||||
|
return check_password_hash(pwhash=self.password, password=password)
|
||||||
|
|
||||||
|
def change_password(self, password: str) -> None:
|
||||||
|
self.password = generate_password_hash(password=password)
|
||||||
|
|
||||||
|
|
||||||
|
class Units(db.Model):
|
||||||
|
__tablename__ = "unit"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
symbol = db.Column(db.String(10), unique=True, nullable=False)
|
||||||
|
name = db.Column(db.String(50), unique=True, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class FoodItems(db.Model):
|
||||||
|
__tablename__ = "food_item"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
|
amount = db.Column(db.Integer, nullable=False)
|
||||||
|
|
||||||
|
unit_id = db.Column(db.Integer, db.ForeignKey("unit.id"), nullable=False)
|
||||||
|
unit = db.relationship("Units")
|
||||||
|
|
||||||
|
energy = db.Column(db.Float)
|
||||||
|
protein = db.Column(db.Float)
|
||||||
|
carbs = db.Column(db.Float)
|
||||||
|
sugar = db.Column(db.Float)
|
||||||
|
fats = db.Column(db.Float)
|
||||||
|
saturated_fats = db.Column(db.Float)
|
||||||
6
pyrightconfig.json
Normal file
6
pyrightconfig.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"exclude": [
|
||||||
|
"migrations",
|
||||||
|
".venv",
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user