Reputation: 343
I have 3 sqlalchemy models setup which are all one to many relationships. The User model contains many Task models and the Task model contains many Subtask models. When I execute the test_script.py I get the error sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.NoneType' is not mapped
.
I have read and tried a few different relationships on each module. My goal is to be able to have lists in the User and Task models containing their correct children. I would eventually like to access list items such as user_x.task[3].subtask[1]
Here is the code, (models, script, error)
models
class User(Base):
""" User Model for storing user related details """
__tablename__ = 'Users'
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
username = Column(String(255), nullable=False)
password_hash = Column(String(128), nullable=False)
email = Column(String(255), nullable=False, unique=True)
created_date = Column(DateTime, default=datetime.utcnow)
tasks = relationship(Task, backref=backref("Users", uselist=False))
def __init__(self, username: str, password_hash: str, email: str):
self.username = username
self.password_hash = password_hash
self.email = email
class Task(Base):
""" Task Model for storing task related details """
__tablename__ = 'Tasks'
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
title = Column(String(255), nullable=False)
folder_name = Column(String(255), nullable=True)
due_date = Column(DateTime, nullable=True)
starred = Column(Boolean, default=False)
completed = Column(Boolean, default=False)
note = Column(String, nullable=True)
created_date = Column(DateTime, default=datetime.utcnow)
user_id = Column(Integer, ForeignKey('Users.id'))
subtasks = relationship(Subtask, backref=backref("Tasks", uselist=False))
def __init__(self, title: str, **kwargs):
self.title = title
self.folder_name = kwargs.get('folder_name', None)
self.due_date = kwargs.get('due_date', None)
self.starred = kwargs.get('starred', False)
self.completed = kwargs.get('completed', False)
self.note = kwargs.get('note', None)
class Subtask(Base):
""" Subtask Model for storing subtask related details """
__tablename__ = 'Subtasks'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
title = Column(String(255), nullable=False)
completed = Column(Boolean, default=False)
task_id = Column(Integer, ForeignKey('Tasks.id'))
def __init__(self, title: str, **kwargs):
self.title = title
self.completed = kwargs.get('completed', False)
test_script.py
session1 = create_session()
user1 = User(
username="Stephen",
password_hash="p-hash",
email="[email protected]"
).tasks.append(
Task(
title='Delete Me',
folder_name='Folder 1'
).subtasks.append(
Subtask(
title='Delete Me 2'
)
)
)
session1.add(user1)
session1.commit()
session1.close()
error message
/Users/StephenCollins/Respositories/pycharm_workspace/Personal/BusyAPI_v1/venv/bin/python /Users/StephenCollins/Respositories/pycharm_workspace/Personal/BusyAPI_v1/tests/test_script.py
Traceback (most recent call last):
File "/Users/StephenCollins/Respositories/pycharm_workspace/Personal/BusyAPI_v1/venv/lib/python3.7/site-packages/sqlalchemy/orm/session.py", line 1943, in add
state = attributes.instance_state(instance)
AttributeError: 'NoneType' object has no attribute '_sa_instance_state'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/StephenCollins/Respositories/pycharm_workspace/Personal/BusyAPI_v1/tests/test_script.py", line 24, in <module>
session1.add(user1)
File "/Users/StephenCollins/Respositories/pycharm_workspace/Personal/BusyAPI_v1/venv/lib/python3.7/site-packages/sqlalchemy/orm/session.py", line 1945, in add
raise exc.UnmappedInstanceError(instance)
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.NoneType' is not mapped
Hopefully I am close but if I am completely in the wrong direction just let me know. I used Hibernate a little bit in my prior internship but I am self taught (the best kind of taught) in python, sqlalchemy, and mysql.
Upvotes: 9
Views: 57921
Reputation: 3283
This typically happens in delete/add operations in sqlalchemy where all items are deleted at the same time. The way to solve it is to add/delete with a loop:
To delete multiple elements do
items_2_delete = postgres_db.query(FilterType).all()
for item in items_2_delete:
session_db.delete(item)
session_db.commit()
, instead of
postgres_db.query(FilterType).delete()
Similarly, to add multiple elements do:
items = ['item1', 'item2']
for item in items:
session_db.add(item)
session_db.commit()
instead of
session_db.add(items)
session_db.commit()
Upvotes: 2
Reputation: 52929
In Python methods that mutate an object in-place usually return None
. Your assignment's expression chains creating the model object, accessing a relationship attribute, and appending to said attribute, which returns None
. Split the operations:
user1 = User(
username="Stephen",
password_hash="p-hash",
email="[email protected]"
)
task1 = Task(
title='Delete Me',
folder_name='Folder 1'
)
task1.subtasks.append(
Subtask(
title='Delete Me 2'
)
)
user1.tasks.append(task1)
Upvotes: 10
Reputation: 51904
since you're overloading __init__
it's necessary to initialize the superclass in your models:
class User(Base):
def __init__(self, username: str, password_hash: str, email: str):
// Initialize self
[...]
// Initialize base class
super(User, self).__init__()
Note that it's not generally necessary to define your own constructor __init__
method because the declarative base class already defines a constructor that takes a **kwargs
corresponding to the model properties.
Upvotes: 0