Sqlalchemy get data from relationship - python-3.x

I have this models
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
content = Column(Text)
author = Column(Integer, ForeignKey('users.id'))
to_topic = Column(Integer, ForeignKey('topics.id'))
def __init__(self, content: str, author: int, to_topic: int) -> None:
self.content = content
self.author = author
self.to_topic = to_topic
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(30))
email = Column(String(40))
password = Column(String(255))
registred_at = Column(DateTime, default=datetime.datetime.now)
last_logged_in = Column(DateTime)
last_login_attempt = Column(DateTime)
avatar = Column(Integer, ForeignKey('files.id'))
role = Column(String(20))
email_confirm = Column(Boolean, default=False)
children_forum = relationship('Forum')
children_topic = relationship('Topic')
children_post = relationship('Post')
And I am trying to get query that will contain Post.content, Post.author.username but how do i do this in sessions?
I tried
posts = db_session.query(Post, User).filter(
Post.to_topic == topic_id).with_entities(Post.content, Post.author.username)
but that doesnt work because author is just integer (id) so I expect that I need somehow get the author object and post object in one query, but I dont know how. In sql that would be easy just 2 queries but here I dont know how it´s done.

query_results = db_session.query(Post, User).\
join(User, Post.author == User.id).\
filter(
Post.to_topic == topic_id
).all()
I've never used with_entities, but I know this would give you a list of tuples where query_results[0] would be your Post instance, and query_results[1] would be your User instance.
EDIT: I believe you don't have to include the Post.author == User.id bit, but it's more legible if you're explicit with your joins.

Related

upload image for a blog post with flask-sqlalchemy

I'm trying to create a form to enable users to add blog posts with images using flask and I saved the name of the picture in the database using SQLAlchemy like this "<FileStorage: 'IMG_20210415_114702.jpg' ('image/jpeg')>" as the name of the picture instead of this 'IMG_20210415_114702.jpg'. Please how do I correct this?
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255))
content = db.Column(db.Text)
author = db.Column(db.String(255))
date_posted = db.Column(db.DateTime, default=datetime.utcnow)
slug = db.Column(db.String(255))
poster_id = db.Column(db.Integer, db.ForeignKey("users.id"))
post_pic = db.Column(db.String(200), nullable=True)
#app.route("/add-post", methods=["GET", "POST"])
##login_required
def add_post():
form = PostForm()
if form.validate_on_submit():
poster = current_user.id
post = Post(title=form.title.data, content=form.content.data, poster_id=poster, slug=form.slug.data, post_pic=form.post_pic.data)
form.title.data = ''
form.content.data = ''
#form.author.data = ''
form.slug.data = ''
name = ''
form.post_pic.data = request.files['post_pic']
name = form.post_pic.data.filename
filepath = os.path.join(app.config['UPLOAD_FOLDER'], name)
form.post_pic.data.save(filepath)
#postpic_filename = secure_filename(form.post_pic.data.filename)
#postpic_name = str(uuid.uuid1()) + "_" + postpic_filename
#saver = request.files['post_pic']
#form.post_pic.data = postpic_name
db.session.add(post)
db.session.commit()
#saver.save(os.path.join(app.config['UPLOAD_FOLDER'], postpic_name))
flash("post submitted successfully!")
return render_template("add_post.html", form=form)

How to create an interface table in sqlAlchemy (relation many-to-many)

I'm using SqlAlchemy to interact with an existing Mariadb database. I need to access data organized in a many-to-many relationship. According to the source site documentation (sqlAlchemy), I created a multi-to-multiple relationship, but after entering the flask db migrate command, I get an error.
It should be noted that the tables of posts and categories have been created.
Documents for creating the interface table:
from sqlalchemy import Column, String, Text, Integer, Table, ForeignKey
from app import db
posts_categories = Table('posts_categories', db.metadata,
Column('post_id', Integer, ForeignKey('posts.id', ondelete='cascade')),
Column('category_id', Integer, ForeignKey('categories.id', ondelete='cascade'))
)
class Category(db.Model):
__tablename = 'categories'
id = Column(Integer, primary_key=True)
name = Column(String(128), nullable=False, unique=True)
description = Column(String(256), nullable=True, unique=False)
slug = Column(String(128), nullable=False, unique=True)
posts = db.relationship('Post', secondary=posts_categories, back_populates='categories')
class Post(db.Model):
__tablename = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(128), nullable=False, unique=True)
summary = Column(String(256), nullable=True, unique=False)
content = Column(Text, nullable=False, unique=False)
slug = Column(String(128), nullable=False, unique=True)
categories = db.relationship('Category', secondary=posts_categories, back_populates='posts')

sqlalchemy.exc.ArgumentError while running migrations

I am facing an Argument error while trying to run migrations, but I have failed to figure out where the problem is, What am trying to implement is a relationship between two models. Below is my model class :
class TourPackages(db.Model):
__tablename__ = 'tour_package'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
description = db.Column(db.TEXT)
price = db.Column(db.Float)
destination = db.relationship('Destinations', backref='tourpackages', lazy=True)
capacity = db.Column(db.Integer)
#property
def serialize(self):
return {
'name': self.name,
'description': self.destination,
'price': self.price,
'destination': self.destination,
'capacity': self.capacity
}
class Destinations(db.Model):
__tablename__ = 'destination'
id = db.Column(db.Integer)
location = db.Column(db.String(50))
danger_type = db.Column(db.String(50))
When I run migrations with the command below:
flask db migrate -m "Initial migration."
I get this error :
sqlalchemy.exc.ArgumentError: Mapper mapped class Destinations->destination could not assemble any primary key columns for mapped table 'destination'
What am I doing wrong here
In your Destinations model, you need to change the id column to be a primary key
id = db.Column(db.Integer, primary_key=True)
and add a foreign key column for the relationship, again in Destinations:
tourpackages_id = db.Column(db.Integer, db.ForeignKey('tour_package.id'))
See the documentation.

How to test a function that uses db and returns boolean depending on the parameters

I made a function to check if a specific user has permissions to make some action on a resource.
If the user has super permissions has_all_actions, has_all_resources it can make any action on any resource.
I don't know if i implemented the function well, but with some manual tests i made it's working.
def check_if_user_has_permissions(user= None, resource = None, action = None):
tmp = {}
tmp["user"] = user
tmp["resource"] = resource
tmp["action"] = action
data = []
has_permission = db.session.query(RoleMember.user_uid, Permission.resource, Permission.action). \
filter_by(user_uid=user). \
join(Permission, Permission.role == RoleMember.role).all()
if resource and action is not None:
if has_permission == "":
return False
else:
for info in has_permission:
for item in info:
data.append(item)
has_all_actions = "*"
has_all_resources = "machines/*"
if tmp["user"] in data and data[1] == has_all_resources and data[2] == has_all_actions:
return True
else:
if tmp["user"] in data and tmp["resource"] in data and tmp["action"] in data:
return True
else:
return False
else:
return False
This functions returns true of false depending if the user has permissions or not and returns always true if the user has super permissions: has_all_actions, has_all_resources.
My database is SqlAlchemy but at the moment I'am using this SQLALCHEMY_DATABASE_URI: "sqlite://" so i don't have real data stored in the db.
Now i want to test this function with #pytest.mark.parametrize, so I pass different parameters to the actual function and assert that is false or true depending on the data that is on the database.
So i think i also need to mock the database and add some data there so i can do this test? or is there a way without mocking the db?
Any examples i can follow? I'am lost in this.
Forgot to show my models:
class Role(db.Model):
__tablename__ = "roles"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), unique=True)
description = db.Column(db.String(1000))
permissions = db.relationship("Permission", lazy=True)
members = db.relationship("RoleMember", lazy=True)
class Permission(db.Model):
__tablename__ = "permissions"
id = db.Column(db.Integer, primary_key=True)
resource = db.Column(db.String(250))
action = db.Column(db.String(250))
role = db.Column(db.Integer, db.ForeignKey("roles.id"))
class RoleMember(db.Model):
__tablename__ = "role_members"
id = db.Column(db.Integer, primary_key=True)
user_uid = db.Column(db.String(250))
role = db.Column(db.Integer, db.ForeignKey("roles.id"))
So i found the answer, the problem was that my has_permission was returning a type [(" ")] and i was making the mock returning a type [" "].
So the solution was:
#patch("dev_maintenance.roles.db")
def test_check_if_user_has_permissions(db):
tmp = {}
tmp["user"] = "bender"
tmp["resource"] = "machine/1"
tmp["action"] = "delete"
##The mock
db.session.query.return_value.filter_by.return_value.join.return_value.all.return_value = [(tmp["user"], tmp["resource"], tmp["action"])]
## check if the function is returning True
assert check_if_user_has_permissions("bender","machine/1", "delete") == True

Flask-SQLalchemy one-to-one relationship: associate child to already existing parent?

first time I use sqlalchemy and flask and I miss something
I'm trying to do a one-to-one relationship between two tables with Flask-SQLalchemy.
This is my first table (the parent)
class User(db.Model):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True, autoincrement=True)
user_email = Column(String(150), nullable=False)
user_password = Column(String(60), nullable=False)
child_employee = relationship('Employee', uselist=False, backref='users')
def __init__(self, user_email, user_password):
self.user_email = user_email
self.user_password = user_password
And the child
class Employee(User):
__tablename__ = 'employees'
employee_id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey('users.user_id'), nullable=False)
employee_lastname = Column(String(30), nullable=False)
employee_firstname = Column(String(30), nullable=False)
employee_comment = Column(String)
def __init__(self, user_email, user_password, employee_lastname, employee_firstname, employee_comment=None):
super().__init__(user_email, user_password)
self.employee_lastname = employee_lastname
self.employee_firstname = employee_firstname
self.employee_comment = employee_comment
Know when I create an user, I have a user in my table "users" and nothing in my table "employees" : That make sense, perfect
Then if I create an employee, I have a new entry in my table "employees" and a new entry in my table "users". They are linked to each other : that make also sense, perfect
Now the question :
I created an user so there is no employee linked to it.
Now I want to create an employee and I want to link it to the already existing user, how can I do it ?
I tried
# creation of the new employee
new_employee = Employee(exisiting_user.user_email,
exisiting_user.user_password, "email", "lastname", "firstname")
#trying to associate the new employee to the already existing user
exisiting_user.child_employee = new_employee
# save the employee to the database
db.session.add(new_employee)
db.session.commit()
but I get this error
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) duplicate key value violates unique constraint "users_pkey"
DETAIL: Key (user_id)=(2) already exists.
[SQL: 'INSERT INTO users (user_id, user_email, user_password) VALUES (%(user_id)s, %(user_email)s, %(user_password)s)'] [parameters: {'user_id': 2, 'user_email': 'email', 'user_password': 'password'}]
Thanks
----------------First try to resolve it-------------------------
So as if I understand correctly, when I create an instance of "Employee" I also have an instance of the super class "User". Then whend I db.session.add(new_employee), SQLalchemy create an INSERT for the employee and then for the user, but the user already exist so I have the error.
I change my Employee table :
class Employee(User):
__tablename__ = 'employees'
employee_id = Column(Integer, ForeignKey(User.user_id), primary_key=True)
employee_lastname = Column(String(30), nullable=False)
employee_firstname = Column(String(30), nullable=False)
employee_comment = Column(String)
parent = relationship("User", back_populates="child_employee")
def __init__(self, employee_id, employee_lastname, employee_firstname, employee_comment=None):
self.employee_id = employee_id
self.employee_lastname = employee_lastname
self.employee_firstname = employee_firstname
self.employee_comment = employee_comment
and to add only an employee i'm doing
emp = Employee(1, "lastname", "firstname", "comment")
db.session.execute("INSERT INTO employees VALUES("emp.employee_id",'"emp.employee_lastname+"', '"+emp.employee_firstname+"', '"+emp.employee_comment+"')")
Because of the Id who is also a foreign key, the employee and the user are automatically linked
It works but I would prefer to use something more simple like db.session.add(emp), I still miss something

Resources