datetimeflasksqlalchemy

How to add days to DateTime object in Flask SQLAlchemy


I have this model Problem with a DateTime object: the current date. I want to add 3 more attributes, "reviewDate1," "reviewDate2," and "reviewDate3." I want them to be spaced out at 1 day after today, 3 days after, and 7 days after.

models.py:

from datetime import timedelta
from flask_sqlalchemy import SQLAlchemy
from uuid import uuid4
from sqlalchemy import DateTime, func, text

db = SQLAlchemy()
def get_uuid():
    return uuid4().hex

class Problem(db.Model):
    id = db.Column(db.String, primary_key=True, unique=True, default=get_uuid)
    problemName = db.Column(db.String(200), nullable=False, unique=True)
    url = db.Column(db.String, nullable=False)
    date = db.Column(db.DateTime, default=func.current_date())
    reviewDate1 = db.Column(db.DateTime, default=func.current_date() + timedelta(days=1))

    def to_json(self):
        return {
            "id": self.id,
            "problemName": self.problemName,
            "url": self.url,
            "date": self.date,
            "reviewDate1": self.reviewDate1
        }

app.py:

@app.route("/create", methods=["POST"])
def create():
    problemName = request.json["problemName"]
    url = request.json["url"]

    # Check if problem already exists
    problem_exists = Problem.query.filter_by(problemName=problemName).first() is not None
    if problem_exists:
        return jsonify({"error": "Problem already exists."}), 409

    # Add new problem
    new_problem = Problem(problemName=problemName, url=url)
    db.session.add(new_problem)
    db.session.commit()
    return jsonify({
        "problemName": problemName,
        "url": url,
        "date": new_problem.date,
        "review1": new_problem.reviewDate1
    }), 201

I tried using timedelta but for some reason I get an error "TypeError: fromisoformat: argument must be str" when I try to create a problem: { "problemName": "a", "url": "a" }.

I am fairly new to Flask-SQLAlchemy so can someone explain what I am doing wrong and if there is any other way I can go about this?


Solution

  • default=func.current_date() + timedelta(days=1) is mixing a server-side function (func.current_date()) with a client-side timedelta(), and the database is getting confused. engine.echo = True shows that the statement is

    INSERT INTO problem (id, "problemName", url, date, "reviewDate1")
    VALUES (?, ?, ?, CURRENT_DATE, (CURRENT_DATE + ?)) RETURNING date, "reviewDate1"
    

    and the parameter values are

    ('f14790ef25004685b3b20199147dfe79', 'a', 'a', '1970-01-02 00:00:00.000000')
    

    Your default should either be all server-side or all client-side. I would suggest using the following (client-side) for consistency:

    class Problem(db.Model):
        id = db.Column(
            db.String, primary_key=True, unique=True, default=lambda: uuid4().hex
        )  # no separate get_uuid() function required
        problemName = db.Column(db.String(200), nullable=False, unique=True)
        url = db.Column(db.String, nullable=False)
        date = db.Column(
            db.DateTime, default=datetime.now().date
        )  # note .date not .date()
        reviewDate1 = db.Column(
            db.DateTime, default=lambda: datetime.now().date() + timedelta(days=1)
        )
    

    which generates

    INSERT INTO problem (id, "problemName", url, date, "reviewDate1") VALUES (?, ?, ?, ?, ?)
    [generated in 0.00127s] ('e17a781a468f4aa7b0bfea02dbe93d4d', 'a', 'a', '2024-06-16 00:00:00.000000', '2024-06-17 00:00:00.000000')