diff --git a/OpenOversight/app/auth/views.py b/OpenOversight/app/auth/views.py index caa4cfabc..75c02f734 100644 --- a/OpenOversight/app/auth/views.py +++ b/OpenOversight/app/auth/views.py @@ -292,7 +292,7 @@ def get_users(): @auth.route("/users/", methods=[HTTPMethod.GET, HTTPMethod.POST]) @admin_required def edit_user(user_id): - user = User.query.get(user_id) + user = db.session.get(User, user_id) if not user: return render_template("404.html"), HTTPStatus.NOT_FOUND @@ -339,7 +339,7 @@ def edit_user(user_id): @auth.route("/users//delete", methods=[HTTPMethod.GET, HTTPMethod.POST]) @admin_required def delete_user(user_id): - user = User.query.get(user_id) + user = db.session.get(User, user_id) if not user or user.is_administrator: return render_template("403.html"), HTTPStatus.FORBIDDEN if request.method == HTTPMethod.POST: diff --git a/OpenOversight/app/csv_imports.py b/OpenOversight/app/csv_imports.py index ebb63c7da..7904b2bd4 100644 --- a/OpenOversight/app/csv_imports.py +++ b/OpenOversight/app/csv_imports.py @@ -2,6 +2,7 @@ from contextlib import contextmanager from typing import Dict, Optional +from sqlalchemy import sql from sqlalchemy.exc import SQLAlchemyError from OpenOversight.app.models.database import ( @@ -557,7 +558,7 @@ def import_csv_files( select setval('incidents_id_seq', (select max(id) from incidents)); """ try: - db.session.execute(raw_sql) + db.session.execute(sql.text(raw_sql)) print("Updated sequences.") except SQLAlchemyError: print("Failed to update sequences") diff --git a/OpenOversight/app/main/forms.py b/OpenOversight/app/main/forms.py index da6a7b0cf..ce1dc0145 100644 --- a/OpenOversight/app/main/forms.py +++ b/OpenOversight/app/main/forms.py @@ -30,7 +30,7 @@ from wtforms_sqlalchemy.fields import QuerySelectField from OpenOversight.app.formfields import TimeField -from OpenOversight.app.models.database import Officer +from OpenOversight.app.models.database import Officer, db from OpenOversight.app.utils.choices import ( AGE_CHOICES, DEPARTMENT_STATE_CHOICES, @@ -498,11 +498,11 @@ def process_data(self, value): def validate_oo_id(self, oo_id): if oo_id.data and isinstance(oo_id.data, str): if oo_id.data.isnumeric(): - officer = Officer.query.get(oo_id.data) + officer = db.session.get(Officer, oo_id.data) else: try: officer_id = oo_id.data.split('value="')[1][:-2] - officer = Officer.query.get(officer_id) + officer = db.session.get(Officer, officer_id) # Sometimes we get a string in field.data with py.test, this parses it except IndexError: diff --git a/OpenOversight/app/main/model_view.py b/OpenOversight/app/main/model_view.py index 7f8688584..023ccfd76 100644 --- a/OpenOversight/app/main/model_view.py +++ b/OpenOversight/app/main/model_view.py @@ -60,7 +60,7 @@ def get(self, obj_id): url=f"main.{self.model_name}_api", ) else: - obj = self.model.query.get_or_404(obj_id) + obj = db.get_or_404(self.model, obj_id) return render_template( f"{self.model_name}_detail.html", obj=obj, @@ -111,7 +111,7 @@ def new(self, form=None): @login_required @ac_or_admin_required def edit(self, obj_id, form=None): - obj = self.model.query.get_or_404(obj_id) + obj = db.get_or_404(self.model, obj_id) if self.department_check: if ( not current_user.is_administrator @@ -158,7 +158,7 @@ def edit(self, obj_id, form=None): @login_required @ac_or_admin_required def delete(self, obj_id): - obj = self.model.query.get_or_404(obj_id) + obj = db.get_or_404(self.model, obj_id) if self.department_check: if ( not current_user.is_administrator diff --git a/OpenOversight/app/main/views.py b/OpenOversight/app/main/views.py index a05e1ca50..b09447752 100644 --- a/OpenOversight/app/main/views.py +++ b/OpenOversight/app/main/views.py @@ -743,7 +743,7 @@ def redirect_edit_department(department_id: int): @login_required @admin_required def edit_department(department_id: int): - department = Department.query.get_or_404(department_id) + department = db.get_or_404(Department, department_id) previous_name = department.name form = EditDepartmentForm(obj=department) original_ranks = department.jobs @@ -1831,7 +1831,7 @@ def redirect_submit_officer_images(officer_id: int): @login_required @ac_or_admin_required def submit_officer_images(officer_id: int): - officer = Officer.query.get_or_404(officer_id) + officer = db.get_or_404(Officer, officer_id) return render_template("submit_officer_image.html", officer=officer) @@ -1953,7 +1953,7 @@ def get(self, obj_id: int): dept = None if department_id := request.args.get("department_id"): - dept = Department.query.get_or_404(department_id) + dept = db.get_or_404(Department, department_id) form.department_id.data = department_id incidents = incidents.filter_by(department_id=department_id) @@ -2141,7 +2141,7 @@ def get_edit_form(self, obj: TextForm): def dispatch_request(self, *args, **kwargs): if "officer_id" in kwargs: - officer = Officer.query.get_or_404(kwargs["officer_id"]) + officer = db.get_or_404(Officer, kwargs["officer_id"]) self.officer_id = kwargs.pop("officer_id") self.department_id = officer.department_id return super(TextApi, self).dispatch_request(*args, **kwargs) @@ -2392,7 +2392,7 @@ def new(self, form: FlaskForm = None): @login_required @ac_or_admin_required def delete(self, obj_id: int): - obj = self.model.query.get_or_404(obj_id) + obj = db.get_or_404(self.model, obj_id) if ( not current_user.is_administrator and current_user.ac_department_id != self.get_department_id(obj) @@ -2435,7 +2435,7 @@ def get_department_id(self, obj): def dispatch_request(self, *args, **kwargs): if "officer_id" in kwargs: - officer = Officer.query.get_or_404(kwargs["officer_id"]) + officer = db.get_or_404(Officer, kwargs["officer_id"]) self.officer_id = kwargs.pop("officer_id") self.department_id = officer.department_id return super(OfficerLinkApi, self).dispatch_request(*args, **kwargs) diff --git a/OpenOversight/app/models/database.py b/OpenOversight/app/models/database.py index 347ad1465..1ea4f5c36 100644 --- a/OpenOversight/app/models/database.py +++ b/OpenOversight/app/models/database.py @@ -187,7 +187,9 @@ class Job(BaseModel, TrackUpdates): department_id = db.Column( db.Integer, db.ForeignKey("departments.id", name="jobs_department_id_fkey") ) - department = db.relationship("Department", backref="jobs") + department = db.relationship( + "Department", backref=db.backref("jobs", cascade_backrefs=False) + ) __table_args__ = ( UniqueConstraint( @@ -232,27 +234,45 @@ class Officer(BaseModel, TrackUpdates): gender = db.Column(db.String(5), index=True, unique=False, nullable=True) employment_date = db.Column(db.Date, index=True, unique=False, nullable=True) birth_year = db.Column(db.Integer, index=True, unique=False, nullable=True) - assignments = db.relationship("Assignment", back_populates="base_officer") - face = db.relationship("Face", backref="officer") + assignments = db.relationship( + "Assignment", back_populates="base_officer", cascade_backrefs=False + ) + face = db.relationship( + "Face", backref=db.backref("officer", cascade_backrefs=False) + ) department_id = db.Column( db.Integer, db.ForeignKey("departments.id", name="officers_department_id_fkey") ) - department = db.relationship("Department", backref="officers") + department = db.relationship( + "Department", backref=db.backref("officers", cascade_backrefs=False) + ) unique_internal_identifier = db.Column( db.String(50), index=True, unique=True, nullable=True ) links = db.relationship( - "Link", secondary=officer_links, backref=db.backref("officers", lazy=True) + "Link", + secondary=officer_links, + backref=db.backref("officers", lazy=True, cascade_backrefs=False), + lazy=True, ) notes = db.relationship( - "Note", back_populates="officer", order_by="Note.created_at" + "Note", + back_populates="officer", + cascade_backrefs=False, + order_by="Note.created_at", ) descriptions = db.relationship( - "Description", back_populates="officer", order_by="Description.created_at" + "Description", + back_populates="officer", + cascade_backrefs=False, + order_by="Description.created_at", ) salaries = db.relationship( - "Salary", back_populates="officer", order_by="Salary.year.desc()" + "Salary", + back_populates="officer", + cascade_backrefs=False, + order_by="Salary.year.desc()", ) __table_args__ = ( @@ -407,7 +427,9 @@ class Unit(BaseModel, TrackUpdates): db.ForeignKey("departments.id", name="unit_types_department_id_fkey"), ) department = db.relationship( - "Department", backref="unit_types", order_by="Unit.description.asc()" + "Department", + backref=db.backref("unit_types", cascade_backrefs=False), + order_by="Unit.description.asc()", ) def __repr__(self): @@ -445,9 +467,16 @@ class Face(BaseModel, TrackUpdates): face_position_y = db.Column(db.Integer, unique=False) face_width = db.Column(db.Integer, unique=False) face_height = db.Column(db.Integer, unique=False) - image = db.relationship("Image", backref="faces", foreign_keys=[img_id]) + image = db.relationship( + "Image", + backref=db.backref("faces", cascade_backrefs=False), + foreign_keys=[img_id], + ) original_image = db.relationship( - "Image", backref="tags", foreign_keys=[original_image_id], lazy=True + "Image", + backref=db.backref("tags", cascade_backrefs=False), + foreign_keys=[original_image_id], + lazy=True, ) featured = db.Column( db.Boolean, nullable=False, default=False, server_default="false" @@ -478,7 +507,9 @@ class Image(BaseModel, TrackUpdates): db.Integer, db.ForeignKey("departments.id", name="raw_images_department_id_fkey"), ) - department = db.relationship("Department", backref="raw_images") + department = db.relationship( + "Department", backref=db.backref("raw_images", cascade_backrefs=False) + ) def __repr__(self): return f"" @@ -641,29 +672,33 @@ class Incident(BaseModel, TrackUpdates): address_id = db.Column( db.Integer, db.ForeignKey("locations.id", name="incidents_address_id_fkey") ) - address = db.relationship("Location", backref="incidents") + address = db.relationship( + "Location", backref=db.backref("incidents", cascade_backrefs=False) + ) license_plates = db.relationship( "LicensePlate", secondary=incident_license_plates, lazy="subquery", - backref=db.backref("incidents", lazy=True), + backref=db.backref("incidents", cascade_backrefs=False, lazy=True), ) links = db.relationship( "Link", secondary=incident_links, lazy="subquery", - backref=db.backref("incidents", lazy=True), + backref=db.backref("incidents", cascade_backrefs=False, lazy=True), ) officers = db.relationship( "Officer", secondary=officer_incidents, lazy="subquery", - backref=db.backref("incidents"), + backref=db.backref("incidents", cascade_backrefs=False), ) department_id = db.Column( db.Integer, db.ForeignKey("departments.id", name="incidents_department_id_fkey") ) - department = db.relationship("Department", backref="incidents", lazy=True) + department = db.relationship( + "Department", backref=db.backref("incidents", cascade_backrefs=False), lazy=True + ) class User(UserMixin, BaseModel): @@ -690,7 +725,9 @@ class User(UserMixin, BaseModel): db.Integer, db.ForeignKey("departments.id", name="users_ac_department_id_fkey") ) ac_department = db.relationship( - "Department", backref="coordinators", foreign_keys=[ac_department_id] + "Department", + backref=db.backref("coordinators", cascade_backrefs=False), + foreign_keys=[ac_department_id], ) is_administrator = db.Column(db.Boolean, default=False) is_disabled = db.Column(db.Boolean, default=False) diff --git a/OpenOversight/tests/conftest.py b/OpenOversight/tests/conftest.py index abfa3009d..d9904ef6e 100644 --- a/OpenOversight/tests/conftest.py +++ b/OpenOversight/tests/conftest.py @@ -722,7 +722,7 @@ def add_mockdata(session): users_that_can_create_notes = [test_admin, test_area_coordinator] # for testing routes - first_officer = Officer.query.get(1) + first_officer = session.get(Officer, 1) note = build_note( first_officer, test_admin, "### A markdown note\nA **test** note!" ) @@ -737,7 +737,7 @@ def add_mockdata(session): users_that_can_create_descriptions = [test_admin, test_area_coordinator] # for testing routes - first_officer = Officer.query.get(1) + first_officer = session.get(Officer, 1) description = build_description( first_officer, test_admin, "### A markdown description\nA **test** description!" ) diff --git a/OpenOversight/tests/routes/test_descriptions.py b/OpenOversight/tests/routes/test_descriptions.py index a9421732c..b3bcb5d41 100644 --- a/OpenOversight/tests/routes/test_descriptions.py +++ b/OpenOversight/tests/routes/test_descriptions.py @@ -290,7 +290,7 @@ def test_admins_can_delete_descriptions(mockdata, client, session): follow_redirects=True, ) assert rv.status_code == HTTPStatus.OK - deleted = Description.query.get(description_id) + deleted = session.get(Description, description_id) assert deleted is None @@ -319,7 +319,7 @@ def test_acs_can_delete_their_descriptions_in_their_department( follow_redirects=True, ) assert rv.status_code == HTTPStatus.OK - deleted = Description.query.get(description_id) + deleted = session.get(Description, description_id) assert deleted is None @@ -351,7 +351,7 @@ def test_acs_cannot_delete_descriptions_not_in_their_department( ) assert rv.status_code == HTTPStatus.FORBIDDEN - not_deleted = Description.query.get(description_id) + not_deleted = session.get(Description, description_id) assert not_deleted is not None diff --git a/OpenOversight/tests/routes/test_image_tagging.py b/OpenOversight/tests/routes/test_image_tagging.py index 7f050ac07..2cae18f1b 100644 --- a/OpenOversight/tests/routes/test_image_tagging.py +++ b/OpenOversight/tests/routes/test_image_tagging.py @@ -123,7 +123,7 @@ def test_ac_cannot_delete_tag_not_in_their_dept(mockdata, client, session): login_ac(client) tag = ( - Face.query.join(Face.officer, aliased=True) + Face.query.join(Face.officer) .except_(Face.query.filter(Face.officer.has(department_id=AC_DEPT))) .first() ) @@ -282,7 +282,7 @@ def test_user_is_redirected_to_correct_department_after_tagging( ), follow_redirects=True, ) - department = Department.query.get(department_id) + department = session.get(Department, department_id) assert rv.status_code == HTTPStatus.OK assert department.name in rv.data.decode(ENCODING_UTF_8) @@ -323,7 +323,7 @@ def test_ac_cannot_set_featured_tag_not_in_their_dept(mockdata, client, session) login_ac(client) tag = ( - Face.query.join(Face.officer, aliased=True) + Face.query.join(Face.officer) .except_(Face.query.filter(Face.officer.has(department_id=AC_DEPT))) .first() ) diff --git a/OpenOversight/tests/routes/test_incidents.py b/OpenOversight/tests/routes/test_incidents.py index bef5e79a3..eb3f9e4df 100644 --- a/OpenOversight/tests/routes/test_incidents.py +++ b/OpenOversight/tests/routes/test_incidents.py @@ -250,7 +250,7 @@ def test_admins_can_edit_incident_date_and_address(mockdata, client, session): ) assert rv.status_code == HTTPStatus.OK assert "successfully updated" in rv.data.decode(ENCODING_UTF_8) - updated = Incident.query.get(inc_id) + updated = session.get(Incident, inc_id) assert updated.date == test_date assert updated.time == test_time assert updated.address.street_name == street_name @@ -680,7 +680,7 @@ def test_admins_can_delete_incidents(mockdata, client, session): follow_redirects=True, ) assert rv.status_code == HTTPStatus.OK - deleted = Incident.query.get(inc_id) + deleted = session.get(Incident, inc_id) assert deleted is None @@ -694,7 +694,7 @@ def test_acs_can_delete_incidents_in_their_department(mockdata, client, session) follow_redirects=True, ) assert rv.status_code == HTTPStatus.OK - deleted = Incident.query.get(inc_id) + deleted = session.get(Incident, inc_id) assert deleted is None @@ -710,7 +710,7 @@ def test_acs_cannot_delete_incidents_not_in_their_department(mockdata, client, s follow_redirects=True, ) assert rv.status_code == HTTPStatus.FORBIDDEN - not_deleted = Incident.query.get(inc_id) + not_deleted = session.get(Incident, inc_id) assert not_deleted.id is inc_id diff --git a/OpenOversight/tests/routes/test_notes.py b/OpenOversight/tests/routes/test_notes.py index 159beccea..5e50c5059 100644 --- a/OpenOversight/tests/routes/test_notes.py +++ b/OpenOversight/tests/routes/test_notes.py @@ -260,7 +260,7 @@ def test_admins_can_delete_notes(mockdata, client, session): follow_redirects=True, ) assert rv.status_code == HTTPStatus.OK - deleted = Note.query.get(note.id) + deleted = session.get(Note, note.id) assert deleted is None assert has_database_cache_entry(*cache_params) is False @@ -285,7 +285,7 @@ def test_acs_can_delete_their_notes_in_their_department(mockdata, client, sessio follow_redirects=True, ) assert rv.status_code == HTTPStatus.OK - deleted = Note.query.get(note.id) + deleted = session.get(Note, note.id) assert deleted is None @@ -314,7 +314,7 @@ def test_acs_cannot_delete_notes_not_in_their_department( ) assert rv.status_code == HTTPStatus.FORBIDDEN - not_deleted = Note.query.get(note.id) + not_deleted = session.get(Note, note.id) assert not_deleted is not None diff --git a/OpenOversight/tests/routes/test_officer_and_department.py b/OpenOversight/tests/routes/test_officer_and_department.py index ebfc65e86..7f4953859 100644 --- a/OpenOversight/tests/routes/test_officer_and_department.py +++ b/OpenOversight/tests/routes/test_officer_and_department.py @@ -1775,7 +1775,8 @@ def test_assignments_csv(mockdata, client, session, department): all_rows = list(csv.DictReader(csv_data.split("\n"))) for row in all_rows: assert ( - Officer.query.get(int(row["officer id"])).department_id == department.id + session.get(Officer, int(row["officer id"])).department_id + == department.id ) lines = [row for row in all_rows if int(row["officer id"]) == officer.id] assert len(lines) == 2 diff --git a/OpenOversight/tests/routes/test_user_api.py b/OpenOversight/tests/routes/test_user_api.py index 715c021d6..84f7cef09 100644 --- a/OpenOversight/tests/routes/test_user_api.py +++ b/OpenOversight/tests/routes/test_user_api.py @@ -134,7 +134,7 @@ def test_admin_can_delete_user(mockdata, client, session): assert f"User {user.username} has been deleted!" in rv.data.decode( ENCODING_UTF_8 ) - assert not User.query.get(user.id) + assert not session.get(User, user.id) def test_admin_cannot_delete_other_admin(mockdata, client, session): @@ -150,7 +150,7 @@ def test_admin_cannot_delete_other_admin(mockdata, client, session): ) assert rv.status_code == HTTPStatus.FORBIDDEN - assert User.query.get(user.id) is not None + assert session.get(User, user.id) is not None def test_admin_can_disable_user(mockdata, client, session): @@ -175,7 +175,7 @@ def test_admin_can_disable_user(mockdata, client, session): assert "updated!" in rv.data.decode(ENCODING_UTF_8) - user = User.query.get(user.id) + user = session.get(User, user.id) assert user.is_disabled @@ -198,7 +198,7 @@ def test_admin_cannot_disable_self(mockdata, client, session): assert "You cannot edit your own account!" in rv.data.decode(ENCODING_UTF_8) - user = User.query.get(user.id) + user = session.get(User, user.id) assert not user.is_disabled @@ -210,7 +210,7 @@ def test_admin_can_enable_user(mockdata, client, session): user.is_disabled = True db.session.commit() - user = User.query.get(user.id) + user = session.get(User, user.id) assert user.is_disabled form = EditUserForm( @@ -226,7 +226,7 @@ def test_admin_can_enable_user(mockdata, client, session): assert "updated!" in rv.data.decode(ENCODING_UTF_8) - user = User.query.get(user.id) + user = session.get(User, user.id) assert not user.is_disabled @@ -293,7 +293,7 @@ def test_admin_can_approve_user(mockdata, client, session): user.approved = False db.session.commit() - user = User.query.get(user.id) + user = session.get(User, user.id) assert not user.approved form = EditUserForm( @@ -309,7 +309,7 @@ def test_admin_can_approve_user(mockdata, client, session): assert "updated!" in rv.data.decode(ENCODING_UTF_8) - user = User.query.get(user.id) + user = session.get(User, user.id) assert user.approved @@ -346,7 +346,7 @@ def test_admin_approval_sends_confirmation_email( user.confirmed = currently_confirmed db.session.commit() - user = User.query.get(user.id) + user = session.get(User, user.id) assert user.approved == currently_approved assert user.confirmed == currently_confirmed @@ -363,5 +363,5 @@ def test_admin_approval_sends_confirmation_email( ) == should_send_email assert "updated!" in rv.data.decode(ENCODING_UTF_8) - user = User.query.get(user.id) + user = session.get(User, user.id) assert user.approved diff --git a/OpenOversight/tests/test_commands.py b/OpenOversight/tests/test_commands.py index d3704fadb..3bde7a114 100644 --- a/OpenOversight/tests/test_commands.py +++ b/OpenOversight/tests/test_commands.py @@ -1009,7 +1009,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir): assert address.zip_code == "60603" assert incident2.officers == [cop1] - incident3 = Incident.query.get(123456) + incident3 = session.get(Incident, 123456) assert incident3.report_number == "CR-39283" assert incident3.description == "Don't know where it happened" assert incident3.officers == [cop1] @@ -1032,7 +1032,7 @@ def test_advanced_csv_import__success(session, department, test_csv_dir): assert incident_link.title == "Another Link" assert incident_link.author == "Example Times" - updated_link = Link.query.get(55051) + updated_link = session.get(Link, 55051) assert updated_link.title == "Updated Link" assert updated_link.officers == [] assert updated_link.incidents == [incident3] @@ -1157,23 +1157,23 @@ def test_advanced_csv_import__force_create(session, department, tmp_path): assert result.exit_code == 0 # make sure all the data is imported as expected - cop1 = Officer.query.get(99001) + cop1 = session.get(Officer, 99001) assert cop1.first_name == "First" - cop2 = Officer.query.get(99002) + cop2 = session.get(Officer, 99002) assert cop2.assignments[0].star_no == "12345" - assert cop2.assignments[0] == Assignment.query.get(98001) + assert cop2.assignments[0] == session.get(Assignment, 98001) - cop3 = Officer.query.get(99003) + cop3 = session.get(Officer, 99003) assert cop3.salaries[0].salary == 98765 - assert cop3.salaries[0] == Salary.query.get(77001) + assert cop3.salaries[0] == session.get(Salary, 77001) - incident = Incident.query.get(66001) + incident = session.get(Incident, 66001) assert incident.address.street_name == "Fake Street" assert cop1.incidents[0] == incident assert cop2.incidents[0] == incident - link = Link.query.get(55001) + link = session.get(Link, 55001) assert link.url == "https://www.example.org/3629" assert cop1.links[0] == link @@ -1276,13 +1276,13 @@ def test_advanced_csv_import__overwrite_assignments(session, department, tmp_pat assert result.exit_code == 0 # make sure all the data is imported as expected - cop1 = Officer.query.get(cop1_id) + cop1 = session.get(Officer, cop1_id) assert len(cop1.assignments) == 1 assert cop1.assignments[0].star_no == b1 - cop2 = Officer.query.get(cop2_id) + cop2 = session.get(Officer, cop2_id) assert len(cop2.assignments) == 1 - assert cop2.assignments[0] == Assignment.query.get(a2_id) + assert cop2.assignments[0] == session.get(Assignment, a2_id) cop3 = Officer.query.filter_by(first_name="Second", last_name="Test").first() assert len(cop3.assignments) == 1 diff --git a/OpenOversight/tests/test_utils.py b/OpenOversight/tests/test_utils.py index e4b9f1072..420718927 100644 --- a/OpenOversight/tests/test_utils.py +++ b/OpenOversight/tests/test_utils.py @@ -99,11 +99,13 @@ def test_filter_by_name(mockdata): def test_filters_do_not_exclude_officers_without_assignments(mockdata): department = Department.query.first() - officer = Officer( - first_name="Rachel", last_name="S", department=department, birth_year=1992 - ) results = grab_officers({"name": "S", "dept": department}) - assert officer in results.all() + no_assignments = False + for element in results.all(): + if len(element.assignments) == 0: + no_assignments = True + break + assert no_assignments def test_filter_by_badge_no(mockdata): diff --git a/requirements.txt b/requirements.txt index c964db2b6..4d02641a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -59,7 +59,7 @@ s3transfer~=0.6.1 selenium~=4.10.0 six~=1.16.0 sphinx==7.1.2 -SQLAlchemy==1.4.47 # Updating this breaks the build for python 3.9 +SQLAlchemy==1.4.52 tornado~=6.3.2 trio==0.22.1 urllib3~=1.26.16 # This version is required for other packages to run