File: test_metadata.py

package info (click to toggle)
flask-sqlalchemy 3.1.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 832 kB
  • sloc: python: 2,909; makefile: 27; sh: 14
file content (164 lines) | stat: -rw-r--r-- 5,281 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from __future__ import annotations

import typing as t

import pytest
import sqlalchemy as sa
import sqlalchemy.exc as sa_exc
import sqlalchemy.orm as sa_orm
from flask import Flask

from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import DefaultMeta
from flask_sqlalchemy.model import Model


def test_default_metadata(db: SQLAlchemy) -> None:
    assert db.metadata is db.metadatas[None]
    assert db.metadata.info["bind_key"] is None
    assert db.Model.metadata is db.metadata


def test_custom_metadata_1x() -> None:
    metadata = sa.MetaData()
    db = SQLAlchemy(metadata=metadata)
    assert db.metadata is metadata
    assert db.metadata.info["bind_key"] is None
    assert db.Model.metadata is db.metadata


def test_custom_metadata_2x_wrongway() -> None:
    custom_metadata = sa.MetaData()

    class Base(sa_orm.DeclarativeBase):
        pass

    with pytest.deprecated_call():
        db = SQLAlchemy(model_class=Base, metadata=custom_metadata)

        assert db.metadata is Base.metadata
        assert db.metadata.info["bind_key"] is None
        assert db.Model.metadata is db.metadata


def test_custom_metadata_2x() -> None:
    custom_metadata = sa.MetaData()

    class Base(sa_orm.DeclarativeBase):
        metadata = custom_metadata

    db = SQLAlchemy(model_class=Base)

    assert db.metadata is custom_metadata
    assert db.metadata.info["bind_key"] is None
    assert db.Model.metadata is db.metadata


def test_metadata_from_custom_model(model_class: t.Any) -> None:
    if model_class is not Model:
        # In 2.x, SQLAlchemy creates the metadata attribute
        base = model_class
    else:
        # For 1.x, our extension creates the metadata attribute
        base = sa_orm.declarative_base(cls=Model, metaclass=DefaultMeta)
    metadata = base.metadata
    db = SQLAlchemy(model_class=base)
    assert db.Model.metadata is metadata
    assert db.Model.metadata is db.metadata


def test_custom_metadata_overrides_custom_model_legacy() -> None:
    base = sa_orm.declarative_base(cls=Model, metaclass=DefaultMeta)
    metadata = sa.MetaData()
    db = SQLAlchemy(model_class=base, metadata=metadata)
    assert db.Model.metadata is metadata
    assert db.Model.metadata is db.metadata


def test_metadata_per_bind(app: Flask, model_class: t.Any) -> None:
    app.config["SQLALCHEMY_BINDS"] = {"a": "sqlite://"}
    db = SQLAlchemy(app, model_class=model_class)
    assert db.metadatas["a"] is not db.metadata
    assert db.metadatas["a"].info["bind_key"] == "a"


def test_copy_naming_convention(app: Flask, model_class: t.Any) -> None:
    app.config["SQLALCHEMY_BINDS"] = {"a": "sqlite://"}
    if model_class is not Model:
        model_class.metadata = sa.MetaData(
            naming_convention={"pk": "spk_%(table_name)s"}
        )
        db = SQLAlchemy(app, model_class=model_class)
    else:
        db = SQLAlchemy(
            app, metadata=sa.MetaData(naming_convention={"pk": "spk_%(table_name)s"})
        )
    assert db.metadata.naming_convention["pk"] == "spk_%(table_name)s"
    assert db.metadatas["a"].naming_convention == db.metadata.naming_convention


@pytest.mark.usefixtures("app_ctx")
def test_create_drop_all(app: Flask) -> None:
    app.config["SQLALCHEMY_BINDS"] = {"a": "sqlite://"}
    db = SQLAlchemy(app)

    class User(db.Model):
        id = sa.Column(sa.Integer, primary_key=True)

    class Post(db.Model):
        __bind_key__ = "a"
        id = sa.Column(sa.Integer, primary_key=True)

    with pytest.raises(sa_exc.OperationalError):
        db.session.execute(sa.select(User)).scalars()

    with pytest.raises(sa_exc.OperationalError):
        db.session.execute(sa.select(Post)).scalars()

    db.create_all()
    db.session.execute(sa.select(User)).scalars()
    db.session.execute(sa.select(Post)).scalars()
    db.drop_all()

    with pytest.raises(sa_exc.OperationalError):
        db.session.execute(sa.select(User)).scalars()

    with pytest.raises(sa_exc.OperationalError):
        db.session.execute(sa.select(Post)).scalars()


@pytest.mark.usefixtures("app_ctx")
@pytest.mark.parametrize("bind_key", ["a", ["a"]])
def test_create_key_spec(app: Flask, bind_key: str | list[str | None]) -> None:
    app.config["SQLALCHEMY_BINDS"] = {"a": "sqlite://"}
    db = SQLAlchemy(app)

    class User(db.Model):
        id = sa.Column(sa.Integer, primary_key=True)

    class Post(db.Model):
        __bind_key__ = "a"
        id = sa.Column(sa.Integer, primary_key=True)

    db.create_all(bind_key=bind_key)
    db.session.execute(sa.select(Post)).scalars()

    with pytest.raises(sa_exc.OperationalError):
        db.session.execute(sa.select(User)).scalars()


@pytest.mark.usefixtures("app_ctx")
def test_reflect(app: Flask) -> None:
    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///user.db"
    app.config["SQLALCHEMY_BINDS"] = {"post": "sqlite:///post.db"}
    db = SQLAlchemy(app)
    db.Table("user", sa.Column("id", sa.Integer, primary_key=True))
    db.Table("post", sa.Column("id", sa.Integer, primary_key=True), bind_key="post")
    db.create_all()

    del app.extensions["sqlalchemy"]
    db = SQLAlchemy(app)
    assert not db.metadata.tables
    db.reflect()
    assert "user" in db.metadata.tables
    assert "post" in db.metadatas["post"].tables