26

I just started on sqlalchemy and I want to put a check constraint on one of my columns. I have a column called startTime and endTime and I want to ensure that endTime > startTime.

from sqlalchemy import Column, Integer, String, ForeignKey, Date
import models.Base

class Session(Base):
    __tablename__ = 'sessions'

    sid = Column(Integer, primary_key=True)
    uid = Column(Integer, ForeignKey('users.uid'), nullable=False)
    startTime= Column(Date, nullable=False)

    #probably won't work
    endTime = Column(Date, CheckConstraint('endTime > startTime'), nullable=False)
1
  • Have you tried this? What error do you get? Commented Feb 25, 2016 at 2:50

3 Answers 3

24

Apart from the fact that MySQL doesn't support check constraints, I think the issue is that you are trying to refer to multiple columns in a column level check constraint.

Assuming that you used another database, you need to define the constraint at the table level, something like this:

from sqlalchemy import Column, Integer, String, ForeignKey, Date, CheckConstraint
import models.Base

class Session(Base):
    __tablename__ = 'sessions'
    __table_args__ = (
        CheckConstraint('endTime > startTime'),
    )

    sid = Column(Integer, primary_key=True)
    uid = Column(Integer, ForeignKey('users.uid'), nullable=False)
    startTime= Column(Date, nullable=False)
    
    endTime = Column(Date, nullable=False)
Sign up to request clarification or add additional context in comments.

Comments

14

It's a valid syntax, but in MySQL (I assume you're using MySQL?) this will be ignored. From SQLAlchemy docs:

Check constraints can be named or unnamed and can be created at the Column or Table level, using the CheckConstraint construct. The text of the check constraint is passed directly through to the database, so there is limited “database independent” behavior. Column level check constraints generally should only refer to the column to which they are placed, while table level constraints can refer to any columns in the table. Note that some databases do not actively support check constraints such as MySQL.*

You can create a trigger of course, but then you'd put your biz logic to the DB layer. I'd write an app-level constructor check instead.

3 Comments

Yes, I am using MySQL. That probably explains why. I will use an app level constructor check.
What is an 'app level constructor check'?
MySQL supports "check constraints" now: w3schools.com/sql/sql_check.asp
1

An alternative way to create a check constraint is to use the event.listen event API from SQLAlchemy.

In my use case, I wanted to create a column check on my model where the column couldn't be a negative number.

event.listen(target, identifier, func)

The target in this case is my model ("Post") and my column ("upvotes"). The identifier in this case is "set". The function in this case is a static method created within my model. So it would look like this:

event.listen(Post.upvotes, "set", Post.before_set_upvotes)

My function is as follows:

@staticmethod
def before_set_upvotes(target, value, oldvalue, initiator):
    if value < 0:
        raise ValueError('Upvotes cannot be a negative value')

The target argument is "Post.upvotes" and value is the value we are setting the column attribute to be. NOTE: The function that is used in event.listen requires 4 arguments, I am unsure why so if someone has a reason please comment. Without 4 arguments (and just having target and value as args), I get the error TypeError: before_set_upvotes() takes 2 positional arguments but 4 were given.

Now you can test this by running a function that tried to set and commit a negative number to that column. A value error will be raised, thus creating the constraint we need.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.