Skip to content

Dependency sorting #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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