Skip to content

Commit

Permalink
Merge pull request #26 from Brobin/dependency_sorting
Browse files Browse the repository at this point in the history
Dependency sorting
  • Loading branch information
Brobin committed Nov 18, 2015
2 parents 67db4e8 + 21c0f48 commit 4fc6b90
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 7 deletions.
4 changes: 2 additions & 2 deletions django_seed/guessers.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ def guess_format(self, field):
return lambda x: provider.comma_sep_ints()

if isinstance(field, BinaryField): return lambda x: provider.binary()
if isinstance(field, ImageField): return lambda x: None
if isinstance(field, ImageField): return lambda x: provider.file_name()
if isinstance(field, FilePathField): return lambda x: provider.file_name()
if isinstance(field, FileField): return lambda x: None
if isinstance(field, FileField): return lambda x: provider.file_name()

if isinstance(field, CharField):
if field.choices:
Expand Down
29 changes: 26 additions & 3 deletions django_seed/management/commands/seed.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@

from django.core.management.base import AppCommand
from django.db.models import ForeignKey
from django_seed import Seed
from django_seed.exceptions import SeederCommandError
from django_seed.toposort import toposort_flatten
from optparse import make_option
import django

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

args = "[appname ...]"

option_list = AppCommand.option_list + (
option_list = [
make_option('--number', dest='number', default=10,
help='number of each model to seed'),
)
]

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

seeder = Seed.seeder()

for model in app_config.get_models():
for model in self.sorted_models(app_config):
seeder.add_entity(model, number)
print('Seeding %i %ss' % (number, model.__name__))

pks = seeder.execute()
print(pks)

def dependencies(self, model):
dependencies = set()
if hasattr(model._meta, 'get_fields'): # Django>=1.8
for field in model._meta.get_fields():
if field.many_to_one is True and field.concrete and field.blank is False:
dependencies.add(field.related_model)
else: # Django<=1.7
for field in model._meta.fields:
if isinstance(field, ForeignKey) and field.blank is False:
dependencies.add(field.rel.to)
return dependencies

def sorted_models(self, app_config):
dependencies = {}
for model in app_config.get_models():
dependencies[model] = self.dependencies(model)
try:
return toposort_flatten(dependencies)
except ValueError as ex:
raise SeederCommandError(str(ex))

4 changes: 2 additions & 2 deletions django_seed/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys


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

Expand Down Expand Up @@ -45,7 +45,7 @@ def rand_float(self):

def file_name(self):
filename = self.faker.word()
extension = random.choice(file_extenstions)
extension = random.choice(file_extensions)
return '{0}.{1}'.format(filename, extension)

def comma_sep_ints(self):
Expand Down
72 changes: 72 additions & 0 deletions django_seed/toposort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#######################################################################
# Implements a topological sort algorithm.
# https://bitbucket.org/ericvsmith/toposort/src/25b5894c4229cb888f77cf0c077c05e2464446ac/toposort.py
#
# Copyright 2014 True Blade Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
########################################################################

from functools import reduce as _reduce


__all__ = ['toposort', 'toposort_flatten']


def toposort(data):
"""Dependencies are expressed as a dictionary whose keys are items
and whose values are a set of dependent items. Output is a list of
sets in topological order. The first set consists of items with no
dependences, each subsequent set consists of items that depend upon
items in the preceeding sets.
"""

# Special case empty input.
if len(data) == 0:
return

# Copy the input so as to leave it unmodified.
data = data.copy()

# Ignore self dependencies.
for k, v in data.items():
v.discard(k)
# Find all items that don't depend on anything.
extra_items_in_deps = _reduce(set.union, data.values()) - set(data.keys())
# Add empty dependences where needed.
data.update({item:set() for item in extra_items_in_deps})
while True:
ordered = set(item for item, dep in data.items() if len(dep) == 0)
if not ordered:
break
yield ordered
data = {item: (dep - ordered)
for item, dep in data.items()
if item not in ordered}
if len(data) != 0:
raise ValueError('Cyclic dependencies exist among these items: {}'.format(', '.join(repr(x) for x in data.items())))


def toposort_flatten(data, sort=True):
"""Returns a single list of dependencies. For any set returned by
toposort(), those items are sorted and appended to the result (just to
make the results deterministic)."""

result = []
for d in toposort(data):
try:
result.extend((sorted if sort else list)(d))
except TypeError as e:
result.extend(list(d))
return result

0 comments on commit 4fc6b90

Please sign in to comment.