Code Monkey home page Code Monkey logo

django-aggtrigg's Introduction

django-aggtrigg

Automatic trigger generator for Django

https://img.shields.io/pypi/v/djorm-pgarray.svg?style=flat https://img.shields.io/pypi/dm/djorm-pgarray.svg?style=flat

Create triggers to do some aggregate and permit to count objects from database without using COUNT() aggregat. Detailed documentation is in the "docs" directory.

Quick start

  1. Add "django_aggtrigg" to your INSTALLED_APPS setting like this:

    INSTALLED_APPS = (
        ...
        'django_aggtrigg',
    )
    
  2. Import fields in your models:

    from django_aggtrigg.models import IntegerTriggerField
    from django_aggtrigg.models import FloatTriggerField
    
  3. Configure your fields as is:

    class Apple(models.Model):
        indice = IntegerTriggerField(default=0)
        indice.aggregate_trigger=['count','min']
    
        mark = FloatTriggerField(default=0)
        mark.aggregate_trigger=['min']
    

By default only the count aggregat will be created.

  1. Use the new manager on you Model

    objects = AggTriggManager()

Manage triggers and related objects

To create the triggers in the database do:

python manage.py aggtrigg_create

Dropping triggers is easy as doing:

python manage.py aggtrigg_drop | psql -d DATABASE NAME

For safety reason the drop command just ouptput on stdout the SQL statements.

To initialize the aggregeate table, you can fill it by hand or do:

python manage.py aggtrigg_initialize

Howto use the new aggregat

Instead of doing a COUNT as the traditionnal way:

Apple.objects.filter(indice=42).count()

you will do:

Apple.objects.optimized_count(indice=42)

This is may be less easy, but so much more efficient when you manipulate billions or tuples in your relations.

What inside

The class apple was create in the app called foo so the default name of the table that contains data will be foo_apple, we use the tablename from the Model so if it's changed in Meta will still be compliant.

A new table foo_apple__indice_agg is created in the same database as foo_apple, it will contain the aggregat:

foo=# \d foo_apple__indice_agg
Table "public.foo_apple__indice_agg"
  Column   |  Type   | Modifiers
-----------+---------+-----------
 indice    | integer |
 agg_count | integer |
 agg_min   | integer |
Indexes:
    "foo_apple__indice_agg_indice_idx" btree (indice)

Aggregate on related table

If you need to maintain count on related objects, for example the comment count per Article, you can use ForeignKeyTriggerField:

from django_aggtrigg.models import ForeignKeyTriggerField

Trade the ForeignKey on ArticleComment for a ForeignKeyTriggerField:

class ArticleComment(models.Model):
    ...
    article = ForeignKeyTriggerField(Article)
    ...

Add simple count:

article.aggregate_trigger = ["count"]

Or complex one with some filters:

article.aggregate_trigger = [{'count': [
                                {'private': [ {
                                    "field": "is_private",
                                    "value": False}
                                             ]
                                 }
                                       ]
                             }]

Create your triggers:

python manage.py aggtrigg_create

Initialize your triggers:

python manage.py aggtrigg_initialize

To use those triggers easily, you can use AggCount manager:

from django_aggtrigg.models import AggCount

ArticleManager = Manager.include(AggCount)

AggCount give you a new method on your model: get_count. You can use it juste like a traditional queryset method. ex:

Article.objects.filter(..).get_count().values("articlecomment_count_private")
[{'ticketcomment_count_private': 4},
{'ticketcomment_count_private': 2},..]

Article.objects.filter(..).get_count().first().__dict__
{'id': 24,
 ...
 'ticketcomment_count_private': 3
 ...}

The only thing you cannot do with get_count is filtering on the aggregates. ex:

Article.objects.get_count().filter(articlecomment_count_private__gte=3)
# THIS DOES NOT WORK !!!

Because the aggregates are not on the table you working on, Django does not really know anything about this table. THis is the reason why you do not have to bother with migrations.

django-aggtrigg's People

Contributors

boblefrag avatar brunobord avatar moumoutte avatar rodo avatar yakuru avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-aggtrigg's Issues

Remove 0 lines

In the aggretable table we keep records with count = 0 it should be better to remove them

Support ForeignKey

Actually only IntegerField and FloatField are supported, add the support to ForeignKey.

Suppport SQLite

It seems that sqlite3 supports triggers, try to support sqlite3 in django-aggtrigg

Ajouter un check des objets

Compléter la commande de check avec un check réel des objets présents en base pour avoir une vue des actions à faire.

django 1.7, 1.8

I've tested locally, and the extension is not "Django>=1.7 ready."

Fichier tox incorrect

Le fichier tox renvoi une erreur lors du run des tests à cause de mock qui est manquant alors qu'il est présent dans TEST_REQUIREMENTS
De plus flake8 remonte aussi une erreur sur tests.py qui est utilisé pour charger les tests depuis les autres fichiers.
@benoitbryon ou @brunobord si vous avez des idées là-dessus, je découvre tox et prend tous les conseils et bonnes pratiques

Becareful with Syntax description

I've passed two day , trying to debug a bug which doesn't not exists because I was confused on the description syntax. I would like to make a update on the syntaxe describtion, motivated by mind health and clearest code.

For the moment, when we want to add a counter with filtering the syntax is like that:

'agg_type': A list of dictionary with single value inside

'count': [
    {'public_leaves': [{'field': 'private', 'value': True}]},
    {'private_leaves': #blabla }
]

This syntax can be simplify by moving the list and dictionary with unique value inside by a single dictionary with multiple value

'count': {
   'public_leaves': [{'field': 'private', 'value': True}],
   'private_leaves': #blabla 
}

Less inference is better for our mind and our code. This piece of code can be more elegant than accessing the first element into a list, just to have the name of the counter:

https://github.com/novafloss/django-aggtrigg/blob/master/django_aggtrigg/util.py#L229
https://github.com/novafloss/django-aggtrigg/blob/master/django_aggtrigg/util.py#L231

Here I'm just talking about item in agg_type description. But, It can be extended to the description of agg_type itself.

Suggestions about naming (pep-423)

At the moment, this is pip install django-aggtrigg VS import aggregat_trigger.
Could be something like pip install django-aggtrigg and import django_aggtrigg. Notice that, in this proposal, the "django_" prefix in Python package name is related to "django-" prefix in project name.

In the name you choose (aka aggtrigg or aggregat_trigger), you'd better:

  • avoid underscores whenever possible (i.e. aggregattrigger > aggregate_trigger).
  • prefer memorable names to descriptive names (i.e. aggtrigg > aggregatetriggerfields)

It also applies to the demo project, kind of s/django_aggregat_trigger_demo/django_aggtriggdemo/.

For initial value, 0 is better than None

I assume Aggregation result is an integer, event the computation cannot be done (internal reason), the return value has to be an integer (sum, max, min, count..)

To be coherent, the value which does not exists in Database must returned 0 , not None.

Be careful, my assumption can be wrong ("Aggregation result is an integer").

djutil command doesn't show help info

When I try to display the help message via the command line I see this error:

$ python manage.py help djutil
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/pabluk/dev/projects/django-aggtrigg/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 399, in execute_from_command_line
    utility.execute()
  File "/home/pabluk/dev/projects/django-aggtrigg/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 380, in execute
    self.fetch_command(args[2]).print_help(self.prog_name, args[2])
  File "/home/pabluk/dev/projects/django-aggtrigg/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 272, in fetch_command
    klass = load_command_class(app_name, subcommand)
  File "/home/pabluk/dev/projects/django-aggtrigg/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 76, in load_command_class
    return module.Command()
AttributeError: 'module' object has no attribute 'Command'

I only see this error with the djutil command of django-aggtrigg. I'm using Python 2.7.6 and here's the output of pip freeze:

$ pip freeze
Django==1.6.8
Jinja2==2.7.3
MarkupSafe==0.23
argparse==1.2.1
django-aggtrigg==0.6.4
psycopg2==2.5.4
wsgiref==0.1.2

Changer la façon dont les triggers sont nommés

https://github.com/novafloss/django-aggtrigg/blob/master/django_aggtrigg/util.py#L53

Cette façon de faire engendre des problèmes au moment de la création des triggers.

Si je crée les compteurs suivants :

process.aggregate_trigger = [
    {'count': [
        {'tasks_needed_employee': [
            {'field': 'action_needed_employee', 'value': True}
        ]},
        {'tasks_needed_company': [
            {'field': 'action_needed_company', 'value': True}
        ]},
    ]}
]

Je n'obtiendrai que 3 triggers au lieu de 6 du fait que les noms de triggers générés sont équivalents pour les deux compteurs :

processes_task_process_id_need_insert_trigger
processes_task_process_id_need_update_trigger
processes_task_process_id_need_delete_trigger

When added multiple counter (with filtering), tests failed.

This patch make tests failed (in my own machin),

http://paste.debian.net/176840/

======================================================================
FAIL: test_real_triggers (django_aggtrigg_demo.tests.TestMockingTrigger)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/moumoutte/projects/django-aggtrigg/demo/django_aggtrigg_demo/tests.py", line 58, in      test_real_triggers
    Tree.objects.get_count().first().leave_count_private_leaves, 5)
AssertionError: 0 != 5

----------------------------------------------------------------------
Ran 7 tests in 1.310s

Add option --yes

Add an option --yes to bypass all question when create or delete relations and functions

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.