1 # Copyright (c) 2009 Dennis Kaarsemaker <dennis@kaarsemaker.net> 2 # All rights reserved. 3 # 4 # Redistribution and use in source and binary forms, with or without modification, 5 # are permitted provided that the following conditions are met: 6 # 7 # 1. Redistributions of source code must retain the above copyright notice, 8 # this list of conditions and the following disclaimer. 9 # 10 # 2. Redistributions in binary form must reproduce the above copyright 11 # notice, this list of conditions and the following disclaimer in the 12 # documentation and/or other materials provided with the distribution. 13 # 14 # 3. Neither the name of Django nor the names of its contributors may be used 15 # to endorse or promote products derived from this software without
1 # Copyright (c) 2009 Dennis Kaarsemaker <dennis@kaarsemaker.net>
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without modification,
5 # are permitted provided that the following conditions are met:
6 #
7 # 1. Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
9 #
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 #
14 # 3. Neither the name of Django nor the names of its contributors may be used
15 # to endorse or promote products derived from this software without
16 # specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 from django.db.models import Q
30 from django.core.management import BaseCommand
31 from django.template import loader, Template, Context
32 from optparse import make_option
33 import os.path
34 import sys
35
36 usage="""The django ORM will be queried with the filters on the commandline. Records
37 will be separated with newlines, fields with the specified separator
38 (the default is a comma). Alternatively, a template can be specified which
39 will be passed the result of the query as the 'objects' variable
40
41 Query key/value pairs can be prefixed with a '!' to negate it, internally this uses
42 a Q object.
43
44 Examples:
45 - Display name and assettag of all mc01 servers
46 %prog query name__startswith=mc01 -f name,assettag
47 - Get a list of name, ip, mac for all servers where the does not contain .82.
48 %prog query -m Interface !ip_address__contains='.82.' -f server.name,ip_address,mac_address
49 - Use a template to get the roles, depending on mac address
50 %prog query interface__mac_address=00:17:A4:8D:E6:BC -t '{{ objects.0.role_set.all|join:"," }}'
51
52 /!\\ Warning /!\\
53 This script does not do much error checking. If you spell your query wrong, or
54 do something wrong with templates, you will get a python traceback and not a
55 nice error message."""
56
57 class Command(BaseCommand):
58 option_list = BaseCommand.option_list + (
59 make_option('-a', '--application', dest="application",
60 default=os.environ.get("DJANGO_QUERY_DEFAULT_APPLICATION", None),
61 help="Use this application", metavar="APP"),
62 make_option('-m', '--model', dest="model",
63 default=os.environ.get("DJANGO_QUERY_DEFAULT_MODEL", None),
64 help="Query this model"),
65 make_option('-f', '--fields', dest="fields", default=None,
66 help="Give these fields"),
67 make_option('-o', '--order', dest="order", default=None,
68 help="Order by this field"),
69 make_option('-s', '--separator', dest="separator", default=",",
70 help="Output separator"),
71 make_option('-t', '--template', dest="template", default='',
72 help="Template in django syntax"),
73 make_option('-T', '--template-file', dest="template_file", default=None,
74 help="File containing the template (abs/rel path or loader path)")
75 )
76 help = usage
77 args = 'filter [filter ...]'
78
79 def handle(self, *args, **options):
80 if not options['application']:
81 print "You must specify which application to use"
82 sys.exit(1)
83 if not options['model']:
84 print "You must specify which model to use"
85 sys.exit(1)
86 if not options['fields'] and not options['template'] and not options['template_file']:
87 print "You must specify a list of fields or a template"
88 sys.exit(1)
89
90 # Import the model
91 models = options['application'] + '.models'
92 __import__(models)
93 models = sys.modules[models]
94 model = getattr(models, options['model'])
95
96 # Create queryset
97 qargs = []
98 for x in args:
99 key, val = x.split('=',1)
100 if key.startswith('!') or key.startswith('~'):
101 qargs.append(~Q(**{key[1:]: val}))
102 else:
103 qargs.append(Q(**{key: val}))
104 queryset = model.objects.filter(*qargs)
105 if options['order']:
106 queryset = queryset.order_by(options['order'])
107
108 # Generate output
109 if options['template'] or options['template_file']:
110 template = Template(options['template'])
111 tf = options['template_file']
112 if tf == '-':
113 template = Template(sys.stdin.read())
114 elif tf and os.path.exists(tf):
115 template = Template(open(tf).read())
116 elif tf:
117 template = loader.get_template(tf)
118 print template.render(Context({'objects': queryset}))
119 else:
120 def getattr_r(obj, attr):
121 if '.' in attr:
122 me, next = attr.split('.',1)
123 return getattr_r(getattr(obj, me), next)
124 return getattr(obj, attr)
125 fields = options['fields'].split(',')
126 for record in queryset:
127 print options['separator'].join([unicode(getattr_r(record, x)) for x in fields])
Show all