Skip to content

Commit 4fc6b90

Browse files
committed
Merge pull request #26 from Brobin/dependency_sorting
Dependency sorting
2 parents 67db4e8 + 21c0f48 commit 4fc6b90

File tree

4 files changed

+102
-7
lines changed

4 files changed

+102
-7
lines changed

django_seed/guessers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ def guess_format(self, field):
101101
return lambda x: provider.comma_sep_ints()
102102

103103
if isinstance(field, BinaryField): return lambda x: provider.binary()
104-
if isinstance(field, ImageField): return lambda x: None
104+
if isinstance(field, ImageField): return lambda x: provider.file_name()
105105
if isinstance(field, FilePathField): return lambda x: provider.file_name()
106-
if isinstance(field, FileField): return lambda x: None
106+
if isinstance(field, FileField): return lambda x: provider.file_name()
107107

108108
if isinstance(field, CharField):
109109
if field.choices:

django_seed/management/commands/seed.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11

22
from django.core.management.base import AppCommand
3+
from django.db.models import ForeignKey
34
from django_seed import Seed
45
from django_seed.exceptions import SeederCommandError
6+
from django_seed.toposort import toposort_flatten
57
from optparse import make_option
68
import django
79

@@ -11,10 +13,10 @@ class Command(AppCommand):
1113

1214
args = "[appname ...]"
1315

14-
option_list = AppCommand.option_list + (
16+
option_list = [
1517
make_option('--number', dest='number', default=10,
1618
help='number of each model to seed'),
17-
)
19+
]
1820

1921
def handle_app_config(self, app_config, **options):
2022
if app_config.models_module is None:
@@ -27,10 +29,31 @@ def handle_app_config(self, app_config, **options):
2729

2830
seeder = Seed.seeder()
2931

30-
for model in app_config.get_models():
32+
for model in self.sorted_models(app_config):
3133
seeder.add_entity(model, number)
3234
print('Seeding %i %ss' % (number, model.__name__))
3335

3436
pks = seeder.execute()
3537
print(pks)
3638

39+
def dependencies(self, model):
40+
dependencies = set()
41+
if hasattr(model._meta, 'get_fields'): # Django>=1.8
42+
for field in model._meta.get_fields():
43+
if field.many_to_one is True and field.concrete and field.blank is False:
44+
dependencies.add(field.related_model)
45+
else: # Django<=1.7
46+
for field in model._meta.fields:
47+
if isinstance(field, ForeignKey) and field.blank is False:
48+
dependencies.add(field.rel.to)
49+
return dependencies
50+
51+
def sorted_models(self, app_config):
52+
dependencies = {}
53+
for model in app_config.get_models():
54+
dependencies[model] = self.dependencies(model)
55+
try:
56+
return toposort_flatten(dependencies)
57+
except ValueError as ex:
58+
raise SeederCommandError(str(ex))
59+

django_seed/providers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88

99

10-
file_extenstions = ( "flac", "mp3", "wav", "bmp", "gif", "jpeg", "jpg", "png",
10+
file_extensions = ( "flac", "mp3", "wav", "bmp", "gif", "jpeg", "jpg", "png",
1111
"tiff", "css", "csv", "html", "js", "json", "txt", "mp4",
1212
"avi", "mov", "webm" )
1313

@@ -45,7 +45,7 @@ def rand_float(self):
4545

4646
def file_name(self):
4747
filename = self.faker.word()
48-
extension = random.choice(file_extenstions)
48+
extension = random.choice(file_extensions)
4949
return '{0}.{1}'.format(filename, extension)
5050

5151
def comma_sep_ints(self):

django_seed/toposort.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#######################################################################
2+
# Implements a topological sort algorithm.
3+
# https://bitbucket.org/ericvsmith/toposort/src/25b5894c4229cb888f77cf0c077c05e2464446ac/toposort.py
4+
#
5+
# Copyright 2014 True Blade Systems, Inc.
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
#
19+
########################################################################
20+
21+
from functools import reduce as _reduce
22+
23+
24+
__all__ = ['toposort', 'toposort_flatten']
25+
26+
27+
def toposort(data):
28+
"""Dependencies are expressed as a dictionary whose keys are items
29+
and whose values are a set of dependent items. Output is a list of
30+
sets in topological order. The first set consists of items with no
31+
dependences, each subsequent set consists of items that depend upon
32+
items in the preceeding sets.
33+
"""
34+
35+
# Special case empty input.
36+
if len(data) == 0:
37+
return
38+
39+
# Copy the input so as to leave it unmodified.
40+
data = data.copy()
41+
42+
# Ignore self dependencies.
43+
for k, v in data.items():
44+
v.discard(k)
45+
# Find all items that don't depend on anything.
46+
extra_items_in_deps = _reduce(set.union, data.values()) - set(data.keys())
47+
# Add empty dependences where needed.
48+
data.update({item:set() for item in extra_items_in_deps})
49+
while True:
50+
ordered = set(item for item, dep in data.items() if len(dep) == 0)
51+
if not ordered:
52+
break
53+
yield ordered
54+
data = {item: (dep - ordered)
55+
for item, dep in data.items()
56+
if item not in ordered}
57+
if len(data) != 0:
58+
raise ValueError('Cyclic dependencies exist among these items: {}'.format(', '.join(repr(x) for x in data.items())))
59+
60+
61+
def toposort_flatten(data, sort=True):
62+
"""Returns a single list of dependencies. For any set returned by
63+
toposort(), those items are sorted and appended to the result (just to
64+
make the results deterministic)."""
65+
66+
result = []
67+
for d in toposort(data):
68+
try:
69+
result.extend((sorted if sort else list)(d))
70+
except TypeError as e:
71+
result.extend(list(d))
72+
return result

0 commit comments

Comments
 (0)