Skip to content

Commit e603a21

Browse files
committed
First commit for testing purposes
1 parent c72e0a3 commit e603a21

File tree

9 files changed

+185
-0
lines changed

9 files changed

+185
-0
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,25 @@
11
# django-dbml
2+
23
This app can generate a DBML output for all installed models.
4+
5+
## How to install and use?
6+
7+
#### 1. Install the django-dbml package
8+
9+
```
10+
pip install django-dbml
11+
```
12+
13+
#### 2. Put django_dbml on your django settings
14+
15+
```python
16+
'...',
17+
'django_dbml',
18+
'...',
19+
```
20+
21+
#### 3. Run the command to generate a DBML schema based on your Django models
22+
23+
```bash
24+
$ python manage.py dbml all
25+
```

django_dbml/__init__.py

Whitespace-only changes.

django_dbml/management/__init__.py

Whitespace-only changes.

django_dbml/management/commands/__init__.py

Whitespace-only changes.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from django_dbml.utils import to_snake_case
2+
from django.apps import apps
3+
from django.core.management.base import BaseCommand
4+
from django.db import models
5+
6+
7+
class Command(BaseCommand):
8+
help = "The main DBML management file"
9+
10+
def get_field_notes(self, field):
11+
if len(field.keys()) == 1:
12+
return ""
13+
14+
attributes = []
15+
for name, value in field.items():
16+
if name == "type":
17+
continue
18+
19+
if name == "note":
20+
attributes.append("note:\"{}\"".format(value))
21+
continue
22+
23+
if name in ("null", "pk", "unique"):
24+
attributes.append(name)
25+
continue
26+
27+
attributes.append("{}:{}".format(name, value))
28+
if not attributes:
29+
return ""
30+
return "[{}]".format(", ".join(attributes))
31+
32+
def handle(self, *args, **kwargs):
33+
all_fields = {}
34+
for field_type in models.__all__:
35+
if "Field" not in field_type:
36+
continue
37+
all_fields[field_type] = to_snake_case(field_type.replace("Field", ""))
38+
fields_available = all_fields.keys()
39+
40+
tables = {}
41+
relations = []
42+
relation_types = (models.ManyToManyField, models.OneToOneField, models.ForeignKey)
43+
app_tables = apps.get_models()
44+
for app_table in app_tables:
45+
keys = app_table.__dict__.keys()
46+
table_name = app_table.__name__
47+
tables[table_name] = {
48+
"fields": {},
49+
}
50+
for key in keys:
51+
value = app_table.__dict__[key]
52+
53+
# Check the fields and ManyToMany relations only
54+
if not isinstance(
55+
value,
56+
(
57+
models.fields.related_descriptors.ManyToManyDescriptor,
58+
models.query_utils.DeferredAttribute,
59+
),
60+
):
61+
continue
62+
63+
field = value.__dict__["field"]
64+
if (
65+
isinstance(field, models.ManyToManyField)
66+
and field.related_model == app_table
67+
):
68+
continue
69+
70+
field_type_name = all_fields.get(type(field).__name__)
71+
if not field_type_name:
72+
continue
73+
74+
tables[table_name]["fields"][field.name] = {
75+
"type": field_type_name,
76+
}
77+
78+
if field.help_text:
79+
tables[table_name]["fields"][field.name]["note"] = field.help_text
80+
81+
if field.null is True:
82+
tables[table_name]["fields"][field.name]["null"] = True
83+
84+
if field.primary_key is True:
85+
tables[table_name]["fields"][field.name]["pk"] = True
86+
87+
if field.unique is True:
88+
tables[table_name]["fields"][field.name]["unique"] = True
89+
90+
if not field.default == models.fields.NOT_PROVIDED:
91+
tables[table_name]["fields"][field.name]["default"] = field.default
92+
93+
# if isinstance(field, models.ManyToManyField):
94+
# if field.to_fields and field.to_fields[0] is not None and field.to_fields[0] != 'self':
95+
# print("ref: "+app_table.__name__+"."+field.name+" > "+str(field.related_model.__name__)+"."+field.to_fields[0])
96+
# # relations.append("ref: "+app_table.__name__+"."+field.name+" > "+str(field.related_model.__name__)+"."+field.to_fields[0])
97+
# else:
98+
# # _, related_field = field.related_fields[0]
99+
# print("ref: "+app_table.__name__+"."+field.name+" > "+str(field.related_model.__name__)+"."+related_field.name)
100+
# # relations.append("ref: "+app_table.__name__+"."+field.name+" > "+str(field.related_model.__name__)+"."+related_field.name)
101+
# else:
102+
# related_field = ""
103+
# for keey in field.related_model.__dict__.keys():
104+
# valuee = field.related_model.__dict__[keey]
105+
# if isinstance(valuee, models.query_utils.DeferredAttribute):
106+
# related_field = valuee.__dict__['field'].name
107+
# break
108+
# relations.append("ref: " + app_table.__name__ + "." + field.name + " > " + str(field.related_model.__name__) + "." + related_field)
109+
110+
for table_name, table in tables.items():
111+
# if not table_name == "Shipping":
112+
# continue
113+
print("Table {} {{".format(table_name))
114+
for field_name, field in table["fields"].items():
115+
print(" {} {} {}".format(field_name, field["type"], self.get_field_notes(field)))
116+
print("}\n")

django_dbml/tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.test import TestCase
2+
3+
# Create your tests here.

django_dbml/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import re
2+
3+
snake_pattern = re.compile(r"(?<!^)(?=[A-Z])")
4+
5+
6+
def to_snake_case(value):
7+
value = snake_pattern.sub("_", value).lower()
8+
value = (
9+
value.replace("i_p_", "ip_").replace("u_r_l", "url").replace("u_u_i_d", "uuid")
10+
)
11+
12+
return value

setup.cfg

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[metadata]
2+
name = django-dbml
3+
version = 0.1
4+
description = This app can generate a DBML output for all installed models.
5+
long_description = file: README.md
6+
url = https://makecodes.dev/
7+
author = Michel Wilhelm
8+
author_email = [email protected]
9+
license = unlicense
10+
classifiers =
11+
Environment :: Web Environment
12+
Framework :: Django
13+
Framework :: Django :: 2.0
14+
Intended Audience :: Developers
15+
License :: OSI Approved :: BSD License
16+
Operating System :: OS Independent
17+
Programming Language :: Python
18+
Programming Language :: Python :: 3
19+
Programming Language :: Python :: 3 :: Only
20+
Programming Language :: Python :: 3.6
21+
Programming Language :: Python :: 3.7
22+
Programming Language :: Python :: 3.8
23+
Topic :: Internet :: WWW/HTTP
24+
Topic :: Internet :: WWW/HTTP :: Dynamic Content
25+
26+
[options]
27+
include_package_data = true
28+
packages = find:

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from setuptools import setup
2+
3+
setup()

0 commit comments

Comments
 (0)