datasette-edit-schema 0.8a1__tar.gz → 0.8a2__tar.gz
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.
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/PKG-INFO +1 -1
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/__init__.py +19 -14
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_database.html +1 -1
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_table.html +8 -8
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/PKG-INFO +1 -1
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/pyproject.toml +1 -1
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/tests/test_edit_schema.py +50 -9
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/LICENSE +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/README.md +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/static/draggable.1.0.0-beta.11.bundle.js +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/static/draggable.1.0.0-beta.11.bundle.min.js +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_create_table.html +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_index.html +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/utils.py +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/SOURCES.txt +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/dependency_links.txt +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/entry_points.txt +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/requires.txt +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/top_level.txt +0 -0
- {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/setup.cfg +0 -0
{datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a2}/datasette_edit_schema/__init__.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from datasette import hookimpl
|
|
2
2
|
from datasette.events import CreateTableEvent, AlterTableEvent, DropTableEvent
|
|
3
3
|
from datasette.utils.asgi import Response, NotFound, Forbidden
|
|
4
|
-
from datasette.utils import sqlite3
|
|
4
|
+
from datasette.utils import sqlite3, tilde_decode, tilde_encode
|
|
5
5
|
from urllib.parse import quote_plus, unquote_plus
|
|
6
6
|
import sqlite_utils
|
|
7
7
|
import textwrap
|
|
@@ -40,7 +40,7 @@ def table_actions(datasette, actor, database, table):
|
|
|
40
40
|
return [
|
|
41
41
|
{
|
|
42
42
|
"href": datasette.urls.path(
|
|
43
|
-
"/-/edit-schema/{}/{}".format(database,
|
|
43
|
+
"/-/edit-schema/{}/{}".format(database, tilde_encode(table))
|
|
44
44
|
),
|
|
45
45
|
"label": "Edit table schema",
|
|
46
46
|
"description": "Rename the table, add and remove columns...",
|
|
@@ -216,6 +216,7 @@ async def edit_schema_database(request, datasette):
|
|
|
216
216
|
{
|
|
217
217
|
"database": database,
|
|
218
218
|
"tables": tables,
|
|
219
|
+
"tilde_encode": tilde_encode,
|
|
219
220
|
},
|
|
220
221
|
request=request,
|
|
221
222
|
)
|
|
@@ -307,7 +308,7 @@ async def edit_schema_create_table(request, datasette):
|
|
|
307
308
|
|
|
308
309
|
|
|
309
310
|
async def edit_schema_table(request, datasette):
|
|
310
|
-
table =
|
|
311
|
+
table = tilde_decode(request.url_vars["table"])
|
|
311
312
|
databases = get_databases(datasette)
|
|
312
313
|
database_name = request.url_vars["database"]
|
|
313
314
|
|
|
@@ -548,16 +549,14 @@ async def edit_schema_table(request, datasette):
|
|
|
548
549
|
info["foreign_key"].other_column,
|
|
549
550
|
),
|
|
550
551
|
"value": "{}.{}".format(
|
|
551
|
-
info["foreign_key"].other_table,
|
|
552
|
-
info["foreign_key"].other_column,
|
|
552
|
+
tilde_encode(info["foreign_key"].other_table),
|
|
553
|
+
tilde_encode(info["foreign_key"].other_column),
|
|
553
554
|
),
|
|
554
555
|
"selected": True,
|
|
555
556
|
}
|
|
556
557
|
)
|
|
557
558
|
seen.add(
|
|
558
|
-
"
|
|
559
|
-
info["foreign_key"].other_table, info["foreign_key"].other_column
|
|
560
|
-
)
|
|
559
|
+
(info["foreign_key"].other_table, info["foreign_key"].other_column)
|
|
561
560
|
)
|
|
562
561
|
# Now add suggestions
|
|
563
562
|
for suggested_table, suggested_column in info["suggestions"]:
|
|
@@ -570,19 +569,24 @@ async def edit_schema_table(request, datasette):
|
|
|
570
569
|
"name": "{}.{} (suggested)".format(
|
|
571
570
|
suggested_table, suggested_column
|
|
572
571
|
),
|
|
573
|
-
"value": "{}.{}".format(
|
|
572
|
+
"value": "{}.{}".format(
|
|
573
|
+
tilde_encode(suggested_table),
|
|
574
|
+
tilde_encode(suggested_column),
|
|
575
|
+
),
|
|
574
576
|
"selected": False,
|
|
575
577
|
}
|
|
576
578
|
)
|
|
577
|
-
seen.add(
|
|
579
|
+
seen.add((suggested_table, suggested_column))
|
|
578
580
|
info["suggested"] = "{}.{}".format(suggested_table, suggested_column)
|
|
579
581
|
# And the rest
|
|
580
582
|
for rest_table, rest_column in info["options"]:
|
|
581
|
-
if
|
|
583
|
+
if (rest_table, rest_column) not in seen:
|
|
582
584
|
options.append(
|
|
583
585
|
{
|
|
584
586
|
"name": "{}.{}".format(rest_table, rest_column),
|
|
585
|
-
"value": "{}.{}".format(
|
|
587
|
+
"value": "{}.{}".format(
|
|
588
|
+
tilde_encode(rest_table), tilde_encode(rest_column)
|
|
589
|
+
),
|
|
586
590
|
"selected": False,
|
|
587
591
|
}
|
|
588
592
|
)
|
|
@@ -619,6 +623,7 @@ async def edit_schema_table(request, datasette):
|
|
|
619
623
|
"can_rename_table": await can_rename_table(
|
|
620
624
|
datasette, request.actor, database_name, table
|
|
621
625
|
),
|
|
626
|
+
"tilde_encode": tilde_encode,
|
|
622
627
|
},
|
|
623
628
|
request=request,
|
|
624
629
|
)
|
|
@@ -782,8 +787,8 @@ async def update_foreign_keys(request, datasette, database, table, formdata):
|
|
|
782
787
|
fks.append(
|
|
783
788
|
(
|
|
784
789
|
column,
|
|
785
|
-
split[0],
|
|
786
|
-
split[1],
|
|
790
|
+
tilde_decode(split[0]),
|
|
791
|
+
tilde_decode(split[1]),
|
|
787
792
|
)
|
|
788
793
|
)
|
|
789
794
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<h1>Edit tables in {{ database.name }}.db</h1>
|
|
11
11
|
|
|
12
12
|
{% for table in tables %}
|
|
13
|
-
<h2><a href="/-/edit-schema/{{ database.name|quote_plus }}/{{ table.name
|
|
13
|
+
<h2><a href="/-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table.name) }}">{{ table.name }}</a></h2>
|
|
14
14
|
<p>{% for column in table.columns %}{{ column.name }}{% if not loop.last %}, {% endif %}{% endfor %}</p>
|
|
15
15
|
{% endfor %}
|
|
16
16
|
|
|
@@ -86,12 +86,12 @@ html body label {
|
|
|
86
86
|
{% endblock %}
|
|
87
87
|
|
|
88
88
|
{% block content %}
|
|
89
|
-
<h1>Edit table <a href="{{ base_url }}{{ database.name|quote_plus }}/{{ table
|
|
89
|
+
<h1>Edit table <a href="{{ base_url }}{{ database.name|quote_plus }}/{{ tilde_encode(table) }}">{{ database.name }}/{{ table }}</a></h1>
|
|
90
90
|
|
|
91
91
|
{% if can_rename_table %}
|
|
92
92
|
<h2>Rename table</h2>
|
|
93
93
|
|
|
94
|
-
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
94
|
+
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
95
95
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
96
96
|
<p><label>New name <input type="text" name="name"></label>
|
|
97
97
|
<input type="hidden" name="rename_table" value="1">
|
|
@@ -99,7 +99,7 @@ html body label {
|
|
|
99
99
|
</form>
|
|
100
100
|
{% endif %}
|
|
101
101
|
|
|
102
|
-
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
102
|
+
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
103
103
|
<h2>Change existing columns</h2>
|
|
104
104
|
<ul class="sortable-columns">
|
|
105
105
|
{% for column in columns %}
|
|
@@ -134,7 +134,7 @@ html body label {
|
|
|
134
134
|
|
|
135
135
|
<h2>Add a column</h2>
|
|
136
136
|
|
|
137
|
-
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
137
|
+
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
138
138
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
139
139
|
<input type="hidden" name="add_column" value="1">
|
|
140
140
|
<p><label>Name <input type="text" name="name"></label>
|
|
@@ -156,7 +156,7 @@ table.foreign-key-options td {
|
|
|
156
156
|
}
|
|
157
157
|
</style>
|
|
158
158
|
|
|
159
|
-
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
159
|
+
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
160
160
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
161
161
|
<input type="hidden" name="action" value="update_foreign_keys">
|
|
162
162
|
<table class="foreign-key-options">
|
|
@@ -180,7 +180,7 @@ table.foreign-key-options td {
|
|
|
180
180
|
|
|
181
181
|
<p>The primary key column uniquely identifies each row in the table.</p>
|
|
182
182
|
|
|
183
|
-
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
183
|
+
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
184
184
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
185
185
|
<input type="hidden" name="action" value="update_primary_key">
|
|
186
186
|
<label for="primary_key">Primary key column </label>
|
|
@@ -201,7 +201,7 @@ table.foreign-key-options td {
|
|
|
201
201
|
|
|
202
202
|
<p>Indexes can speed up filter and sort operations against indexed columns.</p>
|
|
203
203
|
|
|
204
|
-
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
204
|
+
<form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
205
205
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
206
206
|
{% if non_primary_key_columns %}
|
|
207
207
|
<p><label for="id_add_index_column">
|
|
@@ -233,7 +233,7 @@ table.foreign-key-options td {
|
|
|
233
233
|
{% if can_drop_table %}
|
|
234
234
|
<h2>Drop table</h2>
|
|
235
235
|
|
|
236
|
-
<form id="drop-table-form" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table
|
|
236
|
+
<form id="drop-table-form" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
|
|
237
237
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
238
238
|
<input type="hidden" name="drop_table" value="1">
|
|
239
239
|
<input type="submit" class="button-red" value="Drop this table">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datasette.app import Datasette
|
|
2
|
+
from datasette.utils import tilde_encode
|
|
2
3
|
from datasette_edit_schema.utils import (
|
|
3
4
|
potential_foreign_keys,
|
|
4
5
|
get_primary_keys,
|
|
@@ -41,8 +42,9 @@ async def test_csrf_required(db_path):
|
|
|
41
42
|
("user_with_no_perms", False),
|
|
42
43
|
),
|
|
43
44
|
)
|
|
45
|
+
@pytest.mark.parametrize("table", ("creatures", "animal.name/with/slashes"))
|
|
44
46
|
@pytest.mark.asyncio
|
|
45
|
-
async def test_table_actions(permission_plugin, ds, actor_id, should_allow):
|
|
47
|
+
async def test_table_actions(permission_plugin, ds, actor_id, should_allow, table):
|
|
46
48
|
ds._rules_allow = [
|
|
47
49
|
Rule(
|
|
48
50
|
actor_id="user_with_edit_schema",
|
|
@@ -60,9 +62,13 @@ async def test_table_actions(permission_plugin, ds, actor_id, should_allow):
|
|
|
60
62
|
cookies = None
|
|
61
63
|
if actor_id:
|
|
62
64
|
cookies = {"ds_actor": ds.sign({"a": {"id": actor_id}}, "actor")}
|
|
63
|
-
response = await ds.client.get(
|
|
65
|
+
response = await ds.client.get(
|
|
66
|
+
ds.urls.table(database="data", table=table), cookies=cookies
|
|
67
|
+
)
|
|
64
68
|
assert response.status_code == 200
|
|
65
|
-
fragment = '<a href="/-/edit-schema/data/
|
|
69
|
+
fragment = '<a href="/-/edit-schema/data/{}">Edit table schema'.format(
|
|
70
|
+
tilde_encode(table)
|
|
71
|
+
)
|
|
66
72
|
if should_allow:
|
|
67
73
|
# Should have table action
|
|
68
74
|
assert fragment in response.text
|
|
@@ -814,6 +820,24 @@ async def test_edit_form_for_empty_table(db_path):
|
|
|
814
820
|
["id"],
|
|
815
821
|
"Foreign keys updated to distraction_id → cities.id",
|
|
816
822
|
),
|
|
823
|
+
# Same again for tables with weird characters in their names
|
|
824
|
+
(
|
|
825
|
+
"animal.name/with/slashes",
|
|
826
|
+
{
|
|
827
|
+
"action": "update_foreign_keys",
|
|
828
|
+
"fk.species": "table~2Ename~2Fwith~2Fslashes~2Ecategories.id",
|
|
829
|
+
},
|
|
830
|
+
[
|
|
831
|
+
(
|
|
832
|
+
"animal.name/with/slashes",
|
|
833
|
+
"species",
|
|
834
|
+
"table.name/with/slashes.categories",
|
|
835
|
+
"id",
|
|
836
|
+
)
|
|
837
|
+
],
|
|
838
|
+
["id"],
|
|
839
|
+
"Foreign keys updated to species → table.name/with/slashes.categories.id",
|
|
840
|
+
),
|
|
817
841
|
# Change primary key in a way that works
|
|
818
842
|
(
|
|
819
843
|
"museums",
|
|
@@ -839,13 +863,13 @@ async def test_edit_keys(
|
|
|
839
863
|
# Grab a csrftoken
|
|
840
864
|
cookies = {"ds_actor": ds.sign({"a": {"id": "root"}}, "actor")}
|
|
841
865
|
csrftoken_r = await ds.client.get(
|
|
842
|
-
"/-/edit-schema/data/{}".format(table), cookies=cookies
|
|
866
|
+
"/-/edit-schema/data/{}".format(tilde_encode(table)), cookies=cookies
|
|
843
867
|
)
|
|
844
868
|
csrftoken = csrftoken_r.cookies["ds_csrftoken"]
|
|
845
869
|
cookies["ds_csrftoken"] = csrftoken
|
|
846
870
|
post_data["csrftoken"] = csrftoken
|
|
847
871
|
response = await ds.client.post(
|
|
848
|
-
"/-/edit-schema/data/{}".format(table),
|
|
872
|
+
"/-/edit-schema/data/{}".format(tilde_encode(table)),
|
|
849
873
|
data=post_data,
|
|
850
874
|
cookies=cookies,
|
|
851
875
|
)
|
|
@@ -1065,6 +1089,19 @@ def test_potential_primary_keys_primary_key_only_table():
|
|
|
1065
1089
|
"Index dropped: name_unique_index",
|
|
1066
1090
|
[{"columns": ["name"], "name": "name_index", "unique": 0}],
|
|
1067
1091
|
),
|
|
1092
|
+
# Test for table with surprising characters in its name
|
|
1093
|
+
(
|
|
1094
|
+
"animal.name/with/slashes",
|
|
1095
|
+
{"add_index": "1", "add_index_column": "species"},
|
|
1096
|
+
"Index added on species",
|
|
1097
|
+
[
|
|
1098
|
+
{
|
|
1099
|
+
"name": "idx_animal.name/with/slashes_species",
|
|
1100
|
+
"columns": ["species"],
|
|
1101
|
+
"unique": 0,
|
|
1102
|
+
}
|
|
1103
|
+
],
|
|
1104
|
+
),
|
|
1068
1105
|
),
|
|
1069
1106
|
)
|
|
1070
1107
|
async def test_add_remove_index(
|
|
@@ -1072,13 +1109,17 @@ async def test_add_remove_index(
|
|
|
1072
1109
|
):
|
|
1073
1110
|
ds = Datasette([db_path])
|
|
1074
1111
|
cookies = {"ds_actor": ds.sign({"a": {"id": "root"}}, "actor")}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
)
|
|
1112
|
+
get_response = await ds.client.get(
|
|
1113
|
+
"/-/edit-schema/data/{}".format(tilde_encode(table)), cookies=cookies
|
|
1114
|
+
)
|
|
1115
|
+
assert get_response.status_code == 200
|
|
1116
|
+
csrftoken = get_response.cookies["ds_csrftoken"]
|
|
1078
1117
|
cookies["ds_csrftoken"] = csrftoken
|
|
1079
1118
|
post_data["csrftoken"] = csrftoken
|
|
1080
1119
|
response = await ds.client.post(
|
|
1081
|
-
"/-/edit-schema/data/{}".format(table),
|
|
1120
|
+
"/-/edit-schema/data/{}".format(tilde_encode(table)),
|
|
1121
|
+
cookies=cookies,
|
|
1122
|
+
data=post_data,
|
|
1082
1123
|
)
|
|
1083
1124
|
assert response.status_code == 302
|
|
1084
1125
|
messages = ds.unsign(response.cookies["ds_messages"], "messages")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|