How do I check if many-to-many row already exists in sqlalchemy to prevent duplicate? - flask-sqlalchemy

Here is code for my models:
class IllustratorAsset(db.Model):
id = db.Column(db.Integer, primary_key=True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
uploader = db.Column(db.Integer, db.ForeignKey('user.id'))
project = db.Column(db.Integer, db.ForeignKey('project.id'))
fragments = db.relationship('AssetFragment', backref='ai', lazy='dynamic')
file_hash = db.Column(db.String(128), unique=True)
file_name = db.Column(db.String(128))
def __repr__(self):
return '<Picture {}>'.format(self.id)
tags_association = db.Table(
'tags_association',
db.Column('fragment_id', db.Integer(), db.ForeignKey('asset_fragment.id'),index=True),
db.Column('tag_id', db.Integer(), db.ForeignKey('tag.id'),index=True),
PrimaryKeyConstraint('fragment_id', 'tag_id')
)
class AssetFragment(db.Model):
id = db.Column(db.Integer, primary_key=True)
asset = db.Column(db.Integer, db.ForeignKey('illustrator_asset.id'))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
uploader = db.Column(db.Integer, db.ForeignKey('user.id'))
tags = db.relationship('Tag', secondary=tags_association, backref='fragments', lazy='dynamic')
file_name = db.Column(db.String(128))
file_hash = db.Column(db.String(128))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag = db.Column(db.String(64), index=True, unique=True)
def __repr__(self):
return '<Tag {}>'.format(self.tag)
The idea is illustrations storage, there is Asset (Adobe Illustrator file), it has Fragments (renders of its different fragments), the fragment has some Tags for quick search which should be working through many-to-many relationship tags_association.
Currently, I'm stuck at trying to check if an association already exists, I'm sure there is some simple way to do it, but I'm missing some point. I came up with the code below, and I still get the exception of constraints.
for tag in new_tags: #list of tags from form
t = Tag.query.filter_by(tag=tag).first()
if (t is not None) and not(t in fragment.tags):
fragment.tags.append(t)
else:
new_tag = Tag(tag=tag)
if not(new_tag in fragment.tags):
fragment.tags.append(new_tag)
DB is Postgres

Well answer came shortly after I've tried to do it in flask shell. Shell output
I've realized, that it seems to be something wrong with the comparator, simple override makes it work:
def __eq__(self, __o: object) -> bool:
return self.tag == __o.tag
Also needed to change checking by a bit:
for tag in new_tags:
t = Tag.query.filter_by(tag=tag).first()
if t is None:
new_tag = Tag(tag=tag)
print(new_tag)
if not(new_tag in fragment.tags):
fragment.tags.append(new_tag)
else:
print(t)
if not(t in fragment.tags):
fragment.tags.append(t)

Related

ArgumentError: Mapper mapped class Roles_users->User Roles could not assemble any primary key columns for mapped table 'User Roles'

I'm trying to write a web app with user roles and I keep getting this error no matter the changes I make.
Could you please help me find what's wrong?
Thank you in advance!!
This is my models.py file:
from sqlalchemy import Column, Integer, String, Float, Boolean, ForeignKey
from flask_login import UserMixin
from sqlalchemy.orm import relationship, backref
import db
class Rol(db.Base):
__tablename__ = 'Roles'
id_rol = Column(Integer, primary_key=True)
nombre_rol = Column(String, nullable=False)
user_rol = relationship("User rol", back_populates="rol", uselist =False)
def __init__(self, nombre_rol):
self.nombre_rol = nombre_rol
class User(UserMixin, db.Base):
__tablename__ = 'Users'
user_id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
password = Column(String, unique=True, nullable=False)
is_active = Column(Boolean, default=False)
user_rol = relationship("User rol", back_populates="user", uselist =False)
def __init__(self,username,password,is_active):
self.username = username
self.password = password
self.is_active = is_active
def get_id(self):
return self.id
def is_active(self):
return self.is_active
def activate_user(self):
self.is_active = True
def get_username(self):
return self.username
def get_rol(self):
return self.rol
class Roles_users(db.Base):
__tablename__ = 'User Roles'
user_id = Column(Integer, ForeignKey("Users.user_id"))
id_rol = Column(Integer, ForeignKey("Roles.id_rol"))
user = relationship("User", back_populates="user roles")
rol = relationship("Rol", back_populates="user roles")

Issue when creating on-to-many relationships in SQLAlchemy database

I created a Flask app with a database, with following classes:
db = SQLAlchemy(app)
class Category(db.Model):
__tablename__ = 'Category'
children = relationship("Child")
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Text())
icon = db.Column(db.Text())
subcategories = db.relationship('Subcategory', backref="category")
def __init__(self, name, subcategories, icon):
self.name = name
self.color = icon
self.subcategories = subcategories
class Subcategory(db.Model):
__tablename__ = 'Subcategory'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Text())
color = db.Column(db.Text())
reward_points = db.Column(db.Integer())
category = db.Column(db.Text())
tasks = db.relationship('Task', backref="subcategory")
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
def __init__(self, name, color, reward_points, category, tasks, category_id):
self.name = name
self.color = color
self.reward_points = reward_points
self.category = category
self.tasks = tasks
self.category_id = category_id
class Task(db.Model):
__tablename__ = 'Task'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
task = db.Column(db.Text())
start = db.Column(db.DateTime())
end = db.Column(db.DateTime())
duration = db.Column(db.Integer)
scheduled = db.Column(db.DateTime())
created = db.Column(db.DateTime())
status = db.Column(db.Text())
category = db.Column(db.Text())
subcategory = db.Column(db.Text())
tags = db.Column(db.Text())
subcategory_id = db.Column(db.Integer, db.ForeignKey('subcategory.id'))
def __init__(self, task, start, end, duration, category, subcategory, tags, created, status, scheduled, subcategory_id):
self.task = task
self.start = start
self.end = end
self.duration = duration
self.category = category
self.subcategory = subcategory
self.tags = tags
self.created = created
self.status = status
self.scheduled = scheduled
self.subcategory_id = subcategory_id
My goal is to create one to many relationships between the classes. I am trying to run the db.create_all() command, but am getting following error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'Subcategory.category_id' could not find table 'category' with which to generate a foreign key to target column 'id'
What am I doing wrong? Other questions on Stackoverflow with similar issues did not resolve my error.
Changing
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
to
category_id = db.Column(db.Integer, db.ForeignKey('Category.id'))
solved the issue.
Changing "category.id" to "category.c.id" solved the issue as well. If anyone knows why, pls let me know ;-)

How to add a self increased field by category

I want to set a sub id for my post table. This table already has a id field as primary. But post has categories. I want a sub_id for post in each category as self increased.
I am writing my blog. Post is has PostCategory, PostCategory to Post is one-to-many relationship.
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
# I tried this, but this did not work
sub_id = db.Column(db.Integer, nullable=True, default=int(f'select count(*) from post where category_id={category_id}')+1)
title = db.Column(db.String(120), unique=True, nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
# user in Foreignkey is table name
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('post_category.id'), nullable=False)
def post_content_render_markdown(self):
return markdown(self.content, extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
])
def __repr__(self):
return f"Post('{self.title}', {self.category.name}, '{self.date_posted}')"
class PostCategory(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), unique=True, nullable=False)
description = db.Column(db.Text, nullable=False)
posts = db.relationship('Post', backref='category', lazy=True)
def __repr__(self):
return f"Post Category('{self.id}', '{self.name}')"
error msg
sub_id = db.Column(db.Integer, nullable=True, default=int(f'select count(*) from post where category_id={category_id}')+1)
NameError: name 'category_id' is not defined
There is an another existing argument that can increase your sub_id
https://docs.sqlalchemy.org/en/13/core/defaults.html#python-executed-functions
sub_id = db.Column(db.Integer, nullable=True, autoincrement=True)
Try this

sqlalchemy internal error which seams not like my coding problem because of the same code

I just did the same things as a tutorial, his works, but mine failed. It seams like a sqlalchemy internal error which shows in the terminal.
I dont know what's wrong with my code, what I did before that is to install the flask-sqlalthemy,pymysql and flask in my project interpreter in pycharm community settings.
I use the virtrualenev in this project.
the code below
coding:utf8
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import pymysql
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:root#127.0.0.1:8889/movie"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
db = SQLAlchemy(app)
# user
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True) # id
name = db.Column(db.String(100), unique=True) # name
pwd = db.Column(db.String(100)) # password
email = db.Column(db.String(100), unique=True) # email
phone = db.Column(db.String(11), unique=True) # phone
info = db.Column(db.Text, unique=True) # resume
face = db.Column(db.String(255), unique=True) # icon
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
uuid = db.Column(db.String(255), unique=True) # unique id
userlogs = db.relationship('Userlog', backref='user') # user log foreign key relationship
comments = db.relationship('Comment', backref='user') # comment foreign key relationship
moviecols = db.relationship('Moviecol', backref='user') # movie collection foreign key relationship
def __repr__(self):
return "<User %r>" % self.name
# user log
class Userlog(db.Model):
__tablename__ = "userlog"
id = db.Column(db.Integer, primary_key=True) # id
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # user id
ip = db.Column(db.String(100)) # login ip
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
movies = db.relationship('Movie', backref='tag') # movie tag foreign key relationship
def __repr__(self):
return "<Userlog %r>" % self.id
# Tag
class Tag(db.Model):
__tablename__ = "tag"
id = db.Column(db.Integer, primary_key=True) # id
name = db.Column(db.String(100), unique=True) # title
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Tag %r>" % self.name
# movie
class Movie(db.Model):
__tablename__ = "movie"
id = db.Column(db.Integer, primary_key=True) # id
title = db.Column(db.String(255), unique=True) # title
url = db.Column(db.String(255), unique=True) # url
info = db.Column(db.Text, unique=True) # resume
logo = db.Column(db.String(255), unique=True) # logo
star = db.Column(db.SmallInteger) # star
playnum = db.Column(db.BigInteger) # play number
commentnum = db.Column(db.BigInteger) # comment number
tag_id = db.Column(db.Integer, db.ForeignKey('tag.id')) # tag id
area = db.Column(db.String(255)) # area
release_time = db.Column(db.Date) # release time
length = db.Column(db.String(255)) # length
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
comments = db.relationship('Comment', backref='movie') # comment foreign key relationship
moviecols = db.relationship('Moviecol', backref='movie') # movie collection foreign key relationship
def __repr__(self):
return "<Movie %r>" % self.title
# Preview
class Preview(db.Model):
__tablename__ = "preview"
id = db.Column(db.Integer, primary_key=True) # id
title = db.Column(db.String(255), unique=True) # title
logo = db.Column(db.String(255), unique=True) # logo
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Preview %r>" % self.title
# Comment
class Comment(db.Model):
__tablename__ = "comment"
id = db.Column(db.Integer, primary_key=True) # id
content = db.Column(db.Text) # content
logo = db.Column(db.String(255), unique=True) # logo
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
movie_id = db.Column(db.Integer, db.ForeignKey('movie.id')) # movie id
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # movie id
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Comment %r>" % self.id
# movie collection
class Moviecol(db.Model):
__tablename__ = "moviecol"
id = db.Column(db.Integer, primary_key=True) # id
movie_id = db.Column(db.Integer, db.ForeignKey('movie.id')) # movie id
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # movie id
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Moviecol %r>" % self.id
# Authority
class Auth(db.Model):
__tablename__ = "auth"
id = db.Column(db.Integer, primary_key=True) # id
name = db.Column(db.String(100), unique=True) # title
url = db.Column(db.String(255), unique=True) # movie id
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Auth %r>" % self.name
# Role
class Role(db.Model):
__tablename__ = "role"
id = db.Column(db.Integer, primary_key=True) # id
name = db.Column(db.String(100), unique=True) # title
auths = db.Column(db.String(600)) # movie id
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Role %r>" % self.name
# Admin
class Admin(db.Model):
__tablename__ = "admin"
id = db.Column(db.Integer, primary_key=True) # id
name = db.Column(db.String(100), unique=True) # name
pwd = db.Column(db.String(100)) # password
is_super = db.Column(db.SmallInteger) # whether is super admin, 0 is super admin
role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # role id
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
adminlogs = db.relationship('Adminlog', backref='admin') # admin login log foreign key relationship
oplogs = db.relationship('Oplog', backref='admin') # admin operation log foreign key relationship
def __repr__(self):
return "<Admin %r>" % self.name
# Admin login log
class Adminlog(db.Model):
__tablename__ = "adminlog"
id = db.Column(db.Integer, primary_key=True) # id
admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # admin id
ip = db.Column(db.String(100)) # login ip
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Adminlog %r>" % self.id
# Admin operation log
class Oplog(db.Model):
__tablename__ = "oplog"
id = db.Column(db.Integer, primary_key=True) # id
admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # user id
ip = db.Column(db.String(100)) # login ip
reason = db.Column(db.String(600)) # reason
addtime = db.Column(db.DateTime, index=True, default=datetime.utcnow) # adding time
def __repr__(self):
return "<Oplog %r>" % self.id
if __name__ == "__main__":
db.create_all()
#
# the error below
(venv) D:\Python\movie_project\app>python models.py
# the error below
Traceback (most recent call last):
File "models.py", line 188, in <module>
db.create_all()
File "D:\Python\movie_project\venv\lib\site-packages\flask_sqlalchemy\__init__.py", line 1033, in create_all
self._execute_for_all_tables(app, bind, 'create_all')
File "D:\Python\movie_project\venv\lib\site-packages\flask_sqlalchemy\__init__.py", line 1025, in _execute_for_all_tables
op(bind=self.get_engine(app, bind), **extra)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\sql\schema.py", line 4287, in create_all
ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\engine\base.py", line 2032, in _run_visitor
with self._optional_conn_ctx_manager(connection) as conn:
File "E:\Program Files\Python\lib\contextlib.py", line 112, in __enter__
return next(self.gen)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\engine\base.py", line 2024, in _optional_conn_ctx_manager
with self._contextual_connect() as conn:
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\engine\base.py", line 2226, in _contextual_connect
self._wrap_pool_connect(self.pool.connect, None),
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\engine\base.py", line 2262, in _wrap_pool_connect
return fn()
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\base.py", line 363, in connect
return _ConnectionFairy._checkout(self)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\base.py", line 760, in _checkout
fairy = _ConnectionRecord.checkout(pool)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\base.py", line 492, in checkout
rec = pool._do_get()
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\impl.py", line 139, in _do_get
self._dec_overflow()
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\util\langhelpers.py", line 68, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\util\compat.py", line 129, in reraise
raise value
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\impl.py", line 136, in _do_get
return self._create_connection()
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\base.py", line 308, in _create_connection
return _ConnectionRecord(self)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\base.py", line 437, in __init__
self.__connect(first_connect_check=True)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\pool\base.py", line 639, in __connect
connection = pool._invoke_creator(self)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\engine\strategies.py", line 114, in connect
return dialect.connect(*cargs, **cparams)
File "D:\Python\movie_project\venv\lib\site-packages\sqlalchemy\engine\default.py", line 453, in connect
return self.dbapi.connect(*cargs, **cparams)
File "D:\Python\movie_project\venv\lib\site-packages\pymysql\__init__.py", line 94, in Connect
return Connection(*args, **kwargs)
File "D:\Python\movie_project\venv\lib\site-packages\pymysql\connections.py", line 325, in __init__
self.connect()
File "D:\Python\movie_project\venv\lib\site-packages\pymysql\connections.py", line 599, in connect
self._request_authentication()
File "D:\Python\movie_project\venv\lib\site-packages\pymysql\connections.py", line 882, in _request_authentication
auth_packet = _auth.caching_sha2_password_auth(self, auth_packet)
File "D:\Python\movie_project\venv\lib\site-packages\pymysql\_auth.py", line 264, in caching_sha2_password_auth
data = sha2_rsa_encrypt(conn.password, conn.salt, conn.server_public_key)
File "D:\Python\movie_project\venv\lib\site-packages\pymysql\_auth.py", line 142, in sha2_rsa_encrypt
raise RuntimeError("cryptography is required for sha256_password or caching_sha2_password")
RuntimeError: cryptography is required for sha256_password or caching_sha2_password
I had the same error cryptography is required for sha256_password or caching_sha2_password
it was solved when i installed "cryptography" try:
pip install cryptography
I know a possible solution of this question is installing a previous version of mysql, for examle, v5.7. I think it is better to install a version lower than 8.0.

Sqlalchemy: Propagation of updates across multiple (linked) relationships

I show here an (artificial) example of three linked tables: ParentA, ChildA, and ChildAA. ChildA is related to the primary key (PK) of ParentA via foreign key, and ChildAA relates to the same key in ChildA. In this way ChildAA links to the primary key of the ParentA. I would expect that when I make a change to the ParentA PK this change propagates back to the corresponding ChildAA's attribute, but it doesn't.
Thanks in advance!
(I apologize if this has been answered or documented before, I really couldn't find anything.)
The Code:
from sqlalchemy import *
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class ParentA(Base):
__tablename__ = 'tbl_parentA'
pid = Column(Integer, primary_key=True)
childA = orm.relationship("ChildA", passive_updates=False, backref='parentA')
class ChildA(Base):
__tablename__ = 'tbl_childA'
attrib1 = Column(String, nullable=True)
parentA_id = Column(Integer, ForeignKey(ParentA.pid), primary_key=True)
childAA = orm.relationship("ChildAA", passive_updates=False, backref="childA")
# This class is related to Parents through ChildA
class ChildAA(Base):
__tablename__ = 'tbl_childAA'
cid = Column(Integer, primary_key=True)
attrib1 = Column(String, nullable=True)
parentA_id = Column(Integer, ForeignKey(ChildA.parentA_id))
def clear_db(db):
tmp = db.echo
db.echo = False
metadata = MetaData(bind=db)
metadata.reflect(db)
for table in reversed(metadata.sorted_tables):
table.drop(db)
metadata.clear()
db.echo = tmp
if __name__ == '__main__':
# SQLite Connection
db = create_engine('sqlite:///linked_updates.db')
# db.echo = True
# Initalize Objects
pa1 = ParentA()
ca1 = ChildA(attrib1='ca1 str')
caa1= ChildAA(attrib1='caa1 str')
# Assign a parent to ChildA
ca1.parentA = pa1
# Assign a parent to ChildAA
caa1.childA = ca1
# Initialize clean DB & session
clear_db(db)
Base.metadata.create_all(db)
session = orm.create_session(db)
# Write to DB
session.add_all([pa1, ca1, caa1])
session.flush()
print 'After flush, we have: ', caa1.parentA_id, '==', caa1.childA.parentA_id
# Induce change, check propagation
pa1.pid = 2
session.flush()
print 'I expect: ', caa1.parentA_id, '==', caa1.childA.parentA_id
print 'END'