Index: django/core/management/commands/query.py
===================================================================
--- django/core/management/commands/query.py	(revision 0)
+++ django/core/management/commands/query.py	(revision 0)
@@ -0,0 +1,99 @@
+from django.db.models import Q
+from django.core.management import BaseCommand
+from django.template import loader, Template, Context
+from optparse import make_option
+import os.path
+import sys
+
+usage="""The django ORM will be queried with the filters on the commandline. Records
+will be separated with newlines, fields with the specified separator 
+(the default is a comma). Alternatively, a template can be specified which 
+will be passed the result of the query as the 'objects' variable
+
+Query key/value pairs can be prefixed with a '!' to negate it, internally this uses
+a Q object.
+
+Examples:
+ - Display name and assettag of all mc01 servers
+   %prog query name__startswith=mc01 -f name,assettag
+ - Get a list of name, ip, mac for all servers where the does not contain .82.
+   %prog query -m Interface !ip_address__contains='.82.' -f server.name,ip_address,mac_address
+ - Use a template to get the roles, depending on mac address
+   %prog query interface__mac_address=00:17:A4:8D:E6:BC -t '{{ objects.0.role_set.all|join:"," }}'
+
+/!\\ Warning /!\\
+This script does not do much error checking. If you spell your query wrong, or
+do something wrong with templates, you will get a python traceback and not a
+nice error message."""
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('-a', '--application', dest="application",
+                    default=os.environ.get("DJANGO_QUERY_DEFAULT_APPLICATION", None),
+                    help="Use this application", metavar="APP"),
+        make_option('-m', '--model', dest="model",
+                    default=os.environ.get("DJANGO_QUERY_DEFAULT_MODEL", None),
+                    help="Query this model"),
+        make_option('-f', '--fields', dest="fields", default=None,
+                    help="Give these fields"),
+        make_option('-o', '--order', dest="order", default=None,
+                    help="Order by this field"),
+        make_option('-s', '--separator', dest="separator", default=",",
+                    help="Output separator"),
+        make_option('-t', '--template', dest="template", default='',
+                    help="Template in django syntax"),
+        make_option('-T', '--template-file', dest="template_file", default=None,
+                    help="File containing the template (abs/rel path or loader path)")
+    )
+    help = usage
+    args = 'filter [filter ...]'
+
+    def handle(self, *args, **options):
+        if not options['application']:
+            print "You must specify which application to use"
+            sys.exit(1)
+        if not options['model']:
+            print "You must specify which model to use"
+            sys.exit(1)
+        if not options['fields'] and not options['template'] and not options['template_file']:
+            print "You must specify a list of fields or a template"
+            sys.exit(1)
+
+        # Import the model
+        models = options['application'] + '.models'
+        __import__(models)
+        models = sys.modules[models]
+        model = getattr(models, options['model'])
+
+        # Create queryset
+        qargs = []
+        for x in args:
+            key, val = x.split('=',1)
+            if key.startswith('!') or key.startswith('~'):
+                qargs.append(~Q(**{key[1:]: val}))
+            else:
+                qargs.append(Q(**{key: val}))
+        queryset = model.objects.filter(*qargs)
+        if options['order']:
+            queryset = queryset.order_by(options['order'])
+
+        # Generate output
+        if options['template'] or options['template_file']:
+            template = Template(options['template'])
+            tf = options['template_file']
+            if tf == '-':
+                template = Template(sys.stdin.read())
+            elif tf and os.path.exists(tf):
+                template = Template(open(tf).read())
+            elif tf:
+                template = loader.get_template(tf)
+            print template.render(Context({'objects': queryset}))
+        else:
+            def getattr_r(obj, attr):
+                if '.' in attr:
+                    me, next = attr.split('.',1)
+                    return getattr_r(getattr(obj, me), next)
+                return getattr(obj, attr)
+            fields = options['fields'].split(',')
+            for record in queryset:
+                print options['separator'].join([unicode(getattr_r(record, x)) for x in fields])
