Ticket #11005: django-admin-query.diff

File django-admin-query.diff, 8.2 KB (added by seveas, 6 years ago)

Improved patch, with documentation

  • django/core/management/commands/query.py

     
     1from django.core.exceptions import FieldError
     2from django.core.management import BaseCommand
     3from django.db.models import Q
     4from django.template import loader, Template, Context, TemplateSyntaxError, TemplateDoesNotExist
     5from optparse import make_option
     6import os.path
     7import sys
     8
     9usage="""The django ORM will be queried with the filters on the commandline.
     10Records will be separated with newlines, fields with the specified separator
     11(the default is a comma). Alternatively, a template can be specified which will
     12be passed the result of the query as the 'objects' variable
     13
     14Query key/value pairs can be prefixed with a '!' or ~ to negate the query,
     15internally this uses a Q object.
     16
     17Examples:
     18 - Display username and email for all users
     19   %prog query -a django.contrib.auth -m User -f username
     20 - Show who can delete sites
     21   %prog query -a django.contrib.auth -m User -f username groups__permissions__codename=delete_site
     22 - Use comma-separated values for __in lookups
     23   %prog query -a django.contrib.auth -m User -f username groups__permissions__codename__in=delete_site,change_site
     24 - Use a template to format the information
     25   %prog query -a django.contrib.auth -m User -t '{% for o in objects %}{{ o.get_full_name }} has joined on {{ o.date_joined }}
     26{% endfor %}'
     27"""
     28
     29class Command(BaseCommand):
     30    option_list = BaseCommand.option_list + (
     31        make_option('-a', '--application', dest="application",
     32                    default=os.environ.get("DJANGO_QUERY_DEFAULT_APPLICATION", None),
     33                    help="Use this application", metavar="APP"),
     34        make_option('-m', '--model', dest="model",
     35                    default=os.environ.get("DJANGO_QUERY_DEFAULT_MODEL", None),
     36                    help="Query this model"),
     37        make_option('-o', '--order', dest="order", default=None,
     38                    help="Order by this field"),
     39        make_option('-f', '--fields', dest="fields", default=None,
     40                    help="Give these fields"),
     41        make_option('-s', '--separator', dest="separator", default=",",
     42                    help="Output separator"),
     43        make_option('-t', '--template', dest="template", default='',
     44                    help="Inline template in django syntax"),
     45        make_option('-T', '--template-file', dest="template_file", default=None,
     46                    help="File containing the template (abs/rel path or loader path)")
     47    )
     48    help = usage
     49    args = 'filter [filter ...]'
     50
     51    def handle(self, *args, **options):
     52        if not options['application']:
     53            print "You must specify which application to use"
     54            sys.exit(1)
     55        if not options['model']:
     56            print "You must specify which model to use"
     57            sys.exit(1)
     58        if not options['fields'] and not options['template'] and not options['template_file']:
     59            print "You must specify a list of fields or a template"
     60            sys.exit(1)
     61
     62        # Import the model
     63        models = options['application'] + '.models'
     64        __import__(models)
     65        models = sys.modules[models]
     66        model = getattr(models, options['model'])
     67
     68        # Create queryset
     69        qargs = []
     70        for x in args:
     71            if '=' not in args:
     72                print "Invalid filter '%s' - should be in attribute=value format" % x
     73                sys.exit(1)
     74            key, val = x.split('=',1)
     75            # Accecpt comma-separated values for __in lookups
     76            if key.endswith('__in'):
     77                val = val.split(',')
     78            if key.startswith('!') or key.startswith('~'):
     79                qargs.append(~Q(**{key[1:]: val}))
     80            else:
     81                qargs.append(Q(**{key: val}))
     82        try:
     83            queryset = model.objects.filter(*qargs).distinct()
     84            if options['order']:
     85                queryset = queryset.order_by(options['order'])
     86            # Force coercion into list to trap illegal values for options['order']
     87            queryset = list(queryset)
     88        except FieldError, e:
     89            print e
     90            sys.exit(1)
     91
     92        # Generate output
     93        if options['template']:
     94            # Inline template
     95            template = options['template']
     96        elif options['template_file']:
     97            # Template file, 3 options: stdin, existing path, or loader
     98            tf = options['template_file']
     99            if tf == '-':
     100                template = sys.stdin.read()
     101            elif tf and os.path.exists(tf):
     102                template = open(tf).read()
     103            elif tf:
     104                try:
     105                    template = loader.get_template(tf)
     106                except TemplateDoesNotExist:
     107                    print "Template %s does not exist" % tf
     108                    sys.exit(1)
     109        else:
     110            # Build a (c)sv template
     111            template = "{% for obj in objects %}"
     112            for field in options['fields'].split(','):
     113                template += '{{ obj.' + field + ' }}' + options['separator']
     114            template = template[:-len(options['separator'])] + "\n{% endfor %}"
     115        try:
     116            template = Template(template)
     117        except TemplateSyntaxError, e:
     118            print "Template syntax error: " + str(e)
     119            sys.exit(1)
     120        print template.render(Context({'objects': queryset}))
  • docs/ref/django-admin.txt

     
    252252    you sure?" confirmation messages. This is useful if ``django-admin.py`` is
    253253    being executed as an unattended, automated script.
    254254
     255query
     256-----
     257
     258.. django-admin:: query -a application -m model [filters] [output options]
     259
     260Query the django ORM from the commandline. You need to specify the application
     261and model to query and optionally a list of filters. The default output is csv
     262format, but you can change the separator to something else or use a template to
     263format the output.
     264
     265Query options
     266~~~~~~~~~~~~~
     267
     268..django-admin-option:: --application
     269Query a model in this application.
     270
     271..django-admin-option:: --model
     272Use -m or --model to specify which model in the application to query
     273
     274..django-admin-option:: --order
     275Order by this field
     276
     277Output options
     278~~~~~~~~~~~~~~~~
     279
     280..django-admin-option:: --fields
     281A comma-separated list of fields to output in CSV format
     282
     283..django-admin-option:: --separator
     284Use another separator than the comma in the output
     285
     286..django-admin-option:: --template
     287Inline template to use for rendering the data. The data will be available in
     288the ``objects`` variable.
     289
     290..django-admin-option:: --template-file
     291Use a template from a file. If you specify ``-`` as template file, the template
     292will be read from stdin. If the path you specify is an absolute/relative path
     293to an existing file it will be used. For other values, the template will be
     294loaded with ``template.loader``.
     295
     296Query filters
     297~~~~~~~~~~~~~
     298The remaining arguments will be interpreted as filters, very similar to the
     299standard query syntax. There are a few syntactic changes to accomodate
     300negations and ``__in`` lookups.
     301
     302    * ``~Q(key=value)`` can be written as ``~key=value`` or ``!key=value``
     303    * ``key__in=(value1,value2,value3)`` can be written as ``key__in=value1,value2,value3``
     304
     305Examples
     306~~~~~~~~
     307Display username and email for all users::
     308
     309    django-admin.py query -a django.contrib.auth -m User -f username
     310
     311Show who can delete sites::
     312
     313    django-admin.py query -a django.contrib.auth -m User -f username groups__permissions__codename=delete_site
     314   
     315Use comma-separated values for __in lookups::
     316
     317    django-admin.py query -a django.contrib.auth -m User -f username groups__permissions__codename__in=delete_site,change_site
     318
     319Use a template to format the information::
     320
     321    django-admin.py query -a django.contrib.auth -m User -t '{% for o in objects %}{{ o.get_full_name }} has joined on {{ o.date_joined }}
     322    {% endfor %}'
     323
    255324inspectdb
    256325---------
    257326
Back to Top