From 9c99d2f471301781ae86f19344ad72745da9caf2 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 01:44:38 +0000 Subject: [PATCH 01/20] fixes #11 --- django_spaghetti/views.py | 197 ++++++++++++++++++++++---------------- 1 file changed, 117 insertions(+), 80 deletions(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index ec2827b..2e7a400 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -6,95 +6,132 @@ from django.template.loader import get_template from django.template import Context import json +from django.views.generic import View -graph_settings = getattr(settings, 'SPAGHETTI_SAUCE', {}) -apps = graph_settings.get('apps',[]) -def plate(request): - excludes = ['%s__%s'%(app,model) for app,models in graph_settings.get('exclude',{}).items() for model in models ] - models = ContentType.objects.filter(app_label__in=apps) - nodes = [] - edges = [] - for model in models: - if (model.model_class() == None): - continue - model.is_proxy = model.model_class()._meta.proxy - if (model.is_proxy and not graph_settings.get('show_proxy',False)): - continue +class Plate(View): + settings = None + override_settings = {} + template_name = 'django_spaghetti/plate.html' + + def get(self, request): + return self.plate() - model.doc = model.model_class().__doc__ - _id = "%s__%s"%(model.app_label,model.model) - if _id in excludes: - continue - if model.is_proxy: - label = "(P) %s"%(model.model) + def plate(self): + request = self.request + if self.settings is None: + graph_settings = getattr(settings, 'SPAGHETTI_SAUCE', {}) + graph_settings.update(self.override_settings) else: - label = "%s"%(model.model) - fields = [f for f in model.model_class()._meta.fields] - many = [f for f in model.model_class()._meta.many_to_many] - if graph_settings.get('show_fields',True): - label += "\n%s\n"%("-"*len(model.model)) - label += "\n".join([str(f.name) for f in fields]) - edge_color = {'inherit':'from'} + graph_settings = self.settings + + apps = graph_settings.get('apps',[]) + - for f in fields+many: - if f.rel is not None: - m = f.rel.to._meta - if m.app_label != model.app_label: - edge_color = {'inherit':'both'} - edge = { 'from':_id, - 'to':"%s__%s"%(m.app_label,m.model_name), - 'color':edge_color, - } + excludes = ['%s__%s'%(app,model) for app,models in graph_settings.get('exclude',{}).items() for model in models ] + models = ContentType.objects.filter(app_label__in=apps) + nodes = [] + edges = [] + for model in models: + if (model.model_class() == None): + continue + model.is_proxy = model.model_class()._meta.proxy + if (model.is_proxy and not graph_settings.get('show_proxy',False)): + continue + + model.doc = model.model_class().__doc__ + _id = "%s__%s"%(model.app_label,model.model) + if _id in excludes: + continue - if str(f.name).endswith('_ptr'): - #fields that end in _ptr are pointing to a parent object - edge.update({ - 'arrows':{'to':{'scaleFactor':0.75}}, #needed to draw from-to - 'font': {'align': 'middle'}, - 'label':'is a', - 'dashes':True - }) - elif type(f) == related.ForeignKey: - edge.update({ - 'arrows':{'to':{'scaleFactor':0.75}} - }) - elif type(f) == related.OneToOneField: - edge.update({ - 'font': {'align': 'middle'}, - 'label':'|' - }) - elif type(f) == related.ManyToManyField: - edge.update({ - 'color':{'color':'gray'}, - 'arrows':{'to':{'scaleFactor':1}, 'from':{'scaleFactor':1}}, - }) + label = self.get_node_label(model) + fields = [f for f in model.model_class()._meta.fields] + many = [f for f in model.model_class()._meta.many_to_many] + if graph_settings.get('show_fields',True): + label += "\n%s\n"%("-"*len(model.model)) + label += "\n".join([str(f.name) for f in fields]) + edge_color = {'inherit':'from'} + + for f in fields+many: + if f.rel is not None: + m = f.rel.to._meta + if m.app_label != model.app_label: + edge_color = {'inherit':'both'} + edge = { 'from':_id, + 'to':"%s__%s"%(m.app_label,m.model_name), + 'color':edge_color, + } + + if str(f.name).endswith('_ptr'): + #fields that end in _ptr are pointing to a parent object + edge.update({ + 'arrows':{'to':{'scaleFactor':0.75}}, #needed to draw from-to + 'font': {'align': 'middle'}, + 'label':'is a', + 'dashes':True + }) + elif type(f) == related.ForeignKey: + edge.update({ + 'arrows':{'to':{'scaleFactor':0.75}} + }) + elif type(f) == related.OneToOneField: + edge.update({ + 'font': {'align': 'middle'}, + 'label':'|' + }) + elif type(f) == related.ManyToManyField: + edge.update({ + 'color':{'color':'gray'}, + 'arrows':{'to':{'scaleFactor':1}, 'from':{'scaleFactor':1}}, + }) + + edges.append(edge) + if model.is_proxy: + proxy = model.model_class()._meta.proxy_for_model._meta + model.proxy = proxy + edge = { 'to':_id, + 'from':"%s__%s"%(proxy.app_label,proxy.model_name), + 'color':edge_color, + } edges.append(edge) + + nodes.append( + { + 'id':_id, + 'label':label, + 'shape':'box', + 'group':model.app_label, + 'title':get_template("django_spaghetti/meatball.html").render( + Context({'model':model,'fields':fields,}) + ) + + } + ) + + data = { + 'meatballs':json.dumps(nodes), + 'spaghetti':json.dumps(edges) + } + return render(request, self.template_name, data) + + def get_node_label(self,model): if model.is_proxy: - proxy = model.model_class()._meta.proxy_for_model._meta - model.proxy = proxy - edge = { 'to':_id, - 'from':"%s__%s"%(proxy.app_label,proxy.model_name), - 'color':edge_color, - } - edges.append(edge) + label = "(P) %s"%(model.name.title()) + else: + label = "%s"%(model.name.title()) - nodes.append( - { - 'id':_id, - 'label':label, - 'shape':'box', - 'group':model.app_label, - 'title':get_template("django_spaghetti/meatball.html").render( - Context({'model':model,'fields':fields,}) - ) + line = "" + new_label = [] + for w in label.split(" "): + if len(line + w) > 15: + new_label.append(line) + line = w + else: + line += " " + line += w + new_label.append(line) - } - ) + return "\n".join(new_label) - data = { - 'meatballs':json.dumps(nodes), - 'spaghetti':json.dumps(edges) - } - return render(request, 'django_spaghetti/plate.html', data) +plate = Plate.as_view() \ No newline at end of file From 8c5692db8f84996685454089f8f4cb2505890f73 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 01:51:17 +0000 Subject: [PATCH 02/20] fixes #12 --- django_spaghetti/views.py | 65 ++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 2e7a400..71681a7 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -56,37 +56,40 @@ def plate(self): for f in fields+many: if f.rel is not None: m = f.rel.to._meta - if m.app_label != model.app_label: - edge_color = {'inherit':'both'} - edge = { 'from':_id, - 'to':"%s__%s"%(m.app_label,m.model_name), - 'color':edge_color, - } - - if str(f.name).endswith('_ptr'): - #fields that end in _ptr are pointing to a parent object - edge.update({ - 'arrows':{'to':{'scaleFactor':0.75}}, #needed to draw from-to - 'font': {'align': 'middle'}, - 'label':'is a', - 'dashes':True - }) - elif type(f) == related.ForeignKey: - edge.update({ - 'arrows':{'to':{'scaleFactor':0.75}} - }) - elif type(f) == related.OneToOneField: - edge.update({ - 'font': {'align': 'middle'}, - 'label':'|' - }) - elif type(f) == related.ManyToManyField: - edge.update({ - 'color':{'color':'gray'}, - 'arrows':{'to':{'scaleFactor':1}, 'from':{'scaleFactor':1}}, - }) - - edges.append(edge) + to_id = "%s__%s"%(m.app_label,m.model_name) + print m + if not(_id == to_id and graph_settings.get('ignore_self_referential', False)): + if m.app_label != model.app_label: + edge_color = {'inherit':'both'} + edge = { 'from':_id, + 'to':to_id, + 'color':edge_color, + } + + if str(f.name).endswith('_ptr'): + #fields that end in _ptr are pointing to a parent object + edge.update({ + 'arrows':{'to':{'scaleFactor':0.75}}, #needed to draw from-to + 'font': {'align': 'middle'}, + 'label':'is a', + 'dashes':True + }) + elif type(f) == related.ForeignKey: + edge.update({ + 'arrows':{'to':{'scaleFactor':0.75}} + }) + elif type(f) == related.OneToOneField: + edge.update({ + 'font': {'align': 'middle'}, + 'label':'|' + }) + elif type(f) == related.ManyToManyField: + edge.update({ + 'color':{'color':'gray'}, + 'arrows':{'to':{'scaleFactor':1}, 'from':{'scaleFactor':1}}, + }) + + edges.append(edge) if model.is_proxy: proxy = model.model_class()._meta.proxy_for_model._meta model.proxy = proxy From 96e643e546f6f5f9aa74d7214df63257c0015a71 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 01:52:19 +0000 Subject: [PATCH 03/20] fixes #13 --- django_spaghetti/views.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 71681a7..a7ea487 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -61,13 +61,11 @@ def plate(self): if not(_id == to_id and graph_settings.get('ignore_self_referential', False)): if m.app_label != model.app_label: edge_color = {'inherit':'both'} - edge = { 'from':_id, - 'to':to_id, - 'color':edge_color, - } + + edge = {'from':_id, 'to':to_id, 'color':edge_color } if str(f.name).endswith('_ptr'): - #fields that end in _ptr are pointing to a parent object + # fields that end in _ptr are pointing to a parent object edge.update({ 'arrows':{'to':{'scaleFactor':0.75}}, #needed to draw from-to 'font': {'align': 'middle'}, From bbedb58eac0b89f6794451212d3ca74a7c2e60ae Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 02:15:12 +0000 Subject: [PATCH 04/20] better tests and more peppy --- .travis.yml | 3 + django_spaghetti/__init__.py | 3 +- django_spaghetti/tests/models.py | 11 +-- django_spaghetti/tests/settings.py | 26 ++----- django_spaghetti/tests/test_it.py | 3 +- django_spaghetti/tests/wsgi.py | 3 +- django_spaghetti/views.py | 107 +++++++++++++++-------------- docs/conf.py | 2 +- docs/customising.rst | 4 +- docs/index.rst | 4 +- 10 files changed, 82 insertions(+), 84 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0d76abf..dc9d079 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,9 @@ install: - "pip install coveralls" - pip install -q Django==$DJANGO - "pip install ." +before_script: + - pep8 --ignore=E501,E225,E123 django_spaghetti + - cd docs ; sphinx-build -nW -b html -d _build/doctrees . _build/html ; cd .. # command to run tests script: - coverage run --branch --source=django_spaghetti manage.py test diff --git a/django_spaghetti/__init__.py b/django_spaghetti/__init__.py index 540bbf5..84c72a7 100644 --- a/django_spaghetti/__init__.py +++ b/django_spaghetti/__init__.py @@ -6,6 +6,7 @@ 'serial': 0 } + def get_version(release_level=True): """ Return the formatted version information @@ -15,4 +16,4 @@ def get_version(release_level=True): vers.append('%(releaselevel)s%(serial)i' % __version_info__) return ''.join(vers) -__version__ = get_version() \ No newline at end of file +__version__ = get_version() diff --git a/django_spaghetti/tests/models.py b/django_spaghetti/tests/models.py index 5d30cf7..9b01b82 100644 --- a/django_spaghetti/tests/models.py +++ b/django_spaghetti/tests/models.py @@ -1,5 +1,6 @@ from django.db import models + class PoliceOfficer(models.Model): """ An officer of the NYPD. @@ -9,8 +10,7 @@ class PoliceOfficer(models.Model): first_name = models.CharField(max_length=200) surname = models.CharField(max_length=200) rank = models.CharField(max_length=200) - - arrests = models.ManyToManyField("Arrest",related_name="arresting_officers") + arrests = models.ManyToManyField("Arrest", related_name="arresting_officers") class PoliceStation(models.Model): @@ -25,11 +25,12 @@ class Precinct(PoliceStation): number = models.IntegerField(primary_key=True) burrough = models.CharField(max_length=20) captain = models.OneToOneField(PoliceOfficer) - + class Meta: - unique_together = ("burrough","number") + unique_together = ("burrough", "number") + def natural_key(self): - return (self.burrough,self.number) + return (self.burrough, self.number) class Division(PoliceStation): diff --git a/django_spaghetti/tests/settings.py b/django_spaghetti/tests/settings.py index a059b4d..218a697 100644 --- a/django_spaghetti/tests/settings.py +++ b/django_spaghetti/tests/settings.py @@ -17,7 +17,7 @@ # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'jsut_another_test_key' +SECRET_KEY = 'just_another_test_key' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -52,28 +52,14 @@ } } -# Internationalization -# https://docs.djangoproject.com/en/1.9/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ STATIC_URL = '/static/' SPAGHETTI_SAUCE = { - 'apps':['tests','auth'], #['tests'] - 'exclude':{}, - 'show_fields':False, - 'ignore_self_referential':True, - } + 'apps': ['tests', 'auth'], + 'exclude': {}, + 'show_fields': False, + 'ignore_self_referential': True, +} diff --git a/django_spaghetti/tests/test_it.py b/django_spaghetti/tests/test_it.py index 087a538..2e3a491 100644 --- a/django_spaghetti/tests/test_it.py +++ b/django_spaghetti/tests/test_it.py @@ -4,7 +4,8 @@ setup_test_environment() + class LoadThePlate(TestCase): def test_plate(self): home = self.client.get("/") - self.assertEqual(home.status_code,200) + self.assertEqual(home.status_code, 200) diff --git a/django_spaghetti/tests/wsgi.py b/django_spaghetti/tests/wsgi.py index 04b2ede..39882b8 100644 --- a/django_spaghetti/tests/wsgi.py +++ b/django_spaghetti/tests/wsgi.py @@ -8,7 +8,8 @@ """ import os +from django.core.wsgi import get_wsgi_application + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "parlhand.settings") -from django.core.wsgi import get_wsgi_application application = get_wsgi_application() diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index a7ea487..592c882 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -13,7 +13,7 @@ class Plate(View): settings = None override_settings = {} template_name = 'django_spaghetti/plate.html' - + def get(self, request): return self.plate() @@ -24,11 +24,14 @@ def plate(self): graph_settings.update(self.override_settings) else: graph_settings = self.settings - - apps = graph_settings.get('apps',[]) - - excludes = ['%s__%s'%(app,model) for app,models in graph_settings.get('exclude',{}).items() for model in models ] + apps = graph_settings.get('apps', []) + + excludes = [ + "%s__%s" % (app, model) + for app, models in graph_settings.get('exclude', {}).items() + for model in models + ] models = ContentType.objects.filter(app_label__in=apps) nodes = [] edges = [] @@ -36,11 +39,11 @@ def plate(self): if (model.model_class() == None): continue model.is_proxy = model.model_class()._meta.proxy - if (model.is_proxy and not graph_settings.get('show_proxy',False)): + if (model.is_proxy and not graph_settings.get('show_proxy', False)): continue - - model.doc = model.model_class().__doc__ - _id = "%s__%s"%(model.app_label,model.model) + + model.doc = model.model_class().__doc__ + _id = "%s__%s" % (model.app_label, model.model) if _id in excludes: continue @@ -48,79 +51,79 @@ def plate(self): fields = [f for f in model.model_class()._meta.fields] many = [f for f in model.model_class()._meta.many_to_many] - if graph_settings.get('show_fields',True): - label += "\n%s\n"%("-"*len(model.model)) + if graph_settings.get('show_fields', True): + label += "\n%s\n" % ("-" * len(model.model)) label += "\n".join([str(f.name) for f in fields]) - edge_color = {'inherit':'from'} - - for f in fields+many: + edge_color = {'inherit': 'from'} + + for f in fields + many: if f.rel is not None: m = f.rel.to._meta - to_id = "%s__%s"%(m.app_label,m.model_name) + to_id = "%s__%s" % (m.app_label, m.model_name) print m if not(_id == to_id and graph_settings.get('ignore_self_referential', False)): if m.app_label != model.app_label: - edge_color = {'inherit':'both'} - - edge = {'from':_id, 'to':to_id, 'color':edge_color } - + edge_color = {'inherit': 'both'} + + edge = {'from': _id, 'to': to_id, 'color': edge_color} + if str(f.name).endswith('_ptr'): # fields that end in _ptr are pointing to a parent object edge.update({ - 'arrows':{'to':{'scaleFactor':0.75}}, #needed to draw from-to - 'font': {'align': 'middle'}, - 'label':'is a', - 'dashes':True - }) + 'arrows': {'to': {'scaleFactor': 0.75}}, # needed to draw from-to + 'font': {'align': 'middle'}, + 'label': 'is a', + 'dashes': True + }) elif type(f) == related.ForeignKey: edge.update({ - 'arrows':{'to':{'scaleFactor':0.75}} - }) + 'arrows': {'to': {'scaleFactor': 0.75}} + }) elif type(f) == related.OneToOneField: edge.update({ - 'font': {'align': 'middle'}, - 'label':'|' - }) + 'font': {'align': 'middle'}, + 'label': '|' + }) elif type(f) == related.ManyToManyField: edge.update({ - 'color':{'color':'gray'}, - 'arrows':{'to':{'scaleFactor':1}, 'from':{'scaleFactor':1}}, - }) - + 'color': {'color': 'gray'}, + 'arrows': {'to': {'scaleFactor': 1}, 'from': {'scaleFactor': 1}}, + }) + edges.append(edge) if model.is_proxy: proxy = model.model_class()._meta.proxy_for_model._meta model.proxy = proxy - edge = { 'to':_id, - 'from':"%s__%s"%(proxy.app_label,proxy.model_name), - 'color':edge_color, - } + edge = { + 'to': _id, + 'from': "%s__%s" % (proxy.app_label, proxy.model_name), + 'color': edge_color, + } edges.append(edge) - + nodes.append( { - 'id':_id, - 'label':label, - 'shape':'box', - 'group':model.app_label, - 'title':get_template("django_spaghetti/meatball.html").render( - Context({'model':model,'fields':fields,}) + 'id': _id, + 'label': label, + 'shape': 'box', + 'group': model.app_label, + 'title': get_template("django_spaghetti/meatball.html").render( + Context({'model': model, 'fields': fields}) ) - } ) - + data = { - 'meatballs':json.dumps(nodes), - 'spaghetti':json.dumps(edges) + 'meatballs': json.dumps(nodes), + 'spaghetti': json.dumps(edges) } return render(request, self.template_name, data) - def get_node_label(self,model): + def get_node_label(self, model): if model.is_proxy: - label = "(P) %s"%(model.name.title()) + label = "(P) %s" % (model.name.title()) else: - label = "%s"%(model.name.title()) + label = "%s" % (model.name.title()) line = "" new_label = [] @@ -135,4 +138,4 @@ def get_node_label(self,model): return "\n".join(new_label) -plate = Plate.as_view() \ No newline at end of file +plate = Plate.as_view() diff --git a/docs/conf.py b/docs/conf.py index 122ac91..5d5c64f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -148,7 +148,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = [] #['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/customising.rst b/docs/customising.rst index ec689d4..03dc5d5 100644 --- a/docs/customising.rst +++ b/docs/customising.rst @@ -15,7 +15,7 @@ To serve the spaghetti in a fancier setting, just override the ``django_spaghetti/plate.html`` template in your projects ``templates`` directory. You'll probably want your plate to look similar to that served by django_spaghetti, -`which can be viewed on github `_ +`which can be viewed on github `__ However, the important things when making a plate are: * make sure you import ``vis.js`` and ``vis.css`` *before* the script that creates the graph. @@ -54,7 +54,7 @@ To change how models are shown on hover, just override the ``django_spaghetti/meatball.html`` template in your projects ``templates`` directory. You'll probably want your meatball to taste similar to that served by django_spaghetti, -`which can be viewed on github `_ +`which can be viewed on github `__ For example, to show just the models name, number of fields and its documentation your template would look like this:: diff --git a/docs/index.rst b/docs/index.rst index ae5fb25..b422c95 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -39,7 +39,9 @@ and the code and documentation never get out of sync. It also meant that I (and others) could easily spot gaps in documentation. While some code is self-documenting, hovering over a model and still being confused about the existence of a field shows a definite need for explanation. -.. image:: https://cloud.githubusercontent.com/assets/2173174/9053053/a45e185c-3ab2-11e5-9ea0-89dafb7ac274.png +.. raw:: html + + Contents: From 7164ae9383d75d0e64d2ce215b04e638e2f51781 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 03:40:11 +0000 Subject: [PATCH 05/20] add pep8 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dc9d079..56d8884 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ env: install: # command to install dependencies - "pip install coveralls" + - pip install pep8 - pip install -q Django==$DJANGO - "pip install ." before_script: From 584ea2ce049f197eea05dc3d8a5d954addaaea87 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 03:41:17 +0000 Subject: [PATCH 06/20] fix readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index eeff76b..ce99aac 100644 --- a/README.rst +++ b/README.rst @@ -86,7 +86,7 @@ To see a complex example, where ``django-spaghetti-and-meatballs`` really shines checkout the live version built for the `Aristotle Metadata Registry `_ .. |docs| image:: https://readthedocs.org/projects/django-spaghetti-and-meatballs/badge/?version=latest - :target: https://readthedocs.org/projects/django-spaghetti-and-meatballs/?badge=latest + :target: https://http://django-spaghetti-and-meatballs.readthedocs.io/en/latest/ :alt: Documentation Status .. |code-climate| image:: https://codeclimate.com/github/LegoStormtroopr/django-spaghetti-and-meatballs/badges/gpa.svg From dcceaaf6b69e8da0a691810bb44b49c58f57e773 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:03:09 +0000 Subject: [PATCH 07/20] improved documentation --- django_spaghetti/views.py | 37 ++++++++++++++++++++++++++++++++++--- docs/conf.py | 7 +++++++ docs/customising.rst | 7 +++++++ docs/installing.rst | 14 ++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 592c882..14c010e 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -10,14 +10,41 @@ class Plate(View): + """ + This class-based-view serves up spaghetti and meatballs. + + Override the following class properties when calling `as_view`: + + * `settings` - sets a view specific to use instead of the `SPAGHETTI_SAUCE` django settings + * `override_settings` - overrides specified settings from `SPAGHETTI_SAUCE` django settings + * `plate_template_name` - overrides the template name for the whole view + * `meatball_template_name` - overrides the template used to render nodes + + For example the below URL pattern would specify a path to a view that displayed + models from the `auth` app with the given templates:: + + url(r'^user_graph/$', + Plate.as_view( + settings = { + 'apps': ['auth'], + } + meatball_template_name = "my_app/user_node.html", + plate_template_name = "my_app/auth_details.html" + ) + + """ settings = None override_settings = {} - template_name = 'django_spaghetti/plate.html' + plate_template_name = 'django_spaghetti/plate.html' + meatball_template_name = "django_spaghetti/meatball.html" def get(self, request): return self.plate() def plate(self): + """ + Serves up a delicious plate with your models + """ request = self.request if self.settings is None: graph_settings = getattr(settings, 'SPAGHETTI_SAUCE', {}) @@ -107,7 +134,7 @@ def plate(self): 'label': label, 'shape': 'box', 'group': model.app_label, - 'title': get_template("django_spaghetti/meatball.html").render( + 'title': get_template(self.meatball_template_name).render( Context({'model': model, 'fields': fields}) ) } @@ -117,9 +144,13 @@ def plate(self): 'meatballs': json.dumps(nodes), 'spaghetti': json.dumps(edges) } - return render(request, self.template_name, data) + return render(request, self.plate_template_name, data) def get_node_label(self, model): + """ + Defines how labels are constructed from models. + Default - uses verbose name, lines breaks where sensible + """ if model.is_proxy: label = "(P) %s" % (model.name.title()) else: diff --git a/docs/conf.py b/docs/conf.py index 5d5c64f..893960a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,6 +35,13 @@ 'sphinx.ext.viewcode', ] +sys.path.insert(0, os.path.abspath('../')) +sys.path.insert(0, os.path.abspath('../django_spaghetti/')) +os.environ['DJANGO_SETTINGS_MODULE'] = 'django_spaghetti.tests.settings' +import django +django.setup() + + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/customising.rst b/docs/customising.rst index 03dc5d5..7f03a25 100644 --- a/docs/customising.rst +++ b/docs/customising.rst @@ -67,3 +67,10 @@ For example, to show just the models name, number of fields and its documentatio {{ model.doc }} + + +Using class-based views +----------------------- + +.. autoclass:: django_spaghetti.views.Plate + :members: \ No newline at end of file diff --git a/docs/installing.rst b/docs/installing.rst index f235e0a..5d613fe 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -21,6 +21,20 @@ Installing spaghetti url(r'^plate/', include('django_spaghetti.urls')), ) +4. Or use the class-based view if you want more flexibility:: + + urlpatterns += patterns('', + url(r'^plate/$', + Plate.as_view( + override_settings = { + 'apps':['auth','polls'], + } + ), + name='plate' + ), + ) + + Configuring meatballs --------------------- From c04474425f3058b91b993ac1366328374877e73d Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:10:17 +0000 Subject: [PATCH 08/20] bump version --- django_spaghetti/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/__init__.py b/django_spaghetti/__init__.py index 84c72a7..91ee3a8 100644 --- a/django_spaghetti/__init__.py +++ b/django_spaghetti/__init__.py @@ -1,7 +1,7 @@ __version_info__ = { 'major': 0, 'minor': 2, - 'micro': 0, + 'micro': 1, 'releaselevel': 'final', 'serial': 0 } From 4c64347bf5655fa156f036efba6c25b885b35f86 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:13:04 +0000 Subject: [PATCH 09/20] fix views --- django_spaghetti/views.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 14c010e..584f54c 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -12,17 +12,17 @@ class Plate(View): """ This class-based-view serves up spaghetti and meatballs. - + Override the following class properties when calling `as_view`: - + * `settings` - sets a view specific to use instead of the `SPAGHETTI_SAUCE` django settings * `override_settings` - overrides specified settings from `SPAGHETTI_SAUCE` django settings * `plate_template_name` - overrides the template name for the whole view * `meatball_template_name` - overrides the template used to render nodes - + For example the below URL pattern would specify a path to a view that displayed models from the `auth` app with the given templates:: - + url(r'^user_graph/$', Plate.as_view( settings = { @@ -31,7 +31,6 @@ class Plate(View): meatball_template_name = "my_app/user_node.html", plate_template_name = "my_app/auth_details.html" ) - """ settings = None override_settings = {} From 56579472eb17b8fa5d2d309172e76ed6cc901e55 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:14:27 +0000 Subject: [PATCH 10/20] fix views --- django_spaghetti/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 584f54c..9c4a940 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -62,7 +62,7 @@ def plate(self): nodes = [] edges = [] for model in models: - if (model.model_class() == None): + if (model.model_class() is None): continue model.is_proxy = model.model_class()._meta.proxy if (model.is_proxy and not graph_settings.get('show_proxy', False)): From 0fe49d8823cc936cacf692f5d86e3e3bf89f4128 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:18:30 +0000 Subject: [PATCH 11/20] remove print --- django_spaghetti/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 9c4a940..4e138df 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -86,7 +86,6 @@ def plate(self): if f.rel is not None: m = f.rel.to._meta to_id = "%s__%s" % (m.app_label, m.model_name) - print m if not(_id == to_id and graph_settings.get('ignore_self_referential', False)): if m.app_label != model.app_label: edge_color = {'inherit': 'both'} From 436c5aba9a5cbf1225252d6deeaacb28c796998e Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:42:50 +0000 Subject: [PATCH 12/20] improve tests, done --- django_spaghetti/tests/settings.py | 7 ++----- django_spaghetti/tests/test_it.py | 16 ++++++++++++++-- django_spaghetti/tests/wsgi.py | 15 --------------- django_spaghetti/views.py | 6 +++++- 4 files changed, 21 insertions(+), 23 deletions(-) delete mode 100644 django_spaghetti/tests/wsgi.py diff --git a/django_spaghetti/tests/settings.py b/django_spaghetti/tests/settings.py index 218a697..70af5a4 100644 --- a/django_spaghetti/tests/settings.py +++ b/django_spaghetti/tests/settings.py @@ -26,7 +26,7 @@ ALLOWED_HOSTS = [] - +TEMPLATE_DIRS = [os.path.join(BASE_DIR,'templates')] # Application definition INSTALLED_APPS = ( @@ -37,10 +37,7 @@ 'django.contrib.staticfiles', ) -ROOT_URLCONF = 'django_spaghetti.urls' - -WSGI_APPLICATION = 'django_spaghetti.tests.wsgi.application' - +ROOT_URLCONF = 'django_spaghetti.tests.urls' # Database # https://docs.djangoproject.com/en/1.9/ref/settings/#databases diff --git a/django_spaghetti/tests/test_it.py b/django_spaghetti/tests/test_it.py index 2e3a491..f3b8a6d 100644 --- a/django_spaghetti/tests/test_it.py +++ b/django_spaghetti/tests/test_it.py @@ -7,5 +7,17 @@ class LoadThePlate(TestCase): def test_plate(self): - home = self.client.get("/") - self.assertEqual(home.status_code, 200) + response = self.client.get("/") + self.assertEqual(response.status_code, 200) + self.assertTrue('Officer' in response.content) + + def test_plate_with_settings(self): + response = self.client.get("/test/plate_settings") + self.assertEqual(response.status_code, 200) + self.assertTrue('Officer' not in response.content) + + def test_plate_with_override_settings(self): + response = self.client.get("/test/plate_override") + self.assertEqual(response.status_code, 200) + self.assertTrue('policeofficer' in response.content.lower()) + self.assertTrue('policestation' not in response.content.lower()) diff --git a/django_spaghetti/tests/wsgi.py b/django_spaghetti/tests/wsgi.py deleted file mode 100644 index 39882b8..0000000 --- a/django_spaghetti/tests/wsgi.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -WSGI config for parlhand project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ -""" - -import os -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "parlhand.settings") - -application = get_wsgi_application() diff --git a/django_spaghetti/views.py b/django_spaghetti/views.py index 4e138df..793463a 100644 --- a/django_spaghetti/views.py +++ b/django_spaghetti/views.py @@ -86,7 +86,11 @@ def plate(self): if f.rel is not None: m = f.rel.to._meta to_id = "%s__%s" % (m.app_label, m.model_name) - if not(_id == to_id and graph_settings.get('ignore_self_referential', False)): + if to_id in excludes: + pass + elif _id == to_id and graph_settings.get('ignore_self_referential', False): + pass + else: if m.app_label != model.app_label: edge_color = {'inherit': 'both'} From bedc09f21db29a6cd0fe42d5a585c410aa4757a5 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:45:39 +0000 Subject: [PATCH 13/20] pep --- django_spaghetti/tests/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/tests/settings.py b/django_spaghetti/tests/settings.py index 70af5a4..79e783a 100644 --- a/django_spaghetti/tests/settings.py +++ b/django_spaghetti/tests/settings.py @@ -26,7 +26,7 @@ ALLOWED_HOSTS = [] -TEMPLATE_DIRS = [os.path.join(BASE_DIR,'templates')] +TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] # Application definition INSTALLED_APPS = ( From 7e5a86f03db8fcab35532ed74f33463a8e958430 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:48:26 +0000 Subject: [PATCH 14/20] more files --- .../tests/templates/tests/meatball.html | 0 django_spaghetti/tests/urls.py | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 django_spaghetti/tests/templates/tests/meatball.html create mode 100644 django_spaghetti/tests/urls.py diff --git a/django_spaghetti/tests/templates/tests/meatball.html b/django_spaghetti/tests/templates/tests/meatball.html new file mode 100644 index 0000000..e69de29 diff --git a/django_spaghetti/tests/urls.py b/django_spaghetti/tests/urls.py new file mode 100644 index 0000000..ed36374 --- /dev/null +++ b/django_spaghetti/tests/urls.py @@ -0,0 +1,23 @@ +from django.conf.urls import url +from django.contrib import admin +from django_spaghetti.views import plate, Plate + +urlpatterns = [ + url(r'^$', plate, name='plate'), + url(r'^test/plate_settings$', Plate.as_view( + settings = { + 'apps': ['auth'], + 'exclude': {}, + } + + ), name='test_plate_settings'), + url(r'^test/plate_override$', Plate.as_view( + override_settings = { + 'exclude': { + 'tests':['policestation'] + }, + }, + meatball_template_name = "tests/meatball.html" + + ), name='test_plate_override$'), +] From 2b2052298b5fede78c4bebb5b6a12bcfab39470c Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 04:53:32 +0000 Subject: [PATCH 15/20] more files --- django_spaghetti/tests/urls.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/django_spaghetti/tests/urls.py b/django_spaghetti/tests/urls.py index ed36374..995e39a 100644 --- a/django_spaghetti/tests/urls.py +++ b/django_spaghetti/tests/urls.py @@ -5,19 +5,17 @@ urlpatterns = [ url(r'^$', plate, name='plate'), url(r'^test/plate_settings$', Plate.as_view( - settings = { + settings={ 'apps': ['auth'], 'exclude': {}, } - ), name='test_plate_settings'), url(r'^test/plate_override$', Plate.as_view( - override_settings = { + override_settings={ 'exclude': { - 'tests':['policestation'] + 'tests': ['policestation'] }, }, - meatball_template_name = "tests/meatball.html" - + meatball_template_name="tests/meatball.html" ), name='test_plate_override$'), ] From 64e100983fc8618e5b84633c01e0392bc3455e98 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 05:00:46 +0000 Subject: [PATCH 16/20] py3 --- django_spaghetti/tests/test_it.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/django_spaghetti/tests/test_it.py b/django_spaghetti/tests/test_it.py index f3b8a6d..484c8c1 100644 --- a/django_spaghetti/tests/test_it.py +++ b/django_spaghetti/tests/test_it.py @@ -9,15 +9,15 @@ class LoadThePlate(TestCase): def test_plate(self): response = self.client.get("/") self.assertEqual(response.status_code, 200) - self.assertTrue('Officer' in response.content) + self.assertTrue('Officer' in str(response.content)) def test_plate_with_settings(self): response = self.client.get("/test/plate_settings") self.assertEqual(response.status_code, 200) - self.assertTrue('Officer' not in response.content) + self.assertTrue('Officer' not in str(response.content)) def test_plate_with_override_settings(self): response = self.client.get("/test/plate_override") self.assertEqual(response.status_code, 200) - self.assertTrue('policeofficer' in response.content.lower()) - self.assertTrue('policestation' not in response.content.lower()) + self.assertTrue('policeofficer' in str(response.content).lower()) + self.assertTrue('policestation' not in str(response.content).lower()) From 34d0ee77a6231d79ccd1532e6c7c1251083406a5 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 05:03:49 +0000 Subject: [PATCH 17/20] url --- django_spaghetti/tests/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/tests/urls.py b/django_spaghetti/tests/urls.py index 995e39a..21f73d1 100644 --- a/django_spaghetti/tests/urls.py +++ b/django_spaghetti/tests/urls.py @@ -3,7 +3,7 @@ from django_spaghetti.views import plate, Plate urlpatterns = [ - url(r'^$', plate, name='plate'), + url(r'^/', include('django_spaghetti.urls',namespace="spaghetti")), url(r'^test/plate_settings$', Plate.as_view( settings={ 'apps': ['auth'], From 5252495c3843977359ab551ac0a58c7030197663 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 05:05:15 +0000 Subject: [PATCH 18/20] url --- django_spaghetti/tests/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/tests/urls.py b/django_spaghetti/tests/urls.py index 21f73d1..0efb069 100644 --- a/django_spaghetti/tests/urls.py +++ b/django_spaghetti/tests/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin from django_spaghetti.views import plate, Plate From 1b958d538642a01d1d74d403f8f3326829ae89b2 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 05:07:42 +0000 Subject: [PATCH 19/20] pep --- django_spaghetti/tests/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/tests/urls.py b/django_spaghetti/tests/urls.py index 0efb069..a0f71c0 100644 --- a/django_spaghetti/tests/urls.py +++ b/django_spaghetti/tests/urls.py @@ -3,7 +3,7 @@ from django_spaghetti.views import plate, Plate urlpatterns = [ - url(r'^/', include('django_spaghetti.urls',namespace="spaghetti")), + url(r'^/', include('django_spaghetti.urls', namespace="spaghetti")), url(r'^test/plate_settings$', Plate.as_view( settings={ 'apps': ['auth'], From 692dc46377c8d42e87faddc93730c9f2ef82e5a3 Mon Sep 17 00:00:00 2001 From: Samuel Spencer Date: Wed, 6 Jul 2016 05:09:57 +0000 Subject: [PATCH 20/20] fix urls --- django_spaghetti/tests/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_spaghetti/tests/urls.py b/django_spaghetti/tests/urls.py index a0f71c0..454ce81 100644 --- a/django_spaghetti/tests/urls.py +++ b/django_spaghetti/tests/urls.py @@ -3,7 +3,6 @@ from django_spaghetti.views import plate, Plate urlpatterns = [ - url(r'^/', include('django_spaghetti.urls', namespace="spaghetti")), url(r'^test/plate_settings$', Plate.as_view( settings={ 'apps': ['auth'], @@ -18,4 +17,5 @@ }, meatball_template_name="tests/meatball.html" ), name='test_plate_override$'), + url(r'^$', include('django_spaghetti.urls', namespace="spaghetti")), ]