masonite-framework-orm 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
- masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
- masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
- masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
- masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
- masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
- masoniteorm/__init__.py +1 -0
- masoniteorm/collection/Collection.py +605 -0
- masoniteorm/collection/__init__.py +1 -0
- masoniteorm/commands/CanOverrideConfig.py +16 -0
- masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
- masoniteorm/commands/Command.py +6 -0
- masoniteorm/commands/Entry.py +43 -0
- masoniteorm/commands/MakeMigrationCommand.py +57 -0
- masoniteorm/commands/MakeModelCommand.py +78 -0
- masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
- masoniteorm/commands/MakeObserverCommand.py +54 -0
- masoniteorm/commands/MakeSeedCommand.py +54 -0
- masoniteorm/commands/MigrateCommand.py +46 -0
- masoniteorm/commands/MigrateFreshCommand.py +41 -0
- masoniteorm/commands/MigrateRefreshCommand.py +41 -0
- masoniteorm/commands/MigrateResetCommand.py +25 -0
- masoniteorm/commands/MigrateRollbackCommand.py +26 -0
- masoniteorm/commands/MigrateStatusCommand.py +51 -0
- masoniteorm/commands/SeedRunCommand.py +35 -0
- masoniteorm/commands/ShellCommand.py +205 -0
- masoniteorm/commands/__init__.py +18 -0
- masoniteorm/commands/stubs/create_migration.stub +20 -0
- masoniteorm/commands/stubs/create_seed.stub +9 -0
- masoniteorm/commands/stubs/model.stub +9 -0
- masoniteorm/commands/stubs/observer.stub +101 -0
- masoniteorm/commands/stubs/table_migration.stub +19 -0
- masoniteorm/config.py +123 -0
- masoniteorm/connections/BaseConnection.py +101 -0
- masoniteorm/connections/ConnectionFactory.py +59 -0
- masoniteorm/connections/ConnectionResolver.py +132 -0
- masoniteorm/connections/MSSQLConnection.py +176 -0
- masoniteorm/connections/MySQLConnection.py +232 -0
- masoniteorm/connections/PostgresConnection.py +225 -0
- masoniteorm/connections/SQLiteConnection.py +179 -0
- masoniteorm/connections/__init__.py +6 -0
- masoniteorm/exceptions.py +38 -0
- masoniteorm/expressions/__init__.py +1 -0
- masoniteorm/expressions/expressions.py +288 -0
- masoniteorm/factories/Factory.py +112 -0
- masoniteorm/factories/__init__.py +1 -0
- masoniteorm/helpers/__init__.py +0 -0
- masoniteorm/helpers/misc.py +22 -0
- masoniteorm/migrations/Migration.py +330 -0
- masoniteorm/migrations/__init__.py +1 -0
- masoniteorm/models/MigrationModel.py +9 -0
- masoniteorm/models/Model.py +1209 -0
- masoniteorm/models/Model.pyi +1366 -0
- masoniteorm/models/Pivot.py +5 -0
- masoniteorm/models/__init__.py +1 -0
- masoniteorm/observers/ObservesEvents.py +27 -0
- masoniteorm/observers/__init__.py +1 -0
- masoniteorm/pagination/BasePaginator.py +10 -0
- masoniteorm/pagination/LengthAwarePaginator.py +34 -0
- masoniteorm/pagination/SimplePaginator.py +28 -0
- masoniteorm/pagination/__init__.py +2 -0
- masoniteorm/providers/ORMProvider.py +39 -0
- masoniteorm/providers/__init__.py +1 -0
- masoniteorm/query/EagerRelation.py +42 -0
- masoniteorm/query/QueryBuilder.py +2486 -0
- masoniteorm/query/__init__.py +1 -0
- masoniteorm/query/grammars/BaseGrammar.py +1027 -0
- masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
- masoniteorm/query/grammars/MySQLGrammar.py +238 -0
- masoniteorm/query/grammars/PostgresGrammar.py +213 -0
- masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
- masoniteorm/query/grammars/__init__.py +4 -0
- masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
- masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
- masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
- masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
- masoniteorm/query/processors/__init__.py +4 -0
- masoniteorm/relationships/BaseRelationship.py +161 -0
- masoniteorm/relationships/BelongsTo.py +124 -0
- masoniteorm/relationships/BelongsToMany.py +604 -0
- masoniteorm/relationships/HasMany.py +66 -0
- masoniteorm/relationships/HasManyThrough.py +269 -0
- masoniteorm/relationships/HasOne.py +111 -0
- masoniteorm/relationships/HasOneThrough.py +275 -0
- masoniteorm/relationships/MorphMany.py +152 -0
- masoniteorm/relationships/MorphOne.py +156 -0
- masoniteorm/relationships/MorphTo.py +111 -0
- masoniteorm/relationships/MorphToMany.py +108 -0
- masoniteorm/relationships/__init__.py +10 -0
- masoniteorm/schema/Blueprint.py +1161 -0
- masoniteorm/schema/Column.py +144 -0
- masoniteorm/schema/ColumnDiff.py +0 -0
- masoniteorm/schema/Constraint.py +5 -0
- masoniteorm/schema/ForeignKeyConstraint.py +28 -0
- masoniteorm/schema/Index.py +5 -0
- masoniteorm/schema/Schema.py +359 -0
- masoniteorm/schema/Table.py +94 -0
- masoniteorm/schema/TableDiff.py +86 -0
- masoniteorm/schema/__init__.py +3 -0
- masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
- masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
- masoniteorm/schema/platforms/Platform.py +97 -0
- masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
- masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
- masoniteorm/schema/platforms/__init__.py +4 -0
- masoniteorm/scopes/BaseScope.py +6 -0
- masoniteorm/scopes/SoftDeleteScope.py +56 -0
- masoniteorm/scopes/SoftDeletesMixin.py +13 -0
- masoniteorm/scopes/TimeStampsMixin.py +12 -0
- masoniteorm/scopes/TimeStampsScope.py +47 -0
- masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
- masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
- masoniteorm/scopes/__init__.py +8 -0
- masoniteorm/scopes/scope.py +15 -0
- masoniteorm/seeds/Seeder.py +42 -0
- masoniteorm/seeds/__init__.py +1 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import shlex
|
|
4
|
+
import subprocess
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
|
|
7
|
+
from ..config import load_config
|
|
8
|
+
from .Command import Command
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ShellCommand(Command):
|
|
12
|
+
"""
|
|
13
|
+
Connect to your database interactive terminal.
|
|
14
|
+
|
|
15
|
+
shell
|
|
16
|
+
{--c|connection=default : The connection you want to use to connect to interactive terminal}
|
|
17
|
+
{--s|show=? : Display the command which will be called to connect}
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
shell_programs = {
|
|
21
|
+
"mysql": "mysql",
|
|
22
|
+
"postgres": "psql",
|
|
23
|
+
"sqlite": "sqlite3",
|
|
24
|
+
"mssql": "sqlcmd",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def handle(self):
|
|
28
|
+
resolver = load_config(self.option("config")).DB
|
|
29
|
+
connection = self.option("connection")
|
|
30
|
+
if connection == "default":
|
|
31
|
+
connection = resolver.get_connection_details()["default"]
|
|
32
|
+
config = resolver.get_connection_information(connection)
|
|
33
|
+
if not config.get("full_details"):
|
|
34
|
+
self.line(
|
|
35
|
+
f"<error>Connection configuration for '{connection}' not found !</error>"
|
|
36
|
+
)
|
|
37
|
+
exit(-1)
|
|
38
|
+
|
|
39
|
+
command, env = self.get_command(config)
|
|
40
|
+
|
|
41
|
+
if self.option("show"):
|
|
42
|
+
cleaned_command = self.hide_sensitive_options(config, command)
|
|
43
|
+
self.comment(cleaned_command)
|
|
44
|
+
self.line("")
|
|
45
|
+
|
|
46
|
+
# let shlex split command in a list as it's safer
|
|
47
|
+
command_args = shlex.split(command)
|
|
48
|
+
try:
|
|
49
|
+
subprocess.run(command_args, check=True, env=env)
|
|
50
|
+
except FileNotFoundError:
|
|
51
|
+
self.line(
|
|
52
|
+
f"<error>Cannot find {config.get('full_details').get('driver')} program ! Please ensure you can call this program in your shell first.</error>"
|
|
53
|
+
)
|
|
54
|
+
exit(-1)
|
|
55
|
+
except subprocess.CalledProcessError:
|
|
56
|
+
self.line("<error>An error happened calling the command.</error>")
|
|
57
|
+
exit(-1)
|
|
58
|
+
|
|
59
|
+
def get_shell_program(self, connection):
|
|
60
|
+
"""Get the database shell program to run."""
|
|
61
|
+
return self.shell_programs.get(connection)
|
|
62
|
+
|
|
63
|
+
def get_command(self, config):
|
|
64
|
+
"""Get the command to run as a string."""
|
|
65
|
+
driver = config.get("full_details").get("driver")
|
|
66
|
+
program = self.get_shell_program(driver)
|
|
67
|
+
try:
|
|
68
|
+
get_driver_args = getattr(self, f"get_{driver}_args")
|
|
69
|
+
except AttributeError:
|
|
70
|
+
self.line(
|
|
71
|
+
f"<error>Connecting with driver '{driver}' is not implemented !</error>"
|
|
72
|
+
)
|
|
73
|
+
exit(-1)
|
|
74
|
+
args, options = get_driver_args(config)
|
|
75
|
+
# process positional arguments
|
|
76
|
+
args = " ".join(args)
|
|
77
|
+
# process optional arguments
|
|
78
|
+
options = self.remove_empty_options(options)
|
|
79
|
+
options_string = " ".join(
|
|
80
|
+
f"{option} {value}" if value else option
|
|
81
|
+
for option, value in options.items()
|
|
82
|
+
)
|
|
83
|
+
# finally build command string
|
|
84
|
+
command = program
|
|
85
|
+
if args:
|
|
86
|
+
command += f" {args}"
|
|
87
|
+
if options_string:
|
|
88
|
+
command += f" {options_string}"
|
|
89
|
+
|
|
90
|
+
# prepare environment in which command will be run
|
|
91
|
+
# some drivers need to define env variable such as psql for specifying password
|
|
92
|
+
try:
|
|
93
|
+
driver_env = getattr(self, f"get_{driver}_env")(config)
|
|
94
|
+
except AttributeError:
|
|
95
|
+
driver_env = {}
|
|
96
|
+
command_env = {**os.environ.copy(), **driver_env}
|
|
97
|
+
|
|
98
|
+
return command, command_env
|
|
99
|
+
|
|
100
|
+
def get_mysql_args(self, config):
|
|
101
|
+
"""Get command positional arguments and options for MySQL driver."""
|
|
102
|
+
args = [config.get("database")]
|
|
103
|
+
options = OrderedDict(
|
|
104
|
+
{
|
|
105
|
+
"--host": config.get("host"),
|
|
106
|
+
"--port": config.get("port"),
|
|
107
|
+
"--user": config.get("user"),
|
|
108
|
+
"--password": config.get("password"),
|
|
109
|
+
"--default-character-set": config.get("options", {}).get(
|
|
110
|
+
"charset"
|
|
111
|
+
),
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
return args, options
|
|
115
|
+
|
|
116
|
+
def get_postgres_args(self, config):
|
|
117
|
+
"""Get command positional arguments and options for PostgreSQL driver."""
|
|
118
|
+
args = [config.get("database")]
|
|
119
|
+
options = OrderedDict(
|
|
120
|
+
{
|
|
121
|
+
"--host": config.get("host"),
|
|
122
|
+
"--port": config.get("port"),
|
|
123
|
+
"--username": config.get("user"),
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
return args, options
|
|
127
|
+
|
|
128
|
+
def get_postgres_env(self, config):
|
|
129
|
+
return {"PGPASSWORD": config.get("password")}
|
|
130
|
+
|
|
131
|
+
def get_mssql_args(self, config):
|
|
132
|
+
"""Get command positional arguments and options for MSSQL driver."""
|
|
133
|
+
args = []
|
|
134
|
+
|
|
135
|
+
# instance of SQL Server: -S [protocol:]server[instance_name][,port]
|
|
136
|
+
server = f"tcp:{config.get('host')}"
|
|
137
|
+
if config.get("port"):
|
|
138
|
+
server += f",{config.get('port')}"
|
|
139
|
+
|
|
140
|
+
trusted_connection = (
|
|
141
|
+
config.get("options").get("trusted_connection") == "Yes"
|
|
142
|
+
)
|
|
143
|
+
options = OrderedDict(
|
|
144
|
+
{
|
|
145
|
+
"-d": config.get("database"),
|
|
146
|
+
"-U": config.get("user"),
|
|
147
|
+
"-P": config.get("password"),
|
|
148
|
+
"-S": server,
|
|
149
|
+
"-E": trusted_connection,
|
|
150
|
+
"-t": config.get("options", {}).get("connection_timeout"),
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
return args, options
|
|
154
|
+
|
|
155
|
+
def get_sqlite_args(self, config):
|
|
156
|
+
"""Get command positional arguments and options for SQLite driver."""
|
|
157
|
+
args = [config.get("database")]
|
|
158
|
+
options = OrderedDict()
|
|
159
|
+
return args, options
|
|
160
|
+
|
|
161
|
+
def remove_empty_options(self, options):
|
|
162
|
+
"""Remove empty options when value does not evaluate to True.
|
|
163
|
+
Also when value is exactly 'True' we don't want True to be passed as option value but
|
|
164
|
+
we want the option to be passed.
|
|
165
|
+
"""
|
|
166
|
+
# remove empty options and remove value when option is True
|
|
167
|
+
cleaned_options = OrderedDict()
|
|
168
|
+
for key, value in options.items():
|
|
169
|
+
if value is True:
|
|
170
|
+
cleaned_options[key] = ""
|
|
171
|
+
elif value:
|
|
172
|
+
cleaned_options[key] = value
|
|
173
|
+
return cleaned_options
|
|
174
|
+
|
|
175
|
+
def get_sensitive_options(self, config):
|
|
176
|
+
driver = config.get("full_details").get("driver")
|
|
177
|
+
try:
|
|
178
|
+
sensitive_options = getattr(
|
|
179
|
+
self, f"get_{driver}_sensitive_options"
|
|
180
|
+
)()
|
|
181
|
+
except AttributeError:
|
|
182
|
+
sensitive_options = []
|
|
183
|
+
return sensitive_options
|
|
184
|
+
|
|
185
|
+
def get_mysql_sensitive_options(self):
|
|
186
|
+
return ["--password"]
|
|
187
|
+
|
|
188
|
+
def get_mssql_sensitive_options(self):
|
|
189
|
+
return ["-P"]
|
|
190
|
+
|
|
191
|
+
def hide_sensitive_options(self, config, command):
|
|
192
|
+
"""Hide sensitive options in command string before it's displayed in the shell for
|
|
193
|
+
security reasons. All drivers can declare what options are considered sensitive, their
|
|
194
|
+
values will be then replaced by *** when displayed only."""
|
|
195
|
+
cleaned_command = command
|
|
196
|
+
sensitive_options = self.get_sensitive_options(config)
|
|
197
|
+
for option in sensitive_options:
|
|
198
|
+
# if option is used obfuscate its value
|
|
199
|
+
if option in command:
|
|
200
|
+
match = re.search(rf"{option} (\w+)", command)
|
|
201
|
+
if match.groups():
|
|
202
|
+
cleaned_command = cleaned_command.replace(
|
|
203
|
+
match.groups()[0], "***"
|
|
204
|
+
)
|
|
205
|
+
return cleaned_command
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
sys.path.append(os.getcwd())
|
|
5
|
+
|
|
6
|
+
from .MakeMigrationCommand import MakeMigrationCommand
|
|
7
|
+
from .MakeModelCommand import MakeModelCommand
|
|
8
|
+
from .MakeModelDocstringCommand import MakeModelDocstringCommand
|
|
9
|
+
from .MakeObserverCommand import MakeObserverCommand
|
|
10
|
+
from .MakeSeedCommand import MakeSeedCommand
|
|
11
|
+
from .MigrateCommand import MigrateCommand
|
|
12
|
+
from .MigrateFreshCommand import MigrateFreshCommand
|
|
13
|
+
from .MigrateRefreshCommand import MigrateRefreshCommand
|
|
14
|
+
from .MigrateResetCommand import MigrateResetCommand
|
|
15
|
+
from .MigrateRollbackCommand import MigrateRollbackCommand
|
|
16
|
+
from .MigrateStatusCommand import MigrateStatusCommand
|
|
17
|
+
from .SeedRunCommand import SeedRunCommand
|
|
18
|
+
from .ShellCommand import ShellCommand
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""__MIGRATION_NAME__ Migration."""
|
|
2
|
+
|
|
3
|
+
from masoniteorm.migrations import Migration
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class __MIGRATION_NAME__(Migration):
|
|
7
|
+
def up(self):
|
|
8
|
+
"""
|
|
9
|
+
Run the migrations.
|
|
10
|
+
"""
|
|
11
|
+
with self.schema.create("__TABLE_NAME__") as table:
|
|
12
|
+
table.increments("id")
|
|
13
|
+
|
|
14
|
+
table.timestamps()
|
|
15
|
+
|
|
16
|
+
def down(self):
|
|
17
|
+
"""
|
|
18
|
+
Revert the migrations.
|
|
19
|
+
"""
|
|
20
|
+
self.schema.drop("__TABLE_NAME__")
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""__CLASS__ Observer"""
|
|
2
|
+
|
|
3
|
+
from masoniteorm.models import Model
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class __CLASS__Observer:
|
|
7
|
+
def created(self, __MODEL_VARIABLE__):
|
|
8
|
+
"""Handle the __MODEL__ "created" event.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
12
|
+
"""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def creating(self, __MODEL_VARIABLE__):
|
|
16
|
+
"""Handle the __MODEL__ "creating" event.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
20
|
+
"""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def saving(self, __MODEL_VARIABLE__):
|
|
24
|
+
"""Handle the __MODEL__ "saving" event.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def saved(self, __MODEL_VARIABLE__):
|
|
32
|
+
"""Handle the __MODEL__ "saved" event.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
36
|
+
"""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def updating(self, __MODEL_VARIABLE__):
|
|
40
|
+
"""Handle the __MODEL__ "updating" event.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
44
|
+
"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
def updated(self, __MODEL_VARIABLE__):
|
|
48
|
+
"""Handle the __MODEL__ "updated" event.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
52
|
+
"""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
def booted(self, __MODEL_VARIABLE__):
|
|
56
|
+
"""Handle the __MODEL__ "booted" event.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
60
|
+
"""
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
def booting(self, __MODEL_VARIABLE__):
|
|
64
|
+
"""Handle the __MODEL__ "booting" event.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
68
|
+
"""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def hydrating(self, __MODEL_VARIABLE__):
|
|
72
|
+
"""Handle the __MODEL__ "hydrating" event.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
76
|
+
"""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
def hydrated(self, __MODEL_VARIABLE__):
|
|
80
|
+
"""Handle the __MODEL__ "hydrated" event.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
84
|
+
"""
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
def deleting(self, __MODEL_VARIABLE__):
|
|
88
|
+
"""Handle the __MODEL__ "deleting" event.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
92
|
+
"""
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
def deleted(self, __MODEL_VARIABLE__):
|
|
96
|
+
"""Handle the __MODEL__ "deleted" event.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
__MODEL_VARIABLE__ (masoniteorm.models.Model): __MODEL__ model.
|
|
100
|
+
"""
|
|
101
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""__MIGRATION_NAME__ Migration."""
|
|
2
|
+
|
|
3
|
+
from masoniteorm.migrations import Migration
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class __MIGRATION_NAME__(Migration):
|
|
7
|
+
def up(self):
|
|
8
|
+
"""
|
|
9
|
+
Run the migrations.
|
|
10
|
+
"""
|
|
11
|
+
with self.schema.table("__TABLE_NAME__") as table:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
def down(self):
|
|
15
|
+
"""
|
|
16
|
+
Revert the migrations.
|
|
17
|
+
"""
|
|
18
|
+
with self.schema.table("__TABLE_NAME__") as table:
|
|
19
|
+
pass
|
masoniteorm/config.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pydoc
|
|
3
|
+
import urllib.parse as urlparse
|
|
4
|
+
|
|
5
|
+
from .exceptions import ConfigurationNotFound, InvalidUrlConfiguration
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def load_config(config_path=None):
|
|
9
|
+
"""Load ORM configuration from given configuration path (dotted or not).
|
|
10
|
+
If no path is provided:
|
|
11
|
+
1. try to load from DB_CONFIG_PATH environment variable
|
|
12
|
+
2. else try to load from default config_path: config/database
|
|
13
|
+
"""
|
|
14
|
+
selected_config_path = (
|
|
15
|
+
os.getenv("DB_CONFIG_PATH", config_path) or "config/database"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
os.environ["DB_CONFIG_PATH"] = selected_config_path
|
|
19
|
+
|
|
20
|
+
# format path as python module if needed
|
|
21
|
+
selected_config_path = (
|
|
22
|
+
selected_config_path.replace("/", ".").replace("\\", ".").rstrip(".py")
|
|
23
|
+
)
|
|
24
|
+
config_module = pydoc.locate(selected_config_path)
|
|
25
|
+
if config_module is None:
|
|
26
|
+
raise ConfigurationNotFound(
|
|
27
|
+
f"ORM configuration file has not been found in {selected_config_path}."
|
|
28
|
+
)
|
|
29
|
+
return config_module
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def db_url(database_url=None, prefix="", options={}, log_queries=False):
|
|
33
|
+
"""Parse connection configuration from database url format. If no url is provided,
|
|
34
|
+
DATABASE_URL environment variable will be used instead.
|
|
35
|
+
|
|
36
|
+
Reference: Code adapted from https://github.com/jacobian/dj-database-url
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
url = database_url or os.getenv("DATABASE_URL")
|
|
40
|
+
if not url:
|
|
41
|
+
raise InvalidUrlConfiguration("Database url is empty !")
|
|
42
|
+
|
|
43
|
+
# Register database schemes in URLs.
|
|
44
|
+
urlparse.uses_netloc.append("postgres")
|
|
45
|
+
urlparse.uses_netloc.append("postgresql")
|
|
46
|
+
urlparse.uses_netloc.append("pgsql")
|
|
47
|
+
urlparse.uses_netloc.append("postgis")
|
|
48
|
+
urlparse.uses_netloc.append("mysql")
|
|
49
|
+
urlparse.uses_netloc.append("mysql2")
|
|
50
|
+
urlparse.uses_netloc.append("mysqlgis")
|
|
51
|
+
urlparse.uses_netloc.append("mssql")
|
|
52
|
+
urlparse.uses_netloc.append("sqlite")
|
|
53
|
+
|
|
54
|
+
DRIVERS_MAP = {
|
|
55
|
+
"postgres": "postgres",
|
|
56
|
+
"postgresql": "postgres",
|
|
57
|
+
"pgsql": "postgres",
|
|
58
|
+
"postgis": "postgres",
|
|
59
|
+
"mysql": "mysql",
|
|
60
|
+
"mysql2": "mysql",
|
|
61
|
+
"mysqlgis": "mysql",
|
|
62
|
+
"mysql-connector": "mysql",
|
|
63
|
+
"mssql": "mssql",
|
|
64
|
+
"sqlite": "sqlite",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# this is a special case, because if we pass this URL into
|
|
68
|
+
# urlparse, urlparse will choke trying to interpret "memory"
|
|
69
|
+
# as a port number
|
|
70
|
+
if url in ["sqlite://:memory:", "sqlite://memory"]:
|
|
71
|
+
driver = DRIVERS_MAP["sqlite"]
|
|
72
|
+
path = ":memory:"
|
|
73
|
+
# otherwise parse the url as normal
|
|
74
|
+
else:
|
|
75
|
+
url = urlparse.urlparse(url)
|
|
76
|
+
# remove query string from path (not parsed for now)
|
|
77
|
+
path = url.path[1:]
|
|
78
|
+
if "?" in path and not url.query:
|
|
79
|
+
path, _ = path.split("?", 2)
|
|
80
|
+
|
|
81
|
+
# if we are using sqlite and we have no path, then assume we
|
|
82
|
+
# want an in-memory database (this is the behaviour of sqlalchemy)
|
|
83
|
+
if url.scheme == "sqlite" and path == "":
|
|
84
|
+
path = ":memory:"
|
|
85
|
+
|
|
86
|
+
# handle postgres percent-encoded paths.
|
|
87
|
+
hostname = url.hostname or ""
|
|
88
|
+
if "%2f" in hostname.lower():
|
|
89
|
+
# Switch to url.netloc to avoid lower cased paths
|
|
90
|
+
hostname = url.netloc
|
|
91
|
+
if "@" in hostname:
|
|
92
|
+
hostname = hostname.rsplit("@", 1)[1]
|
|
93
|
+
if ":" in hostname:
|
|
94
|
+
hostname = hostname.split(":", 1)[0]
|
|
95
|
+
hostname = hostname.replace("%2f", "/").replace("%2F", "/")
|
|
96
|
+
|
|
97
|
+
# lookup specified driver
|
|
98
|
+
driver = DRIVERS_MAP[url.scheme]
|
|
99
|
+
port = (
|
|
100
|
+
str(url.port)
|
|
101
|
+
if url.port and driver in [DRIVERS_MAP["mssql"]]
|
|
102
|
+
else url.port
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# build final configuration
|
|
106
|
+
config = {
|
|
107
|
+
"driver": driver,
|
|
108
|
+
"database": urlparse.unquote(path or ""),
|
|
109
|
+
"prefix": prefix,
|
|
110
|
+
"options": options,
|
|
111
|
+
"log_queries": log_queries,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if driver != DRIVERS_MAP["sqlite"]:
|
|
115
|
+
config.update(
|
|
116
|
+
{
|
|
117
|
+
"user": urlparse.unquote(url.username or ""),
|
|
118
|
+
"password": urlparse.unquote(url.password or ""),
|
|
119
|
+
"host": hostname,
|
|
120
|
+
"port": port or "",
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
return config
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from timeit import default_timer as timer
|
|
3
|
+
|
|
4
|
+
from .ConnectionResolver import ConnectionResolver
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseConnection:
|
|
8
|
+
_connection = None
|
|
9
|
+
_cursor = None
|
|
10
|
+
_dry = False
|
|
11
|
+
|
|
12
|
+
def dry(self):
|
|
13
|
+
self._dry = True
|
|
14
|
+
return self
|
|
15
|
+
|
|
16
|
+
def set_schema(self, schema):
|
|
17
|
+
self.schema = schema
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
def log(
|
|
21
|
+
self,
|
|
22
|
+
query,
|
|
23
|
+
bindings,
|
|
24
|
+
query_time=0,
|
|
25
|
+
logger="masoniteorm.connections.queries",
|
|
26
|
+
):
|
|
27
|
+
logger = logging.getLogger("masoniteorm.connection.queries")
|
|
28
|
+
logger.propagate = self.full_details.get("propagate", True)
|
|
29
|
+
|
|
30
|
+
logger.debug(
|
|
31
|
+
f"Running query {query}, {bindings}. Executed in {query_time}ms",
|
|
32
|
+
extra={
|
|
33
|
+
"query": query,
|
|
34
|
+
"bindings": bindings,
|
|
35
|
+
"query_time": query_time,
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def statement(self, query, bindings=()):
|
|
40
|
+
"""Wrapper around calling the cursor query. Helpful for logging output.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
query (string): The query to execute on the cursor
|
|
44
|
+
bindings (tuple, optional): Tuple of query bindings. Defaults to ().
|
|
45
|
+
"""
|
|
46
|
+
start = timer()
|
|
47
|
+
if not self._cursor:
|
|
48
|
+
raise AttributeError(
|
|
49
|
+
f"Must set the _cursor attribute on the {self.__class__.__name__} class before calling the 'statement' method."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self._cursor.execute(query, bindings)
|
|
53
|
+
end = "{:.2f}".format(timer() - start)
|
|
54
|
+
|
|
55
|
+
if self.full_details and self.full_details.get("log_queries", False):
|
|
56
|
+
self.log(query, bindings, query_time=end)
|
|
57
|
+
|
|
58
|
+
def has_global_connection(self):
|
|
59
|
+
return self.name in ConnectionResolver().get_global_connections()
|
|
60
|
+
|
|
61
|
+
def get_global_connection(self):
|
|
62
|
+
return ConnectionResolver().get_global_connections()[self.name]
|
|
63
|
+
|
|
64
|
+
def enable_query_log(self):
|
|
65
|
+
self.full_details["log_queries"] = True
|
|
66
|
+
|
|
67
|
+
def disable_query_log(self):
|
|
68
|
+
self.full_details["log_queries"] = False
|
|
69
|
+
|
|
70
|
+
def format_cursor_results(self, cursor_result):
|
|
71
|
+
return cursor_result
|
|
72
|
+
|
|
73
|
+
def set_cursor(self):
|
|
74
|
+
self._cursor = self._connection.cursor()
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def select_many(self, query, bindings, amount):
|
|
78
|
+
self.set_cursor()
|
|
79
|
+
self.statement(query)
|
|
80
|
+
if not self.open:
|
|
81
|
+
self.make_connection()
|
|
82
|
+
|
|
83
|
+
result = self.format_cursor_results(self._cursor.fetchmany(amount))
|
|
84
|
+
while result:
|
|
85
|
+
yield result
|
|
86
|
+
|
|
87
|
+
result = self.format_cursor_results(self._cursor.fetchmany(amount))
|
|
88
|
+
|
|
89
|
+
def get_row_count(self):
|
|
90
|
+
return self._cursor.rowcount
|
|
91
|
+
|
|
92
|
+
def enable_disable_foreign_keys(self):
|
|
93
|
+
foreign_keys = self.full_details.get("foreign_keys")
|
|
94
|
+
platform = self.get_default_platform()()
|
|
95
|
+
|
|
96
|
+
if foreign_keys:
|
|
97
|
+
self._connection.execute(platform.enable_foreign_key_constraints())
|
|
98
|
+
elif foreign_keys is not None:
|
|
99
|
+
self._connection.execute(
|
|
100
|
+
platform.disable_foreign_key_constraints()
|
|
101
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from ..config import load_config
|
|
2
|
+
from .ConnectionResolver import ConnectionResolver
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ConnectionFactory:
|
|
6
|
+
"""Class for controlling the registration and creation of connection types."""
|
|
7
|
+
|
|
8
|
+
_connections = {}
|
|
9
|
+
|
|
10
|
+
def __init__(self, config_path=None, resolver=None):
|
|
11
|
+
self.config_path = config_path
|
|
12
|
+
self._resolver: ConnectionResolver = resolver
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def register(cls, key, connection):
|
|
16
|
+
"""Registers new connections
|
|
17
|
+
|
|
18
|
+
Arguments:
|
|
19
|
+
key {key} -- The key or driver name you want assigned to this connection
|
|
20
|
+
connection {masoniteorm.connections.BaseConnection} -- An instance of a BaseConnection class.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
cls
|
|
24
|
+
"""
|
|
25
|
+
cls._connections.update({key: connection})
|
|
26
|
+
return cls
|
|
27
|
+
|
|
28
|
+
def make(self, key):
|
|
29
|
+
"""Makes already registered connections
|
|
30
|
+
|
|
31
|
+
Arguments:
|
|
32
|
+
key {string} -- The name of the connection you want to make
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
Exception: Raises exception if there are no driver keys that match
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
masoniteorm.connection.BaseConnection -- Returns an instance of a BaseConnection class.
|
|
39
|
+
"""
|
|
40
|
+
if not self._resolver:
|
|
41
|
+
self._resolver = load_config(config_path=self.config_path).DB
|
|
42
|
+
|
|
43
|
+
connections = self._resolver.get_connection_details()
|
|
44
|
+
if key == "default":
|
|
45
|
+
connection_details = connections.get(connections.get("default"))
|
|
46
|
+
connection = self._connections.get(
|
|
47
|
+
connection_details.get("driver")
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
connection = self._connections.get(key)
|
|
51
|
+
|
|
52
|
+
if connection:
|
|
53
|
+
return connection
|
|
54
|
+
|
|
55
|
+
raise Exception(
|
|
56
|
+
"The '{connection}' connection does not exist".format(
|
|
57
|
+
connection=key
|
|
58
|
+
)
|
|
59
|
+
)
|