Readme and other minor updates.
parent
46a32aba7d
commit
554dcfe8b0
|
|
@ -1,2 +1,2 @@
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include README.md
|
include README.rst
|
||||||
|
|
|
||||||
60
README.md
60
README.md
|
|
@ -1,60 +0,0 @@
|
||||||
# django-database-view
|
|
||||||
|
|
||||||
A simple pluggable application that allows to work with database views.
|
|
||||||
|
|
||||||
So far only MySQL is supported as backend, but more could be added if necessary.
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
1. In your models.py create classes which extend dbview.DbView like this:
|
|
||||||
|
|
||||||
```python
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from dbview.models import DbView
|
|
||||||
|
|
||||||
class ModelA(models.Model):
|
|
||||||
fielda = models.CharField()
|
|
||||||
fieldc = models.IntegerField()
|
|
||||||
|
|
||||||
class MyView(DbView):
|
|
||||||
fieldA = models.OneToOneField(ModelA, primary_key=True,
|
|
||||||
db_column='fielda__id')
|
|
||||||
fieldB = models.IntegerField(blank=True, null=True, db_column='fieldb')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def view(cls):
|
|
||||||
'''
|
|
||||||
This method returns the SQL string that creates the view, in this
|
|
||||||
example fieldB is the result of annotating another column
|
|
||||||
'''
|
|
||||||
qs = modelA.objects.all().\
|
|
||||||
annotate(fieldb=models.Sum('fieldc')) .\
|
|
||||||
annotate(fielda__id=models.F('pk')) .\
|
|
||||||
order_by('fielda__id') .\
|
|
||||||
values('fielda__id', 'fieldb')
|
|
||||||
return str(qs.query)
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively `get_view_str` method could be used to write a custom SQL:
|
|
||||||
|
|
||||||
```python
|
|
||||||
|
|
||||||
class MyView(DbView):
|
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_view_str(cls):
|
|
||||||
return """
|
|
||||||
CREATE VIEW my_view AS (
|
|
||||||
SELECT ...
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Then create a migration point for your view generation, edit that migration
|
|
||||||
and modify it, add: `from dbview.helpers import CreateView` and replace the
|
|
||||||
line the call to `migrations.CreateModel` with `CreateView`.
|
|
||||||
|
|
||||||
3. Migrate your database and start using your database views.
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
####################
|
||||||
|
django-database-view
|
||||||
|
####################
|
||||||
|
|
||||||
|
A simple pluggable application that allows to work with database views.
|
||||||
|
|
||||||
|
Quick start
|
||||||
|
===========
|
||||||
|
|
||||||
|
1. Install the package::
|
||||||
|
|
||||||
|
pip install django-database-models
|
||||||
|
|
||||||
|
2. In your ``models.py`` create classes which extend ``dbview.models.DbView``
|
||||||
|
like this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from dbview.models import DbView
|
||||||
|
|
||||||
|
class ModelA(models.Model):
|
||||||
|
fielda = models.CharField()
|
||||||
|
fieldc = models.IntegerField()
|
||||||
|
|
||||||
|
class MyView(DbView):
|
||||||
|
fieldA = models.OneToOneField(ModelA, primary_key=True,
|
||||||
|
db_column='fielda__id')
|
||||||
|
fieldB = models.IntegerField(blank=True, null=True, db_column='fieldb')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def view(cls):
|
||||||
|
"""
|
||||||
|
This method returns the SQL string that creates the view,
|
||||||
|
in this example fieldB is the result of annotating another column
|
||||||
|
"""
|
||||||
|
qs = modelA.objects.all(
|
||||||
|
).annotate(
|
||||||
|
fieldb=models.Sum('fieldc'),
|
||||||
|
).annotate(
|
||||||
|
fielda__id=models.F('pk'),
|
||||||
|
).order_by(
|
||||||
|
'fielda__id',
|
||||||
|
).values(
|
||||||
|
'fielda__id',
|
||||||
|
'fieldb',
|
||||||
|
)
|
||||||
|
return str(qs.query)
|
||||||
|
|
||||||
|
Alternatively ``get_view_str`` method could be used to write a custom SQL:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class MyView(DbView):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_view_str(cls):
|
||||||
|
return """
|
||||||
|
CREATE VIEW my_view AS (
|
||||||
|
SELECT ...
|
||||||
|
)"""
|
||||||
|
|
||||||
|
3. Then create a migration point for your view generation, edit that
|
||||||
|
migration and modify it, add:
|
||||||
|
``from dbview.helpers import CreateView`` and replace the line the
|
||||||
|
call to ``migrations.CreateModel`` with ``CreateView``.
|
||||||
|
|
||||||
|
4. Migrate your database and start using your database views.
|
||||||
|
|
@ -22,7 +22,8 @@ class CreateView(migrations.CreateModel):
|
||||||
elif hasattr(model, 'get_view_str'):
|
elif hasattr(model, 'get_view_str'):
|
||||||
self._create_view_from_raw_sql(model.get_view_str(), schema_editor)
|
self._create_view_from_raw_sql(model.get_view_str(), schema_editor)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"{model} has neither view nor get_view_str")
|
raise Exception('{} has neither view nor get_view_str'.format(
|
||||||
|
model))
|
||||||
|
|
||||||
def database_backwards(self, app_label, schema_editor, from_state, to):
|
def database_backwards(self, app_label, schema_editor, from_state, to):
|
||||||
fake_model = from_state.apps.get_model(app_label, self.name)
|
fake_model = from_state.apps.get_model(app_label, self.name)
|
||||||
|
|
@ -34,7 +35,7 @@ class CreateView(migrations.CreateModel):
|
||||||
if hasattr(models, self.name):
|
if hasattr(models, self.name):
|
||||||
return getattr(models, self.name)
|
return getattr(models, self.name)
|
||||||
|
|
||||||
# TODO: recursive search
|
# TODO: identify model more reliably and support more than 1 level
|
||||||
for submodule in models.__dict__.values():
|
for submodule in models.__dict__.values():
|
||||||
if hasattr(submodule, self.name):
|
if hasattr(submodule, self.name):
|
||||||
return getattr(submodule, self.name)
|
return getattr(submodule, self.name)
|
||||||
|
|
|
||||||
15
setup.py
15
setup.py
|
|
@ -1,15 +1,16 @@
|
||||||
import os
|
import os
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
# allow setup.py to be run from any path
|
|
||||||
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
|
def read(fname):
|
||||||
|
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-database-view',
|
name='django-database-view',
|
||||||
version='0.2.0',
|
version='0.2.1',
|
||||||
packages=['dbview'],
|
packages=['dbview'],
|
||||||
setup_requires=['setuptools-markdown'],
|
long_description=read('README.rst'),
|
||||||
long_description_markdown_filename='README.md',
|
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
license='MIT',
|
license='MIT',
|
||||||
description='A simple Django app to handle database views.',
|
description='A simple Django app to handle database views.',
|
||||||
|
|
@ -29,6 +30,10 @@ setup(
|
||||||
'Programming Language :: Python :: 3.2',
|
'Programming Language :: Python :: 3.2',
|
||||||
'Programming Language :: Python :: 3.3',
|
'Programming Language :: Python :: 3.3',
|
||||||
'Programming Language :: Python :: 3.4',
|
'Programming Language :: Python :: 3.4',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3.8',
|
||||||
'Topic :: Internet :: WWW/HTTP',
|
'Topic :: Internet :: WWW/HTTP',
|
||||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue