0001-general-add-basic-export-import-of-named-assets-2493.patch
combo/apps/assets/models.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import json |
|
18 | ||
19 |
from django.core import serializers |
|
17 | 20 |
from django.db import models |
18 | 21 | |
22 |
class AssetManager(models.Manager): |
|
23 |
def get_by_natural_key(self, key): |
|
24 |
return self.get(key=key) |
|
25 | ||
26 | ||
19 | 27 |
class Asset(models.Model): |
28 |
objects = AssetManager() |
|
29 | ||
20 | 30 |
key = models.CharField(max_length=128, unique=True) |
21 | 31 |
asset = models.FileField(upload_to='assets') |
32 | ||
33 |
@classmethod |
|
34 |
def export_all_for_json(cls): |
|
35 |
return [x.get_as_serialized_object() for x in Asset.objects.all()] |
|
36 | ||
37 |
def get_as_serialized_object(self): |
|
38 |
serialized_asset = json.loads(serializers.serialize('json', [self], |
|
39 |
use_natural_foreign_keys=True, use_natural_primary_keys=True))[0] |
|
40 |
del serialized_asset['model'] |
|
41 |
del serialized_asset['pk'] |
|
42 |
return serialized_asset |
|
43 | ||
44 |
@classmethod |
|
45 |
def load_serialized_objects(cls, json_site): |
|
46 |
for json_asset in json_site: |
|
47 |
cls.load_serialized_object(json_asset) |
|
48 | ||
49 |
@classmethod |
|
50 |
def load_serialized_object(cls, json_asset): |
|
51 |
json_asset['model'] = 'assets.asset' |
|
52 |
asset, created = Asset.objects.get_or_create(key=json_asset['fields']['key']) |
|
53 |
json_asset['pk'] = asset.id |
|
54 |
asset = [x for x in serializers.deserialize('json', json.dumps([json_asset]))][0] |
|
55 |
asset.save() |
combo/data/utils.py | ||
---|---|---|
18 | 18 |
from django.db import transaction |
19 | 19 |
from django.utils.translation import ugettext_lazy as _ |
20 | 20 | |
21 |
from combo.apps.assets.models import Asset |
|
21 | 22 |
from combo.apps.maps.models import MapLayer |
22 | 23 |
from .models import Page |
23 | 24 | |
... | ... | |
33 | 34 |
def export_site(): |
34 | 35 |
'''Dump site objects to JSON-dumpable dictionnary''' |
35 | 36 |
return {'pages': Page.export_all_for_json(), |
36 |
'map-layers': MapLayer.export_all_for_json()} |
|
37 |
'map-layers': MapLayer.export_all_for_json(), |
|
38 |
'assets': Asset.export_all_for_json(),} |
|
37 | 39 | |
38 | 40 | |
39 | 41 |
def import_site(data, if_empty=False, clean=False): |
... | ... | |
61 | 63 | |
62 | 64 |
if clean: |
63 | 65 |
MapLayer.objects.all().delete() |
66 |
Asset.objects.all().delete() |
|
64 | 67 |
Page.objects.all().delete() |
65 | 68 | |
66 | 69 |
with transaction.atomic(): |
67 | 70 |
MapLayer.load_serialized_objects(data.get('map-layers') or []) |
68 | 71 | |
72 |
with transaction.atomic(): |
|
73 |
Asset.load_serialized_objects(data.get('assets') or []) |
|
74 | ||
69 | 75 |
with transaction.atomic(): |
70 | 76 |
Page.load_serialized_pages(data.get('pages') or []) |
tests/test_import_export.py | ||
---|---|---|
8 | 8 | |
9 | 9 |
import pytest |
10 | 10 |
from django.contrib.auth.models import Group |
11 |
from django.core.files import File |
|
11 | 12 |
from django.core.management import call_command |
12 | 13 | |
14 |
from combo.apps.assets.models import Asset |
|
13 | 15 |
from combo.apps.maps.models import MapLayer, Map |
14 | 16 |
from combo.data.models import Page, TextCell |
15 | 17 |
from combo.data.utils import export_site, import_site, MissingGroups |
... | ... | |
33 | 35 |
MapLayer(label='Foo', slug='foo', geojson_url='http://example.net/foo/').save() |
34 | 36 |
MapLayer(label='Bar', slug='bar', geojson_url='http://example.net/bar/').save() |
35 | 37 | |
38 |
@pytest.fixture |
|
39 |
def some_assets(): |
|
40 |
Asset(key='banner', asset=File(StringIO('test'), 'test.png')).save() |
|
41 |
Asset(key='favicon', asset=File(StringIO('test2'), 'test2.png')).save() |
|
42 | ||
36 | 43 |
def get_output_of_command(command, *args, **kwargs): |
37 | 44 |
old_stdout = sys.stdout |
38 | 45 |
output = sys.stdout = StringIO() |
... | ... | |
162 | 169 | |
163 | 170 |
cell = TextCell.objects.get(order=0) |
164 | 171 |
assert [x.name for x in cell.groups.all()] == ['A Group'] |
172 | ||
173 |
def test_import_export_assets(app, some_assets): |
|
174 |
output = get_output_of_command('export_site') |
|
175 |
assert len(json.loads(output)['assets']) == 2 |
|
176 |
import_site(data={}, clean=True) |
|
177 |
assert Asset.objects.all().count() == 0 |
|
178 |
empty_output = get_output_of_command('export_site') |
|
179 |
assert len(json.loads(empty_output)['assets']) == 0 |
|
180 | ||
181 |
Asset(key='footer', asset=File(StringIO('test3'), 'test3.png')).save() |
|
182 |
old_stdin = sys.stdin |
|
183 |
sys.stdin = StringIO(json.dumps({})) |
|
184 |
assert Asset.objects.count() == 1 |
|
185 |
try: |
|
186 |
call_command('import_site', '-', clean=True) |
|
187 |
finally: |
|
188 |
sys.stdin = old_stdin |
|
189 |
assert Asset.objects.count() == 0 |
|
190 | ||
191 |
with tempfile.NamedTemporaryFile() as f: |
|
192 |
f.write(output) |
|
193 |
f.flush() |
|
194 |
call_command('import_site', f.name) |
|
195 | ||
196 |
assert Asset.objects.count() == 2 |
|
197 | ||
198 |
import_site(data={}, if_empty=True) |
|
199 |
assert Asset.objects.count() == 2 |
|
165 |
- |