Merge branch 'master' of github.com:Gluejar/regluit

pull/1/head
Raymond Yee 2011-10-22 08:18:48 -07:00
commit 2aa91d88de
2240 changed files with 180133 additions and 59 deletions

View File

@ -0,0 +1,3 @@
recursive-include django_extensions/conf *.tmpl
recursive-include django_extensions/templates *.html
recursive-include django_extensions/media *

View File

@ -0,0 +1,21 @@
Metadata-Version: 1.0
Name: django-extensions
Version: 0.7.1
Summary: Extensions for Django
Home-page: http://github.com/django-extensions/django-extensions
Author: Bas van Oostveen
Author-email: v.oostveen@gmail.com
License: New BSD License
Description: django-extensions bundles several useful
additions for Django projects. See the project page for more information:
http://github.com/django-extensions/django-extensions
Platform: any
Classifier: Development Status :: 4 - Beta
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Utilities

View File

@ -0,0 +1,13 @@
VERSION = (0, 7, 1)
# Dynamically calculate the version based on VERSION tuple
if len(VERSION) > 2 and VERSION[2] is not None:
if isinstance(VERSION[2], int):
str_version = "%s.%s.%s" % VERSION[:3]
else:
str_version = "%s.%s_%s" % VERSION[:3]
else:
str_version = "%s.%s" % VERSION[:2]
__version__ = str_version

View File

@ -0,0 +1,147 @@
#
# Autocomplete feature for admin panel
#
# Most of the code has been written by Jannis Leidel and was updated a bit
# for django_extensions.
# http://jannisleidel.com/2008/11/autocomplete-form-widget-foreignkey-model-fields/
#
# to_string_function, Satchmo adaptation and some comments added by emes
# (Michal Salaban)
#
import operator
from django.http import HttpResponse, HttpResponseNotFound
from django.db import models
from django.db.models.query import QuerySet
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from django.utils.text import get_text_list
try:
from functools import update_wrapper
except ImportError:
from django.utils.functional import update_wrapper
from django_extensions.admin.widgets import ForeignKeySearchInput
from django.conf import settings
if 'reversion' in settings.INSTALLED_APPS:
from reversion.admin import VersionAdmin as ModelAdmin
else:
from django.contrib.admin import ModelAdmin
class ForeignKeyAutocompleteAdmin(ModelAdmin):
"""Admin class for models using the autocomplete feature.
There are two additional fields:
- related_search_fields: defines fields of managed model that
have to be represented by autocomplete input, together with
a list of target model fields that are searched for
input string, e.g.:
related_search_fields = {
'author': ('first_name', 'email'),
}
- related_string_functions: contains optional functions which
take target model instance as only argument and return string
representation. By default __unicode__() method of target
object is used.
"""
related_search_fields = {}
related_string_functions = {}
def get_urls(self):
from django.conf.urls.defaults import patterns, url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.module_name
urlpatterns = patterns('',
url(r'foreignkey_autocomplete/$',
wrap(self.foreignkey_autocomplete),
name='%s_%s_autocomplete' % info),
) + super(ForeignKeyAutocompleteAdmin, self).get_urls()
return urlpatterns
def foreignkey_autocomplete(self, request):
"""
Searches in the fields of the given related model and returns the
result as a simple string to be used by the jQuery Autocomplete plugin
"""
query = request.GET.get('q', None)
app_label = request.GET.get('app_label', None)
model_name = request.GET.get('model_name', None)
search_fields = request.GET.get('search_fields', None)
object_pk = request.GET.get('object_pk', None)
try:
to_string_function = self.related_string_functions[model_name]
except KeyError:
to_string_function = lambda x: x.__unicode__()
if search_fields and app_label and model_name and (query or object_pk):
def construct_search(field_name):
# use different lookup methods depending on the notation
if field_name.startswith('^'):
return "%s__istartswith" % field_name[1:]
elif field_name.startswith('='):
return "%s__iexact" % field_name[1:]
elif field_name.startswith('@'):
return "%s__search" % field_name[1:]
else:
return "%s__icontains" % field_name
model = models.get_model(app_label, model_name)
queryset = model._default_manager.all()
data = ''
if query:
for bit in query.split():
or_queries = [models.Q(**{construct_search(
smart_str(field_name)): smart_str(bit)})
for field_name in search_fields.split(',')]
other_qs = QuerySet(model)
other_qs.dup_select_related(queryset)
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
queryset = queryset & other_qs
data = ''.join([u'%s|%s\n' % (
to_string_function(f), f.pk) for f in queryset])
elif object_pk:
try:
obj = queryset.get(pk=object_pk)
except:
pass
else:
data = to_string_function(obj)
return HttpResponse(data)
return HttpResponseNotFound()
def get_help_text(self, field_name, model_name):
searchable_fields = self.related_search_fields.get(field_name, None)
if searchable_fields:
help_kwargs = {
'model_name': model_name,
'field_list': get_text_list(searchable_fields, _('and')),
}
return _('Use the left field to do %(model_name)s lookups in the fields %(field_list)s.') % help_kwargs
return ''
def formfield_for_dbfield(self, db_field, **kwargs):
"""
Overrides the default widget for Foreignkey fields if they are
specified in the related_search_fields class attribute.
"""
if (isinstance(db_field, models.ForeignKey) and
db_field.name in self.related_search_fields):
model_name = db_field.rel.to._meta.object_name
help_text = self.get_help_text(db_field.name, model_name)
if kwargs.get('help_text'):
help_text = u'%s %s' % (kwargs['help_text'], help_text)
kwargs['widget'] = ForeignKeySearchInput(db_field.rel,
self.related_search_fields[db_field.name])
kwargs['help_text'] = help_text
return super(ForeignKeyAutocompleteAdmin,
self).formfield_for_dbfield(db_field, **kwargs)

View File

@ -0,0 +1,77 @@
from django import forms
from django.conf import settings
from django.utils.safestring import mark_safe
from django.utils.text import truncate_words
from django.template.loader import render_to_string
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
class ForeignKeySearchInput(ForeignKeyRawIdWidget):
"""
A Widget for displaying ForeignKeys in an autocomplete search input
instead in a <select> box.
"""
# Set in subclass to render the widget with a different template
widget_template = None
# Set this to the patch of the search view
search_path = '../foreignkey_autocomplete/'
class Media:
css = {
'all': ('django_extensions/css/jquery.autocomplete.css',)
}
js = (
'django_extensions/js/jquery.js',
'django_extensions/js/jquery.bgiframe.min.js',
'django_extensions/js/jquery.ajaxQueue.js',
'django_extensions/js/jquery.autocomplete.js',
)
def label_for_value(self, value):
key = self.rel.get_related_field().name
obj = self.rel.to._default_manager.get(**{key: value})
return truncate_words(obj, 14)
def __init__(self, rel, search_fields, attrs=None):
self.search_fields = search_fields
super(ForeignKeySearchInput, self).__init__(rel, attrs)
def render(self, name, value, attrs=None):
if attrs is None:
attrs = {}
output = [super(ForeignKeySearchInput, self).render(name, value, attrs)]
opts = self.rel.to._meta
app_label = opts.app_label
model_name = opts.object_name.lower()
related_url = '../../../%s/%s/' % (app_label, model_name)
params = self.url_parameters()
if params:
url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
else:
url = ''
if not 'class' in attrs:
attrs['class'] = 'vForeignKeyRawIdAdminField'
# Call the TextInput render method directly to have more control
output = [forms.TextInput.render(self, name, value, attrs)]
if value:
label = self.label_for_value(value)
else:
label = u''
context = {
'url': url,
'related_url': related_url,
'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX,
'search_path': self.search_path,
'search_fields': ','.join(self.search_fields),
'model_name': model_name,
'app_label': app_label,
'label': label,
'name': name,
}
output.append(render_to_string(self.widget_template or (
'django_extensions/widgets/%s/%s/foreignkey_searchinput.html' % (app_label, model_name),
'django_extensions/widgets/%s/foreignkey_searchinput.html' % app_label,
'django_extensions/widgets/foreignkey_searchinput.html',
), context))
output.reverse()
return mark_safe(u''.join(output))

View File

@ -0,0 +1,275 @@
"""
Django Extensions additional model fields
"""
from django.template.defaultfilters import slugify
from django.db.models import DateTimeField, CharField, SlugField
import datetime
import re
try:
import uuid
except ImportError:
from django_extensions.utils import uuid
class AutoSlugField(SlugField):
""" AutoSlugField
By default, sets editable=False, blank=True.
Required arguments:
populate_from
Specifies which field or list of fields the slug is populated from.
Optional arguments:
separator
Defines the used separator (default: '-')
overwrite
If set to True, overwrites the slug on every save (default: False)
Inspired by SmileyChris' Unique Slugify snippet:
http://www.djangosnippets.org/snippets/690/
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('blank', True)
kwargs.setdefault('editable', False)
populate_from = kwargs.pop('populate_from', None)
if populate_from is None:
raise ValueError("missing 'populate_from' argument")
else:
self._populate_from = populate_from
self.separator = kwargs.pop('separator', u'-')
self.overwrite = kwargs.pop('overwrite', False)
self.allow_duplicates = kwargs.pop('allow_duplicates', False)
super(AutoSlugField, self).__init__(*args, **kwargs)
def _slug_strip(self, value):
"""
Cleans up a slug by removing slug separator characters that occur at
the beginning or end of a slug.
If an alternate separator is used, it will also replace any instances
of the default '-' separator with the new separator.
"""
re_sep = '(?:-|%s)' % re.escape(self.separator)
value = re.sub('%s+' % re_sep, self.separator, value)
return re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
def slugify_func(self, content):
if content:
return slugify(content)
return ''
def create_slug(self, model_instance, add):
# get fields to populate from and slug field to set
if not isinstance(self._populate_from, (list, tuple)):
self._populate_from = (self._populate_from, )
slug_field = model_instance._meta.get_field(self.attname)
if add or self.overwrite:
# slugify the original field content and set next step to 2
slug_for_field = lambda field: self.slugify_func(getattr(model_instance, field))
slug = self.separator.join(map(slug_for_field, self._populate_from))
next = 2
else:
# get slug from the current model instance and calculate next
# step from its number, clean-up
slug = self._slug_strip(getattr(model_instance, self.attname))
next = slug.split(self.separator)[-1]
if next.isdigit() and not self.allow_duplicates:
slug = self.separator.join(slug.split(self.separator)[:-1])
next = int(next)
else:
next = 2
# strip slug depending on max_length attribute of the slug field
# and clean-up
slug_len = slug_field.max_length
if slug_len:
slug = slug[:slug_len]
slug = self._slug_strip(slug)
original_slug = slug
if self.allow_duplicates:
return slug
# exclude the current model instance from the queryset used in finding
# the next valid slug
queryset = model_instance.__class__._default_manager.all()
if model_instance.pk:
queryset = queryset.exclude(pk=model_instance.pk)
# form a kwarg dict used to impliment any unique_together contraints
kwargs = {}
for params in model_instance._meta.unique_together:
if self.attname in params:
for param in params:
kwargs[param] = getattr(model_instance, param, None)
kwargs[self.attname] = slug
# increases the number while searching for the next valid slug
# depending on the given slug, clean-up
while not slug or queryset.filter(**kwargs):
slug = original_slug
end = '%s%s' % (self.separator, next)
end_len = len(end)
if slug_len and len(slug) + end_len > slug_len:
slug = slug[:slug_len - end_len]
slug = self._slug_strip(slug)
slug = '%s%s' % (slug, end)
kwargs[self.attname] = slug
next += 1
return slug
def pre_save(self, model_instance, add):
value = unicode(self.create_slug(model_instance, add))
setattr(model_instance, self.attname, value)
return value
def get_internal_type(self):
return "SlugField"
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect the _actual_ field.
from south.modelsinspector import introspector
field_class = '%s.AutoSlugField' % self.__module__
args, kwargs = introspector(self)
kwargs.update({
'populate_from': repr(self._populate_from),
'separator': repr(self.separator),
'overwrite': repr(self.overwrite),
'allow_duplicates': repr(self.allow_duplicates),
})
# That's our definition!
return (field_class, args, kwargs)
class CreationDateTimeField(DateTimeField):
""" CreationDateTimeField
By default, sets editable=False, blank=True, default=datetime.now
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('editable', False)
kwargs.setdefault('blank', True)
kwargs.setdefault('default', datetime.datetime.now)
DateTimeField.__init__(self, *args, **kwargs)
def get_internal_type(self):
return "DateTimeField"
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect ourselves, since we inherit.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.DateTimeField"
args, kwargs = introspector(self)
return (field_class, args, kwargs)
class ModificationDateTimeField(CreationDateTimeField):
""" ModificationDateTimeField
By default, sets editable=False, blank=True, default=datetime.now
Sets value to datetime.now() on each save of the model.
"""
def pre_save(self, model, add):
value = datetime.datetime.now()
setattr(model, self.attname, value)
return value
def get_internal_type(self):
return "DateTimeField"
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect ourselves, since we inherit.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.DateTimeField"
args, kwargs = introspector(self)
return (field_class, args, kwargs)
class UUIDVersionError(Exception):
pass
class UUIDField(CharField):
""" UUIDField
By default uses UUID version 1 (generate from host ID, sequence number and current time)
The field support all uuid versions which are natively supported by the uuid python module.
For more information see: http://docs.python.org/lib/module-uuid.html
"""
def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs):
kwargs['max_length'] = 36
if auto:
kwargs['blank'] = True
kwargs.setdefault('editable', False)
self.auto = auto
self.version = version
if version == 1:
self.node, self.clock_seq = node, clock_seq
elif version == 3 or version == 5:
self.namespace, self.name = namespace, name
CharField.__init__(self, verbose_name, name, **kwargs)
def get_internal_type(self):
return CharField.__name__
def contribute_to_class(self, cls, name):
if self.primary_key:
assert not cls._meta.has_auto_field, \
"A model can't have more than one AutoField: %s %s %s; have %s" % \
(self, cls, name, cls._meta.auto_field)
super(UUIDField, self).contribute_to_class(cls, name)
cls._meta.has_auto_field = True
cls._meta.auto_field = self
else:
super(UUIDField, self).contribute_to_class(cls, name)
def create_uuid(self):
if not self.version or self.version == 4:
return uuid.uuid4()
elif self.version == 1:
return uuid.uuid1(self.node, self.clock_seq)
elif self.version == 2:
raise UUIDVersionError("UUID version 2 is not supported.")
elif self.version == 3:
return uuid.uuid3(self.namespace, self.name)
elif self.version == 5:
return uuid.uuid5(self.namespace, self.name)
else:
raise UUIDVersionError("UUID version %s is not valid." % self.version)
def pre_save(self, model_instance, add):
if self.auto and add:
value = unicode(self.create_uuid())
setattr(model_instance, self.attname, value)
return value
else:
value = super(UUIDField, self).pre_save(model_instance, add)
if self.auto and not value:
value = unicode(self.create_uuid())
setattr(model_instance, self.attname, value)
return value
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect the _actual_ field.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.CharField"
args, kwargs = introspector(self)
# That's our definition!
return (field_class, args, kwargs)

View File

@ -0,0 +1,79 @@
from django.db import models
from django.core.exceptions import ImproperlyConfigured
from django import forms
from django.conf import settings
try:
from keyczar import keyczar
except ImportError:
raise ImportError('Using an encrypted field requires the Keyczar module. '
'You can obtain Keyczar from http://www.keyczar.org/.')
class BaseEncryptedField(models.Field):
prefix = 'enc_str:::'
def __init__(self, *args, **kwargs):
if not hasattr(settings, 'ENCRYPTED_FIELD_KEYS_DIR'):
raise ImproperlyConfigured('You must set the '
'ENCRYPTED_FIELD_KEYS_DIR setting to your Keyczar keys directory.')
self.crypt = keyczar.Crypter.Read(settings.ENCRYPTED_FIELD_KEYS_DIR)
super(BaseEncryptedField, self).__init__(*args, **kwargs)
def to_python(self, value):
if value and (value.startswith(self.prefix)):
retval = self.crypt.Decrypt(value[len(self.prefix):])
else:
retval = value
return retval
def get_db_prep_value(self, value, connection, prepared=False):
if value and not value.startswith(self.prefix):
value = self.prefix + self.crypt.Encrypt(value)
return value
class EncryptedTextField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase
def get_internal_type(self):
return 'TextField'
def formfield(self, **kwargs):
defaults = {'widget': forms.Textarea}
defaults.update(kwargs)
return super(EncryptedTextField, self).formfield(**defaults)
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect the _actual_ field.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.TextField"
args, kwargs = introspector(self)
# That's our definition!
return (field_class, args, kwargs)
class EncryptedCharField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase
def __init__(self, max_length=None, *args, **kwargs):
if max_length:
max_length += len(self.prefix)
super(EncryptedCharField, self).__init__(max_length=max_length, *args, **kwargs)
def get_internal_type(self):
return "CharField"
def formfield(self, **kwargs):
defaults = {'max_length': self.max_length}
defaults.update(kwargs)
return super(EncryptedCharField, self).formfield(**defaults)
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect the _actual_ field.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.CharField"
args, kwargs = introspector(self)
# That's our definition!
return (field_class, args, kwargs)

View File

@ -0,0 +1,101 @@
"""
JSONField automatically serializes most Python terms to JSON data.
Creates a TEXT field with a default value of "{}". See test_json.py for
more information.
from django.db import models
from django_extensions.db.fields import json
class LOL(models.Model):
extra = json.JSONField()
"""
import datetime
from decimal import Decimal
from django.db import models
from django.conf import settings
from django.utils import simplejson
from django.utils.encoding import smart_unicode
class JSONEncoder(simplejson.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return str(obj)
elif isinstance(obj, datetime.datetime):
assert settings.TIME_ZONE == 'UTC'
return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
return simplejson.JSONEncoder.default(self, obj)
def dumps(value):
return JSONEncoder().encode(value)
def loads(txt):
value = simplejson.loads(
txt,
parse_float=Decimal,
encoding=settings.DEFAULT_CHARSET
)
return value
class JSONDict(dict):
"""
Hack so repr() called by dumpdata will output JSON instead of
Python formatted data. This way fixtures will work!
"""
def __repr__(self):
return dumps(self)
class JSONList(list):
"""
As above
"""
def __repr__(self):
return dumps(self)
class JSONField(models.TextField):
"""JSONField is a generic textfield that neatly serializes/unserializes
JSON objects seamlessly. Main thingy must be a dict object."""
# Used so to_python() is called
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
if 'default' not in kwargs:
kwargs['default'] = '{}'
models.TextField.__init__(self, *args, **kwargs)
def to_python(self, value):
"""Convert our string value to JSON after we load it from the DB"""
if not value:
return {}
elif isinstance(value, basestring):
res = loads(value)
if isinstance(res, dict):
return JSONDict(**res)
else:
return JSONList(res)
else:
return value
def get_db_prep_save(self, value, connection):
"""Convert our JSON object to a string before we save"""
if not isinstance(value, (list, dict)):
return super(JSONField, self).get_db_prep_save("", connection)
else:
return super(JSONField, self).get_db_prep_save(dumps(value),
connection)
def south_field_triple(self):
"Returns a suitable description of this field for South."
# We'll just introspect the _actual_ field.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.TextField"
args, kwargs = introspector(self)
# That's our definition!
return (field_class, args, kwargs)

View File

@ -0,0 +1,75 @@
"""
Django Extensions abstract base model classes.
"""
import datetime
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django_extensions.db.fields import (ModificationDateTimeField,
CreationDateTimeField, AutoSlugField)
class TimeStampedModel(models.Model):
""" TimeStampedModel
An abstract base class model that provides self-managed "created" and
"modified" fields.
"""
created = CreationDateTimeField(_('created'))
modified = ModificationDateTimeField(_('modified'))
class Meta:
get_latest_by = 'modified'
ordering = ('-modified', '-created',)
abstract = True
class TitleSlugDescriptionModel(models.Model):
""" TitleSlugDescriptionModel
An abstract base class model that provides title and description fields
and a self-managed "slug" field that populates from the title.
"""
title = models.CharField(_('title'), max_length=255)
slug = AutoSlugField(_('slug'), populate_from='title')
description = models.TextField(_('description'), blank=True, null=True)
class Meta:
abstract = True
class ActivatorModelManager(models.Manager):
""" ActivatorModelManager
Manager to return instances of ActivatorModel: SomeModel.objects.active() / .inactive()
"""
def active(self):
""" Returns active instances of ActivatorModel: SomeModel.objects.active() """
return self.get_query_set().filter(status=ActivatorModel.ACTIVE_STATUS)
def inactive(self):
""" Returns inactive instances of ActivatorModel: SomeModel.objects.inactive() """
return self.get_query_set().filter(status=ActivatorModel.INACTIVE_STATUS)
class ActivatorModel(models.Model):
""" ActivatorModel
An abstract base class model that provides activate and deactivate fields.
"""
INACTIVE_STATUS, ACTIVE_STATUS = range(2)
STATUS_CHOICES = (
(INACTIVE_STATUS, _('Inactive')),
(ACTIVE_STATUS, _('Active')),
)
status = models.IntegerField(_('status'), choices=STATUS_CHOICES,
default=ACTIVE_STATUS)
activate_date = models.DateTimeField(blank=True, null=True,
help_text=_('keep empty for an immediate activation'))
deactivate_date = models.DateTimeField(blank=True, null=True,
help_text=_('keep empty for indefinite activation'))
objects = ActivatorModelManager()
class Meta:
ordering = ('status', '-activate_date',)
abstract = True
def save(self, *args, **kwargs):
if not self.activate_date:
self.activate_date = datetime.datetime.now()
super(ActivatorModel, self).save(*args, **kwargs)

View File

@ -0,0 +1,26 @@
"""
Daily cleanup job.
Can be run as a cronjob to clean out old data from the database (only expired
sessions at the moment).
"""
from django_extensions.management.jobs import DailyJob
class Job(DailyJob):
help = "Cache (db) cleanup Job"
def execute(self):
from django.conf import settings
from django.db import connection
import os
if settings.CACHE_BACKEND.startswith('db://'):
os.environ['TZ'] = settings.TIME_ZONE
table_name = settings.CACHE_BACKEND[5:]
cursor = connection.cursor()
cursor.execute("DELETE FROM %s WHERE %s < UTC_TIMESTAMP()" % \
(connection.ops.quote_name(table_name),
connection.ops.quote_name('expires')))
transaction.commit_unless_managed()

View File

@ -0,0 +1,16 @@
"""
Daily cleanup job.
Can be run as a cronjob to clean out old data from the database (only expired
sessions at the moment).
"""
from django_extensions.management.jobs import DailyJob
class Job(DailyJob):
help = "Django Daily Cleanup Job"
def execute(self):
from django.core import management
management.call_command("cleanup")

View File

@ -0,0 +1,16 @@
"""
Sets up the terminal color scheme.
"""
from django.core.management import color
from django.utils import termcolors
def color_style():
style = color.color_style()
if color.supports_color():
style.URL = termcolors.make_style(fg='green', opts=('bold',))
style.MODULE = termcolors.make_style(fg='yellow')
style.MODULE_NAME = termcolors.make_style(opts=('bold',))
style.URL_NAME = termcolors.make_style(fg='red')
return style

View File

@ -0,0 +1,42 @@
from django.core.management.base import NoArgsCommand
from django_extensions.management.utils import get_project_root
from random import choice
from optparse import make_option
from os.path import join as _j
import os
class Command(NoArgsCommand):
option_list = NoArgsCommand.option_list + (
make_option('--optimize', '-o', '-O', action='store_true', dest='optimize',
help='Remove optimized python bytecode files'),
make_option('--path', '-p', action='store', dest='path',
help='Specify path to recurse into'),
)
help = "Removes all python bytecode compiled files from the project."
requires_model_validation = False
def handle_noargs(self, **options):
project_root = options.get("path", None)
if not project_root:
project_root = get_project_root()
exts = options.get("optimize", False) and [".pyc", ".pyo"] or [".pyc"]
verbose = int(options.get("verbosity", 1)) > 1
for root, dirs, files in os.walk(project_root):
for file in files:
ext = os.path.splitext(file)[1]
if ext in exts:
full_path = _j(root, file)
if verbose:
print full_path
os.remove(full_path)
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
make_option('--verbosity', '-v', action="store", dest="verbosity",
default='1', type='choice', choices=['0', '1', '2'],
help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"),
)

View File

@ -0,0 +1,40 @@
from django.core.management.base import NoArgsCommand
from django_extensions.management.utils import get_project_root
from random import choice
from optparse import make_option
from os.path import join as _j
import py_compile
import os
class Command(NoArgsCommand):
option_list = NoArgsCommand.option_list + (
make_option('--path', '-p', action='store', dest='path',
help='Specify path to recurse into'),
)
help = "Compile python bytecode files for the project."
requires_model_validation = False
def handle_noargs(self, **options):
project_root = options.get("path", None)
if not project_root:
project_root = get_project_root()
verbose = int(options.get("verbosity", 1)) > 1
for root, dirs, files in os.walk(project_root):
for file in files:
ext = os.path.splitext(file)[1]
if ext == ".py":
full_path = _j(root, file)
if verbose:
print "%sc" % full_path
py_compile.compile(full_path)
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
make_option('--verbosity', '-v', action="store", dest="verbosity",
default='1', type='choice', choices=['0', '1', '2'],
help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"),
)

View File

@ -0,0 +1,146 @@
import os
import re
import django_extensions
from django.conf import settings
from django.db import connection
from django.core.management.base import CommandError, LabelCommand, _make_writeable
from django.template import Template, Context
from django_extensions.settings import REPLACEMENTS
from django_extensions.utils.dia2django import dia2django
from optparse import make_option
class Command(LabelCommand):
option_list = LabelCommand.option_list + (
make_option('--template', '-t', action='store', dest='app_template',
help='The path to the app template'),
make_option('--parent_path', '-p', action='store', dest='parent_path',
help='The parent path of the application to be created'),
make_option('-d', action='store_true', dest='dia_parse',
help='Generate model.py and admin.py from [APP_NAME].dia file'),
make_option('--diagram', action='store', dest='dia_path',
help='The diagram path of the app to be created. -d is implied'),
)
help = ("Creates an application directory structure for the specified "
"application name.")
args = "APP_NAME"
label = 'application name'
requires_model_validation = False
can_import_settings = True
def handle_label(self, label, **options):
project_dir = os.getcwd()
project_name = os.path.split(project_dir)[-1]
app_name = label
app_template = options.get('app_template') or os.path.join(django_extensions.__path__[0], 'conf', 'app_template')
app_dir = os.path.join(options.get('parent_path') or project_dir, app_name)
dia_path = options.get('dia_path') or os.path.join(project_dir, '%s.dia' % app_name)
if not os.path.exists(app_template):
raise CommandError("The template path, %r, does not exist." % app_template)
if not re.search(r'^\w+$', label):
raise CommandError("%r is not a valid application name. Please use only numbers, letters and underscores." % label)
dia_parse = options.get('dia_path') or options.get('dia_parse')
if dia_parse:
if not os.path.exists(dia_path):
raise CommandError("The diagram path, %r, does not exist."
% dia_path)
if app_name in settings.INSTALLED_APPS:
raise CommandError("The application %s should not be defined "
"in the settings file. Please remove %s now, and add it "
"after using this command." % (app_name, app_name))
tables = [name for name in connection.introspection.table_names()
if name.startswith('%s_' % app_name)]
if tables:
raise CommandError("%r application has tables in the database. "
"Please delete them." % app_name)
try:
os.makedirs(app_dir)
except OSError, e:
raise CommandError(e)
copy_template(app_template, app_dir, project_name, app_name)
if dia_parse:
generate_models_and_admin(dia_path, app_dir, project_name, app_name)
print "Application %r created." % app_name
print "Please add now %r and any other dependent application in " \
"settings.INSTALLED_APPS, and run 'manage syncdb'" % app_name
def copy_template(app_template, copy_to, project_name, app_name):
"""copies the specified template directory to the copy_to location"""
import shutil
app_template = os.path.normpath(app_template)
# walks the template structure and copies it
for d, subdirs, files in os.walk(app_template):
relative_dir = d[len(app_template) + 1:]
d_new = os.path.join(copy_to, relative_dir).replace('app_name', app_name)
if relative_dir and not os.path.exists(d_new):
os.mkdir(d_new)
for i, subdir in enumerate(subdirs):
if subdir.startswith('.'):
del subdirs[i]
replacements = {'app_name': app_name, 'project_name': project_name}
replacements.update(REPLACEMENTS)
for f in files:
if f.endswith('.pyc') or f.startswith('.DS_Store'):
continue
path_old = os.path.join(d, f)
path_new = os.path.join(d_new, f.replace('app_name', app_name))
if os.path.exists(path_new):
path_new = os.path.join(d_new, f)
if os.path.exists(path_new):
continue
if path_new.endswith('.tmpl'):
path_new = path_new[:-5]
fp_old = open(path_old, 'r')
fp_new = open(path_new, 'w')
fp_new.write(Template(fp_old.read()).render(Context(replacements)))
fp_old.close()
fp_new.close()
try:
shutil.copymode(path_old, path_new)
_make_writeable(path_new)
except OSError:
sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
def generate_models_and_admin(dia_path, app_dir, project_name, app_name):
"""Generates the models.py and admin.py files"""
def format_text(string, indent=False):
"""format string in lines of 80 or less characters"""
retval = ''
while string:
line = string[:77]
last_space = line.rfind(' ')
if last_space != -1 and len(string) > 77:
retval += "%s \\\n" % string[:last_space]
string = string[last_space + 1:]
else:
retval += "%s\n" % string
string = ''
if string and indent:
string = ' %s' % string
return retval
model_path = os.path.join(app_dir, 'models.py')
admin_path = os.path.join(app_dir, 'admin.py')
models_txt = 'from django.db import models\n' + dia2django(dia_path)
open(model_path, 'w').write(models_txt)
classes = re.findall('class (\w+)', models_txt)
admin_txt = 'from django.contrib.admin import site, ModelAdmin\n' + \
format_text('from %s.%s.models import %s' %
(project_name, app_name, ', '.join(classes)), indent=True)
admin_txt += format_text('\n\n%s' %
'\n'.join(map((lambda t: 'site.register(%s)' % t), classes)))
open(admin_path, 'w').write(admin_txt)

View File

@ -0,0 +1,80 @@
import os
from django.core.management.base import CommandError, AppCommand, _make_writeable
from optparse import make_option
class Command(AppCommand):
option_list = AppCommand.option_list + (
make_option('--name', '-n', action='store', dest='command_name', default='sample',
help='The name to use for the management command'),
make_option('--base', '-b', action='store', dest='base_command', default='Base',
help='The base class used for implementation of this command. Should be one of Base, App, Label, or NoArgs'),
)
help = ("Creates a Django management command directory structure for the given app name"
" in the current directory.")
args = "[appname]"
label = 'application name'
requires_model_validation = False
# Can't import settings during this command, because they haven't
# necessarily been created.
can_import_settings = True
def handle_app(self, app, **options):
directory = os.getcwd()
app_name = app.__name__.split('.')[-2]
project_dir = os.path.join(directory, app_name)
if not os.path.exists(project_dir):
try:
os.mkdir(project_dir)
except OSError, e:
raise CommandError(e)
copy_template('command_template', project_dir, options.get('command_name'), '%sCommand' % options.get('base_command'))
def copy_template(template_name, copy_to, command_name, base_command):
"""copies the specified template directory to the copy_to location"""
import django_extensions
import re
import shutil
template_dir = os.path.join(django_extensions.__path__[0], 'conf', template_name)
handle_method = "handle(self, *args, **options)"
if base_command == 'AppCommand':
handle_method = "handle_app(self, app, **options)"
elif base_command == 'LabelCommand':
handle_method = "handle_label(self, label, **options)"
elif base_command == 'NoArgsCommand':
handle_method = "handle_noargs(self, **options)"
# walks the template structure and copies it
for d, subdirs, files in os.walk(template_dir):
relative_dir = d[len(template_dir) + 1:]
if relative_dir and not os.path.exists(os.path.join(copy_to, relative_dir)):
os.mkdir(os.path.join(copy_to, relative_dir))
for i, subdir in enumerate(subdirs):
if subdir.startswith('.'):
del subdirs[i]
for f in files:
if f.endswith('.pyc') or f.startswith('.DS_Store'):
continue
path_old = os.path.join(d, f)
path_new = os.path.join(copy_to, relative_dir, f.replace('sample', command_name))
if os.path.exists(path_new):
path_new = os.path.join(copy_to, relative_dir, f)
if os.path.exists(path_new):
continue
path_new = path_new.rstrip(".tmpl")
fp_old = open(path_old, 'r')
fp_new = open(path_new, 'w')
fp_new.write(fp_old.read().replace('{{ command_name }}', command_name).replace('{{ base_command }}', base_command).replace('{{ handle_method }}', handle_method))
fp_old.close()
fp_new.close()
try:
shutil.copymode(path_old, path_new)
_make_writeable(path_new)
except OSError:
sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))

View File

@ -0,0 +1,56 @@
import os
import sys
from django.core.management.base import CommandError, AppCommand, _make_writeable
class Command(AppCommand):
help = ("Creates a Django jobs command directory structure for the given app name in the current directory.")
args = "[appname]"
label = 'application name'
requires_model_validation = False
# Can't import settings during this command, because they haven't
# necessarily been created.
can_import_settings = True
def handle_app(self, app, **options):
app_dir = os.path.dirname(app.__file__)
copy_template('jobs_template', app_dir)
def copy_template(template_name, copy_to):
"""copies the specified template directory to the copy_to location"""
import django_extensions
import re
import shutil
template_dir = os.path.join(django_extensions.__path__[0], 'conf', template_name)
# walks the template structure and copies it
for d, subdirs, files in os.walk(template_dir):
relative_dir = d[len(template_dir) + 1:]
if relative_dir and not os.path.exists(os.path.join(copy_to, relative_dir)):
os.mkdir(os.path.join(copy_to, relative_dir))
for i, subdir in enumerate(subdirs):
if subdir.startswith('.'):
del subdirs[i]
for f in files:
if f.endswith('.pyc') or f.startswith('.DS_Store'):
continue
path_old = os.path.join(d, f)
path_new = os.path.join(copy_to, relative_dir, f)
if os.path.exists(path_new):
path_new = os.path.join(copy_to, relative_dir, f)
if os.path.exists(path_new):
continue
path_new = path_new.rstrip(".tmpl")
fp_old = open(path_old, 'r')
fp_new = open(path_new, 'w')
fp_new.write(fp_old.read())
fp_old.close()
fp_new.close()
try:
shutil.copymode(path_old, path_new)
_make_writeable(path_new)
except OSError:
sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))

View File

@ -0,0 +1,64 @@
from django.core.management.base import LabelCommand, CommandError
from django.utils.encoding import force_unicode
class Command(LabelCommand):
help = "Outputs the specified model as a form definition to the shell."
args = "[app.model]"
label = 'application name and model name'
requires_model_validation = True
can_import_settings = True
def handle_label(self, label, **options):
return describe_form(label)
def describe_form(label, fields=None):
"""
Returns a string describing a form based on the model
"""
from django.db.models.loading import get_model
try:
app_name, model_name = label.split('.')[-2:]
except (IndexError, ValueError):
raise CommandError("Need application and model name in the form: appname.model")
model = get_model(app_name, model_name)
opts = model._meta
field_list = []
for f in opts.fields + opts.many_to_many:
if not f.editable:
continue
if fields and not f.name in fields:
continue
formfield = f.formfield()
if not '__dict__' in dir(formfield):
continue
attrs = {}
valid_fields = ['required', 'initial', 'max_length', 'min_length', 'max_value', 'min_value', 'max_digits', 'decimal_places', 'choices', 'help_text', 'label']
for k, v in formfield.__dict__.items():
if k in valid_fields and v != None:
# ignore defaults, to minimize verbosity
if k == 'required' and v:
continue
if k == 'help_text' and not v:
continue
if k == 'widget':
attrs[k] = v.__class__
elif k in ['help_text', 'label']:
attrs[k] = force_unicode(v).strip()
else:
attrs[k] = v
params = ', '.join(['%s=%r' % (k, v) for k, v in attrs.items()])
field_list.append(' %(field_name)s = forms.%(field_type)s(%(params)s)' % {'field_name': f.name,
'field_type': formfield.__class__.__name__,
'params': params})
return '''
from django import forms
from %(app_name)s.models import %(object_name)s
class %(object_name)sForm(forms.Form):
%(field_list)s
''' % {'app_name': app_name, 'object_name': opts.object_name, 'field_list': '\n'.join(field_list)}

View File

@ -0,0 +1,509 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
Title: Dumpscript management command
Project: Hardytools (queryset-refactor version)
Author: Will Hardy (http://willhardy.com.au)
Date: June 2008
Usage: python manage.py dumpscript appname > scripts/scriptname.py
$Revision: 217 $
Description:
Generates a Python script that will repopulate the database using objects.
The advantage of this approach is that it is easy to understand, and more
flexible than directly populating the database, or using XML.
* It also allows for new defaults to take effect and only transfers what is
needed.
* If a new database schema has a NEW ATTRIBUTE, it is simply not
populated (using a default value will make the transition smooth :)
* If a new database schema REMOVES AN ATTRIBUTE, it is simply ignored
and the data moves across safely (I'm assuming we don't want this
attribute anymore.
* Problems may only occur if there is a new model and is now a required
ForeignKey for an existing model. But this is easy to fix by editing the
populate script :)
Improvements:
See TODOs and FIXMEs scattered throughout :-)
"""
import sys
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from django.core.management.base import BaseCommand
from django.utils.encoding import smart_unicode, force_unicode
from django.contrib.contenttypes.models import ContentType
class Command(BaseCommand):
help = 'Dumps the data as a customised python script.'
args = '[appname ...]'
def handle(self, *app_labels, **options):
# Get the models we want to export
models = get_models(app_labels)
# A dictionary is created to keep track of all the processed objects,
# so that foreign key references can be made using python variable names.
# This variable "context" will be passed around like the town bicycle.
context = {}
# Create a dumpscript object and let it format itself as a string
print Script(models=models, context=context)
def get_models(app_labels):
""" Gets a list of models for the given app labels, with some exceptions.
TODO: If a required model is referenced, it should also be included.
Or at least discovered with a get_or_create() call.
"""
from django.db.models import get_app, get_apps, get_model
from django.db.models import get_models as get_all_models
# These models are not to be output, e.g. because they can be generated automatically
# TODO: This should be "appname.modelname" string
from django.contrib.contenttypes.models import ContentType
EXCLUDED_MODELS = (ContentType, )
models = []
# If no app labels are given, return all
if not app_labels:
for app in get_apps():
models += [m for m in get_all_models(app) if m not in EXCLUDED_MODELS]
# Get all relevant apps
for app_label in app_labels:
# If a specific model is mentioned, get only that model
if "." in app_label:
app_label, model_name = app_label.split(".", 1)
models.append(get_model(app_label, model_name))
# Get all models for a given app
else:
models += [m for m in get_all_models(get_app(app_label)) if m not in EXCLUDED_MODELS]
return models
class Code(object):
""" A snippet of python script.
This keeps track of import statements and can be output to a string.
In the future, other features such as custom indentation might be included
in this class.
"""
def __init__(self):
self.imports = {}
self.indent = -1
def __str__(self):
""" Returns a string representation of this script.
"""
if self.imports:
sys.stderr.write(repr(self.import_lines))
return flatten_blocks([""] + self.import_lines + [""] + self.lines, num_indents=self.indent)
else:
return flatten_blocks(self.lines, num_indents=self.indent)
def get_import_lines(self):
""" Takes the stored imports and converts them to lines
"""
if self.imports:
return ["from %s import %s" % (value, key) for key, value in self.imports.items()]
else:
return []
import_lines = property(get_import_lines)
class ModelCode(Code):
" Produces a python script that can recreate data for a given model class. "
def __init__(self, model, context={}):
self.model = model
self.context = context
self.instances = []
self.indent = 0
def get_imports(self):
""" Returns a dictionary of import statements, with the variable being
defined as the key.
"""
return {self.model.__name__: smart_unicode(self.model.__module__)}
imports = property(get_imports)
def get_lines(self):
""" Returns a list of lists or strings, representing the code body.
Each list is a block, each string is a statement.
"""
code = []
for counter, item in enumerate(self.model._default_manager.all()):
instance = InstanceCode(instance=item, id=counter + 1, context=self.context)
self.instances.append(instance)
if instance.waiting_list:
code += instance.lines
# After each instance has been processed, try again.
# This allows self referencing fields to work.
for instance in self.instances:
if instance.waiting_list:
code += instance.lines
return code
lines = property(get_lines)
class InstanceCode(Code):
" Produces a python script that can recreate data for a given model instance. "
def __init__(self, instance, id, context={}):
""" We need the instance in question and an id """
self.instance = instance
self.model = self.instance.__class__
self.context = context
self.variable_name = "%s_%s" % (self.instance._meta.db_table, id)
self.skip_me = None
self.instantiated = False
self.indent = 0
self.imports = {}
self.waiting_list = list(self.model._meta.fields)
self.many_to_many_waiting_list = {}
for field in self.model._meta.many_to_many:
self.many_to_many_waiting_list[field] = list(getattr(self.instance, field.name).all())
def get_lines(self, force=False):
""" Returns a list of lists or strings, representing the code body.
Each list is a block, each string is a statement.
force (True or False): if an attribute object cannot be included,
it is usually skipped to be processed later. With 'force' set, there
will be no waiting: a get_or_create() call is written instead.
"""
code_lines = []
# Don't return anything if this is an instance that should be skipped
if self.skip():
return []
# Initialise our new object
# e.g. model_name_35 = Model()
code_lines += self.instantiate()
# Add each field
# e.g. model_name_35.field_one = 1034.91
# model_name_35.field_two = "text"
code_lines += self.get_waiting_list()
if force:
# TODO: Check that M2M are not affected
code_lines += self.get_waiting_list(force=force)
# Print the save command for our new object
# e.g. model_name_35.save()
if code_lines:
code_lines.append("%s.save()\n" % (self.variable_name))
code_lines += self.get_many_to_many_lines(force=force)
return code_lines
lines = property(get_lines)
def skip(self):
""" Determine whether or not this object should be skipped.
If this model is a parent of a single subclassed instance, skip it.
The subclassed instance will create this parent instance for us.
TODO: Allow the user to force its creation?
"""
if self.skip_me is not None:
return self.skip_me
try:
# Django trunk since r7722 uses CollectedObjects instead of dict
from django.db.models.query import CollectedObjects
sub_objects = CollectedObjects()
except ImportError:
# previous versions don't have CollectedObjects
sub_objects = {}
self.instance._collect_sub_objects(sub_objects)
if reduce(lambda x, y: x + y, [self.model in so._meta.parents for so in sub_objects.keys()]) == 1:
pk_name = self.instance._meta.pk.name
key = '%s_%s' % (self.model.__name__, getattr(self.instance, pk_name))
self.context[key] = None
self.skip_me = True
else:
self.skip_me = False
return self.skip_me
def instantiate(self):
" Write lines for instantiation "
# e.g. model_name_35 = Model()
code_lines = []
if not self.instantiated:
code_lines.append("%s = %s()" % (self.variable_name, self.model.__name__))
self.instantiated = True
# Store our variable name for future foreign key references
pk_name = self.instance._meta.pk.name
key = '%s_%s' % (self.model.__name__, getattr(self.instance, pk_name))
self.context[key] = self.variable_name
return code_lines
def get_waiting_list(self, force=False):
" Add lines for any waiting fields that can be completed now. "
code_lines = []
# Process normal fields
for field in list(self.waiting_list):
try:
# Find the value, add the line, remove from waiting list and move on
value = get_attribute_value(self.instance, field, self.context, force=force)
code_lines.append('%s.%s = %s' % (self.variable_name, field.name, value))
self.waiting_list.remove(field)
except SkipValue, e:
# Remove from the waiting list and move on
self.waiting_list.remove(field)
continue
except DoLater, e:
# Move on, maybe next time
continue
return code_lines
def get_many_to_many_lines(self, force=False):
""" Generates lines that define many to many relations for this instance. """
lines = []
for field, rel_items in self.many_to_many_waiting_list.items():
for rel_item in list(rel_items):
try:
pk_name = rel_item._meta.pk.name
key = '%s_%s' % (rel_item.__class__.__name__, getattr(rel_item, pk_name))
value = "%s" % self.context[key]
lines.append('%s.%s.add(%s)' % (self.variable_name, field.name, value))
self.many_to_many_waiting_list[field].remove(rel_item)
except KeyError:
if force:
value = "%s.objects.get(%s=%s)" % (rel_item._meta.object_name, pk_name, getattr(rel_item, pk_name))
lines.append('%s.%s.add(%s)' % (self.variable_name, field.name, value))
self.many_to_many_waiting_list[field].remove(rel_item)
if lines:
lines.append("")
return lines
class Script(Code):
" Produces a complete python script that can recreate data for the given apps. "
def __init__(self, models, context={}):
self.models = models
self.context = context
self.indent = -1
self.imports = {}
def get_lines(self):
""" Returns a list of lists or strings, representing the code body.
Each list is a block, each string is a statement.
"""
code = [self.FILE_HEADER.strip()]
# Queue and process the required models
for model_class in queue_models(self.models, context=self.context):
sys.stderr.write('Processing model: %s\n' % model_class.model.__name__)
code.append(model_class.import_lines)
code.append("")
code.append(model_class.lines)
# Process left over foreign keys from cyclic models
for model in self.models:
sys.stderr.write('Re-processing model: %s\n' % model.model.__name__)
for instance in model.instances:
if instance.waiting_list or instance.many_to_many_waiting_list:
code.append(instance.get_lines(force=True))
return code
lines = property(get_lines)
# A user-friendly file header
FILE_HEADER = """
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file has been automatically generated, changes may be lost if you
# go and generate it again. It was generated with the following command:
# %s
import datetime
from decimal import Decimal
from django.contrib.contenttypes.models import ContentType
def run():
""" % " ".join(sys.argv)
# HELPER FUNCTIONS
#-------------------------------------------------------------------------------
def flatten_blocks(lines, num_indents=-1):
""" Takes a list (block) or string (statement) and flattens it into a string
with indentation.
"""
# The standard indent is four spaces
INDENTATION = " " * 4
if not lines:
return ""
# If this is a string, add the indentation and finish here
if isinstance(lines, basestring):
return INDENTATION * num_indents + lines
# If this is not a string, join the lines and recurse
return "\n".join([flatten_blocks(line, num_indents + 1) for line in lines])
def get_attribute_value(item, field, context, force=False):
""" Gets a string version of the given attribute's value, like repr() might. """
# Find the value of the field, catching any database issues
try:
value = getattr(item, field.name)
except ObjectDoesNotExist:
raise SkipValue('Could not find object for %s.%s, ignoring.\n' % (item.__class__.__name__, field.name))
# AutoField: We don't include the auto fields, they'll be automatically recreated
if isinstance(field, models.AutoField):
raise SkipValue()
# Some databases (eg MySQL) might store boolean values as 0/1, this needs to be cast as a bool
elif isinstance(field, models.BooleanField) and value is not None:
return repr(bool(value))
# Post file-storage-refactor, repr() on File/ImageFields no longer returns the path
elif isinstance(field, models.FileField):
return repr(force_unicode(value))
# ForeignKey fields, link directly using our stored python variable name
elif isinstance(field, models.ForeignKey) and value is not None:
# Special case for contenttype foreign keys: no need to output any
# content types in this script, as they can be generated again
# automatically.
# NB: Not sure if "is" will always work
if field.rel.to is ContentType:
return 'ContentType.objects.get(app_label="%s", model="%s")' % (value.app_label, value.model)
# Generate an identifier (key) for this foreign object
pk_name = value._meta.pk.name
key = '%s_%s' % (value.__class__.__name__, getattr(value, pk_name))
if key in context:
variable_name = context[key]
# If the context value is set to None, this should be skipped.
# This identifies models that have been skipped (inheritance)
if variable_name is None:
raise SkipValue()
# Return the variable name listed in the context
return "%s" % variable_name
elif force:
return "%s.objects.get(%s=%s)" % (value._meta.object_name, pk_name, getattr(value, pk_name))
else:
raise DoLater('(FK) %s.%s\n' % (item.__class__.__name__, field.name))
# A normal field (e.g. a python built-in)
else:
return repr(value)
def queue_models(models, context):
""" Works an an appropriate ordering for the models.
This isn't essential, but makes the script look nicer because
more instances can be defined on their first try.
"""
# Max number of cycles allowed before we call it an infinite loop.
MAX_CYCLES = 5
model_queue = []
number_remaining_models = len(models)
allowed_cycles = MAX_CYCLES
while number_remaining_models > 0:
previous_number_remaining_models = number_remaining_models
model = models.pop(0)
# If the model is ready to be processed, add it to the list
if check_dependencies(model, model_queue):
model_class = ModelCode(model=model, context=context)
model_queue.append(model_class)
# Otherwise put the model back at the end of the list
else:
models.append(model)
# Check for infinite loops.
# This means there is a cyclic foreign key structure
# That cannot be resolved by re-ordering
number_remaining_models = len(models)
if number_remaining_models == previous_number_remaining_models:
allowed_cycles -= 1
if allowed_cycles <= 0:
# Add the remaining models, but do not remove them from the model list
missing_models = [ModelCode(model=m, context=context) for m in models]
model_queue += missing_models
# Replace the models with the model class objects
# (sure, this is a little bit of hackery)
models[:] = missing_models
break
else:
allowed_cycles = MAX_CYCLES
return model_queue
def check_dependencies(model, model_queue):
" Check that all the depenedencies for this model are already in the queue. "
# A list of allowed links: existing fields, itself and the special case ContentType
allowed_links = [m.model.__name__ for m in model_queue] + [model.__name__, 'ContentType']
# For each ForeignKey or ManyToMany field, check that a link is possible
for field in model._meta.fields + model._meta.many_to_many:
if field.rel and field.rel.to.__name__ not in allowed_links:
return False
return True
# EXCEPTIONS
#-------------------------------------------------------------------------------
class SkipValue(Exception):
""" Value could not be parsed or should simply be skipped. """
class DoLater(Exception):
""" Value could not be parsed or should simply be skipped. """

View File

@ -0,0 +1,119 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User, Group
from optparse import make_option
from sys import stdout
from csv import writer
FORMATS = [
'address',
'google',
'outlook',
'linkedin',
'vcard',
]
def full_name(first_name, last_name, username, **extra):
name = u" ".join(n for n in [first_name, last_name] if n)
if not name:
return username
return name
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--group', '-g', action='store', dest='group', default=None,
help='Limit to users which are part of the supplied group name'),
make_option('--format', '-f', action='store', dest='format', default=FORMATS[0],
help="output format. May be one of '" + "', '".join(FORMATS) + "'."),
)
help = ("Export user email address list in one of a number of formats.")
args = "[output file]"
label = 'filename to save to'
requires_model_validation = True
can_import_settings = True
encoding = 'utf-8' # RED_FLAG: add as an option -DougN
def handle(self, *args, **options):
if len(args) > 1:
raise CommandError("extra arguments supplied")
group = options['group']
if group and not Group.objects.filter(name=group).count() == 1:
names = u"', '".join(g['name'] for g in Group.objects.values('name')).encode('utf-8')
if names:
names = "'" + names + "'."
raise CommandError("Unknown group '" + group + "'. Valid group names are: " + names)
if len(args) and args[0] != '-':
outfile = file(args[0], 'w')
else:
outfile = stdout
qs = User.objects.all().order_by('last_name', 'first_name', 'username', 'email')
if group:
qs = qs.filter(group__name=group).distinct()
qs = qs.values('last_name', 'first_name', 'username', 'email')
getattr(self, options['format'])(qs, outfile)
def address(self, qs, out):
"""simple single entry per line in the format of:
"full name" <my@address.com>;
"""
out.write(u"\n".join(u'"%s" <%s>;' % (full_name(**ent), ent['email'])
for ent in qs).encode(self.encoding))
out.write("\n")
def google(self, qs, out):
"""CSV format suitable for importing into google GMail
"""
csvf = writer(out)
csvf.writerow(['Name', 'Email'])
for ent in qs:
csvf.writerow([full_name(**ent).encode(self.encoding),
ent['email'].encode(self.encoding)])
def outlook(self, qs, out):
"""CSV format suitable for importing into outlook
"""
csvf = writer(out)
columns = ['Name', 'E-mail Address', 'Notes', 'E-mail 2 Address', 'E-mail 3 Address',
'Mobile Phone', 'Pager', 'Company', 'Job Title', 'Home Phone', 'Home Phone 2',
'Home Fax', 'Home Address', 'Business Phone', 'Business Phone 2',
'Business Fax', 'Business Address', 'Other Phone', 'Other Fax', 'Other Address']
csvf.writerow(columns)
empty = [''] * (len(columns) - 2)
for ent in qs:
csvf.writerow([full_name(**ent).encode(self.encoding),
ent['email'].encode(self.encoding)] + empty)
def linkedin(self, qs, out):
"""CSV format suitable for importing into linkedin Groups.
perfect for pre-approving members of a linkedin group.
"""
csvf = writer(out)
csvf.writerow(['First Name', 'Last Name', 'Email'])
for ent in qs:
csvf.writerow([ent['first_name'].encode(self.encoding),
ent['last_name'].encode(self.encoding),
ent['email'].encode(self.encoding)])
def vcard(self, qs, out):
try:
import vobject
except ImportError:
print self.style.ERROR("Please install python-vobject to use the vcard export format.")
import sys
sys.exit(1)
for ent in qs:
card = vobject.vCard()
card.add('fn').value = full_name(**ent)
if not ent['last_name'] and not ent['first_name']:
# fallback to fullname, if both first and lastname are not declared
card.add('n').value = vobject.vcard.Name(full_name(**ent))
else:
card.add('n').value = vobject.vcard.Name(ent['last_name'], ent['first_name'])
emailpart = card.add('email')
emailpart.value = ent['email']
emailpart.type_param = 'INTERNET'
out.write(card.serialize().encode(self.encoding))

View File

@ -0,0 +1,26 @@
from django.core.management.base import LabelCommand
from django.template.loader import find_template
from django.template import TemplateDoesNotExist
import sys
def get_template_path(path):
try:
template = find_template(path)
return template[1].name
except TemplateDoesNotExist:
return None
class Command(LabelCommand):
help = "Finds the location of the given template by resolving its path"
args = "[template_path]"
label = 'template path'
def handle_label(self, template_path, **options):
path = get_template_path(template_path)
if path is None:
sys.stderr.write("No template found\n")
sys.exit(1)
else:
print path

View File

@ -0,0 +1,11 @@
from random import choice
from django.core.management.base import NoArgsCommand
class Command(NoArgsCommand):
help = "Generates a new SECRET_KEY that can be used in a project settings file."
requires_model_validation = False
def handle_noargs(self, **options):
return ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])

View File

@ -0,0 +1,69 @@
from django.core.management.base import BaseCommand, CommandError
from optparse import make_option
from django_extensions.management.modelviz import generate_dot
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--disable-fields', '-d', action='store_true', dest='disable_fields',
help='Do not show the class member fields'),
make_option('--group-models', '-g', action='store_true', dest='group_models',
help='Group models together respective to their application'),
make_option('--all-applications', '-a', action='store_true', dest='all_applications',
help='Automatically include all applications from INSTALLED_APPS'),
make_option('--output', '-o', action='store', dest='outputfile',
help='Render output file. Type of output dependent on file extensions. Use png or jpg to render graph to image.'),
make_option('--layout', '-l', action='store', dest='layout', default='dot',
help='Layout to be used by GraphViz for visualization. Layouts: circo dot fdp neato nop nop1 nop2 twopi'),
make_option('--verbose-names', '-n', action='store_true', dest='verbose_names',
help='Use verbose_name of models and fields'),
make_option('--language', '-L', action='store', dest='language',
help='Specify language used for verbose_name localization'),
make_option('--exclude-columns', '-x', action='store', dest='exclude_columns',
help='Exclude specific column(s) from the graph. Can also load exclude list from file.'),
make_option('--exclude-models', '-X', action='store', dest='exclude_models',
help='Exclude specific model(s) from the graph. Can also load exclude list from file.'),
)
help = ("Creates a GraphViz dot file for the specified app names. You can pass multiple app names and they will all be combined into a single model. Output is usually directed to a dot file.")
args = "[appname]"
label = 'application name'
requires_model_validation = True
can_import_settings = True
def handle(self, *args, **options):
if len(args) < 1 and not options['all_applications']:
raise CommandError("need one or more arguments for appname")
dotdata = generate_dot(args, **options)
if options['outputfile']:
self.render_output(dotdata, **options)
else:
self.print_output(dotdata)
def print_output(self, dotdata):
print dotdata.encode('utf-8')
def render_output(self, dotdata, **kwargs):
try:
import pygraphviz
except ImportError, e:
raise CommandError("need pygraphviz python module ( apt-get install python-pygraphviz )")
vizdata = ' '.join(dotdata.split("\n")).strip().encode('utf-8')
version = pygraphviz.__version__.rstrip("-svn")
try:
if [int(v) for v in version.split('.')] < (0, 36):
# HACK around old/broken AGraph before version 0.36 (ubuntu ships with this old version)
import tempfile
tmpfile = tempfile.NamedTemporaryFile()
tmpfile.write(vizdata)
tmpfile.seek(0)
vizdata = tmpfile.name
except ValueError:
pass
graph = pygraphviz.AGraph(vizdata)
graph.layout(prog=kwargs['layout'])
graph.draw(kwargs['outputfile'])

View File

@ -0,0 +1,42 @@
from django.core.management.base import BaseCommand
import sys
import smtpd
import asyncore
class Command(BaseCommand):
help = "Starts a test mail server for development."
args = '[optional port number or ippaddr:port]'
requires_model_validation = False
def handle(self, addrport='', *args, **options):
if args:
raise CommandError('Usage is runserver %s' % self.args)
if not addrport:
addr = ''
port = '1025'
else:
try:
addr, port = addrport.split(':')
except ValueError:
addr, port = '', addrport
if not addr:
addr = '127.0.0.1'
if not port.isdigit():
raise CommandError("%r is not a valid port number." % port)
else:
port = int(port)
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
def inner_run():
print "Now accepting mail at %s:%s" % (addr, port)
server = smtpd.DebuggingServer((addr, port), None)
asyncore.loop()
try:
inner_run()
except KeyboardInterrupt:
pass

View File

@ -0,0 +1,38 @@
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
import os
import re
ANNOTATION_RE = re.compile("#[\s]*?(TODO|FIXME|HACK|XXX)[\s:]?(.+)")
class Command(BaseCommand):
help = 'Show all annotations like TODO, FIXME, HACK or XXX in your py files.'
args = 'tag'
label = 'annotation tag (TODO, FIXME, HACK, XXX)'
def handle(self, *args, **options):
# don't add django internal code
apps = filter(lambda app: not app.startswith('django.contrib'),
settings.INSTALLED_APPS)
for app_dir in apps:
for top, dirs, files in os.walk(app_dir):
for f in files:
if os.path.splitext(f)[1] in ['.py']:
fpath = os.path.join(top, f)
annotation_lines = []
with open(fpath, 'r') as f:
i = 0
for line in f.readlines():
i += 1
if ANNOTATION_RE.search(line):
tag, msg = ANNOTATION_RE.findall(line)[0]
if len(args) == 1:
search_for_tag = args[0].upper()
if not search_for_tag == tag:
break
annotation_lines.append("[%3s] %-5s %s" % (i, tag, msg.strip()))
if annotation_lines:
print fpath+":"
for annotation in annotation_lines:
print " * "+annotation
print

View File

@ -0,0 +1,38 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
import getpass
class Command(BaseCommand):
help = "Clone of the UNIX program ``passwd'', for django.contrib.auth."
requires_model_validation = False
def handle(self, *args, **options):
if len(args) > 1:
raise CommandError("need exactly one or zero arguments for username")
if args:
username, = args
else:
username = getpass.getuser()
try:
u = User.objects.get(username=username)
except User.DoesNotExist:
raise CommandError("user %s does not exist" % username)
print "Changing password for user", u.username
p1 = p2 = ""
while "" in (p1, p2) or p1 != p2:
p1 = getpass.getpass()
p2 = getpass.getpass("Password (again): ")
if p1 != p2:
print "Passwords do not match, try again"
elif "" in (p1, p2):
raise CommandError("aborted")
u.set_password(p1)
u.save()
return "Password changed successfully for user %s\n" % u.username

View File

@ -0,0 +1,47 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from django.contrib.sessions.models import Session
import re
SESSION_RE = re.compile("^[0-9a-f]{20,40}$")
class Command(BaseCommand):
help = ("print the user information for the provided session key. "
"this is very helpful when trying to track down the person who "
"experienced a site crash.")
args = "session_key"
label = 'session key for the user'
requires_model_validation = True
can_import_settings = True
def handle(self, *args, **options):
if len(args) > 1:
raise CommandError("extra arguments supplied")
if len(args) < 1:
raise CommandError("session_key argument missing")
key = args[0].lower()
if not SESSION_RE.match(key):
raise CommandError("malformed session key")
try:
session = Session.objects.get(pk=key)
except Session.DoesNotExist:
print "Session Key does not exist. Expired?"
return
data = session.get_decoded()
print 'Session to Expire:', session.expire_date
print 'Raw Data:', data
uid = data.get('_auth_user_id', None)
if uid is None:
print 'No user associated with session'
return
print "User id:", uid
try:
user = User.objects.get(pk=uid)
except User.DoesNotExist:
print "No user associated with that id."
return
for key in ['username', 'email', 'first_name', 'last_name']:
print key + ': ' + getattr(user, key)

View File

@ -0,0 +1,174 @@
"""
originally from http://www.djangosnippets.org/snippets/828/ by dnordberg
"""
from django.conf import settings
from django.core.management.base import CommandError, BaseCommand
from django.db import connection
import django
import logging
import re
from optparse import make_option
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--noinput', action='store_false',
dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.'),
make_option('--no-utf8', action='store_true',
dest='no_utf8_support', default=False,
help='Tells Django to not create a UTF-8 charset database'),
make_option('-U', '--user', action='store',
dest='user', default=None,
help='Use another user for the database then defined in settings.py'),
make_option('-P', '--password', action='store',
dest='password', default=None,
help='Use another password for the database then defined in settings.py'),
make_option('-D', '--dbname', action='store',
dest='dbname', default=None,
help='Use another database name then defined in settings.py (For PostgreSQL this defaults to "template1")'),
make_option('-R', '--router', action='store',
dest='router', default=None,
help='Use this router-database other then defined in settings.py'),
)
help = "Resets the database for this project."
def set_db_settings(self, *args, **options):
if django.get_version() >= "1.2":
router = options.get('router')
if router == None:
return False
# retrieve this with the 'using' argument
dbinfo = settings.DATABASES.get(router)
settings.DATABASE_ENGINE = dbinfo.get('ENGINE').split('.')[-1]
settings.DATABASE_USER = dbinfo.get('USER')
settings.DATABASE_PASSWORD = dbinfo.get('PASSWORD')
settings.DATABASE_NAME = dbinfo.get('NAME')
settings.DATABASE_HOST = dbinfo.get('HOST')
settings.DATABASE_PORT = dbinfo.get('PORT')
return True
else:
# settings are set for django < 1.2 no modification needed
return True
def handle(self, *args, **options):
"""
Resets the database for this project.
Note: Transaction wrappers are in reverse as a work around for
autocommit, anybody know how to do this the right way?
"""
if django.get_version() >= "1.2":
got_db_settings = self.set_db_settings(*args, **options)
if not got_db_settings:
raise CommandError("You are using Django %s which requires to specify the db-router.\nPlease specify the router by adding --router=<routername> to this command." % django.get_version())
return
verbosity = int(options.get('verbosity', 1))
if options.get('interactive'):
confirm = raw_input("""
You have requested a database reset.
This will IRREVERSIBLY DESTROY
ALL data in the database "%s".
Are you sure you want to do this?
Type 'yes' to continue, or 'no' to cancel: """ % (settings.DATABASE_NAME,))
else:
confirm = 'yes'
if confirm != 'yes':
print "Reset cancelled."
return
postgis = re.compile('.*postgis')
engine = settings.DATABASE_ENGINE
user = options.get('user', settings.DATABASE_USER)
if user == None:
user = settings.DATABASE_USER
password = options.get('password', settings.DATABASE_PASSWORD)
if password == None:
password = settings.DATABASE_PASSWORD
if engine == 'sqlite3':
import os
try:
logging.info("Unlinking sqlite3 database")
os.unlink(settings.DATABASE_NAME)
except OSError:
pass
elif engine == 'mysql':
import MySQLdb as Database
kwargs = {
'user': user,
'passwd': password,
}
if settings.DATABASE_HOST.startswith('/'):
kwargs['unix_socket'] = settings.DATABASE_HOST
else:
kwargs['host'] = settings.DATABASE_HOST
if settings.DATABASE_PORT:
kwargs['port'] = int(settings.DATABASE_PORT)
connection = Database.connect(**kwargs)
drop_query = 'DROP DATABASE IF EXISTS %s' % settings.DATABASE_NAME
utf8_support = options.get('no_utf8_support', False) and '' or 'CHARACTER SET utf8'
create_query = 'CREATE DATABASE %s %s' % (settings.DATABASE_NAME, utf8_support)
logging.info('Executing... "' + drop_query + '"')
connection.query(drop_query)
logging.info('Executing... "' + create_query + '"')
connection.query(create_query)
elif engine == 'postgresql' or engine == 'postgresql_psycopg2' or postgis.match(engine):
if engine == 'postgresql':
import psycopg as Database
elif engine == 'postgresql_psycopg2' or postgis.match(engine):
import psycopg2 as Database
if settings.DATABASE_NAME == '':
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("You need to specify DATABASE_NAME in your Django settings file.")
database_name = options.get('dbname', 'template1')
if options.get('dbname') == None:
database_name = 'template1'
conn_string = "dbname=%s" % database_name
if settings.DATABASE_USER:
conn_string += " user=%s" % user
if settings.DATABASE_PASSWORD:
conn_string += " password='%s'" % password
if settings.DATABASE_HOST:
conn_string += " host=%s" % settings.DATABASE_HOST
if settings.DATABASE_PORT:
conn_string += " port=%s" % settings.DATABASE_PORT
connection = Database.connect(conn_string)
connection.set_isolation_level(0) # autocommit false
cursor = connection.cursor()
drop_query = 'DROP DATABASE %s' % settings.DATABASE_NAME
logging.info('Executing... "' + drop_query + '"')
try:
cursor.execute(drop_query)
except Database.ProgrammingError, e:
logging.info("Error: %s" % str(e))
# Encoding should be SQL_ASCII (7-bit postgres default) or prefered UTF8 (8-bit)
create_query = """CREATE DATABASE %s WITH OWNER = %s ENCODING = 'UTF8' """ % (settings.DATABASE_NAME, settings.DATABASE_USER)
if postgis.match(engine):
create_query += 'TEMPLATE = template_postgis '
if settings.DEFAULT_TABLESPACE:
create_query += 'TABLESPACE = %s;' % (settings.DEFAULT_TABLESPACE)
else:
create_query += ';'
logging.info('Executing... "' + create_query + '"')
cursor.execute(create_query)
else:
raise CommandError("Unknown database engine %s" % engine)
if verbosity >= 2 or options.get('interactive'):
print "Reset successful."

View File

@ -0,0 +1,60 @@
from django.core.management.base import LabelCommand
from optparse import make_option
from django_extensions.management.jobs import get_job, print_jobs
class Command(LabelCommand):
option_list = LabelCommand.option_list + (
make_option('--list', '-l', action="store_true", dest="list_jobs",
help="List all jobs with their description"),
)
help = "Run a single maintenance job."
args = "[app_name] job_name"
label = ""
requires_model_validation = True
def runjob(self, app_name, job_name, options):
verbosity = int(options.get('verbosity', 1))
if verbosity > 1:
print "Executing job: %s (app: %s)" % (job_name, app_name)
try:
job = get_job(app_name, job_name)
except KeyError, e:
if app_name:
print "Error: Job %s for applabel %s not found" % (app_name, job_name)
else:
print "Error: Job %s not found" % job_name
print "Use -l option to view all the available jobs"
return
try:
job().execute()
except Exception, e:
import traceback
print "ERROR OCCURED IN JOB: %s (APP: %s)" % (job_name, app_name)
print "START TRACEBACK:"
traceback.print_exc()
print "END TRACEBACK\n"
def handle(self, *args, **options):
app_name = None
job_name = None
if len(args) == 1:
job_name = args[0]
elif len(args) == 2:
app_name, job_name = args
if options.get('list_jobs'):
print_jobs(only_scheduled=False, show_when=True, show_appname=True)
else:
if not job_name:
print "Run a single maintenance job. Please specify the name of the job."
return
self.runjob(app_name, job_name, options)
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
make_option('--verbosity', '-v', action="store", dest="verbosity",
default='1', type='choice', choices=['0', '1', '2'],
help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"),
)

View File

@ -0,0 +1,95 @@
from django.core.management.base import LabelCommand
from optparse import make_option
from django_extensions.management.jobs import get_jobs, print_jobs
class Command(LabelCommand):
option_list = LabelCommand.option_list + (
make_option('--list', '-l', action="store_true", dest="list_jobs",
help="List all jobs with their description"),
)
help = "Runs scheduled maintenance jobs."
args = "[minutely hourly daily weekly monthly yearly]"
label = ""
requires_model_validation = True
def usage_msg(self):
print "Run scheduled jobs. Please specify 'minutely', 'hourly', 'daily', 'weekly', 'monthly' or 'yearly'"
def runjobs(self, when, options):
verbosity = int(options.get('verbosity', 1))
jobs = get_jobs(when, only_scheduled=True)
list = jobs.keys()
list.sort()
for app_name, job_name in list:
job = jobs[(app_name, job_name)]
if verbosity > 1:
print "Executing %s job: %s (app: %s)" % (when, job_name, app_name)
try:
job().execute()
except Exception, e:
import traceback
print "ERROR OCCURED IN %s JOB: %s (APP: %s)" % (when.upper(), job_name, app_name)
print "START TRACEBACK:"
traceback.print_exc()
print "END TRACEBACK\n"
def runjobs_by_signals(self, when, options):
""" Run jobs from the signals """
# Thanks for Ian Holsman for the idea and code
from django_extensions.management import signals
from django.db import models
from django.conf import settings
verbosity = int(options.get('verbosity', 1))
for app_name in settings.INSTALLED_APPS:
try:
__import__(app_name + '.management', '', '', [''])
except ImportError:
pass
for app in models.get_apps():
if verbosity > 1:
app_name = '.'.join(app.__name__.rsplit('.')[:-1])
print "Sending %s job signal for: %s" % (when, app_name)
if when == 'minutely':
signals.run_minutely_jobs.send(sender=app, app=app)
elif when == 'hourly':
signals.run_hourly_jobs.send(sender=app, app=app)
elif when == 'daily':
signals.run_daily_jobs.send(sender=app, app=app)
elif when == 'weekly':
signals.run_weekly_jobs.send(sender=app, app=app)
elif when == 'monthly':
signals.run_monthly_jobs.send(sender=app, app=app)
elif when == 'yearly':
signals.run_yearly_jobs.send(sender=app, app=app)
def handle(self, *args, **options):
when = None
if len(args) > 1:
self.usage_msg()
return
elif len(args) == 1:
if not args[0] in ['minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly']:
self.usage_msg()
return
else:
when = args[0]
if options.get('list_jobs'):
print_jobs(when, only_scheduled=True, show_when=True, show_appname=True)
else:
if not when:
self.usage_msg()
return
self.runjobs(when, options)
self.runjobs_by_signals(when, options)
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
make_option('--verbosity', '-v', action="store", dest="verbosity",
default='1', type='choice', choices=['0', '1', '2'],
help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"),
)

View File

@ -0,0 +1,226 @@
"""
runprofileserver.py
Starts a lightweight Web server with profiling enabled.
Credits for kcachegrind support taken from lsprofcalltree.py go to:
David Allouche
Jp Calderone & Itamar Shtull-Trauring
Johan Dahlin
"""
from django.core.management.base import BaseCommand, CommandError
from optparse import make_option
from datetime import datetime
import os
import sys
def label(code):
if isinstance(code, str):
return ('~', 0, code) # built-in functions ('~' sorts at the end)
else:
return '%s %s:%d' % (code.co_name,
code.co_filename,
code.co_firstlineno)
class KCacheGrind(object):
def __init__(self, profiler):
self.data = profiler.getstats()
self.out_file = None
def output(self, out_file):
self.out_file = out_file
print >> out_file, 'events: Ticks'
self._print_summary()
for entry in self.data:
self._entry(entry)
def _print_summary(self):
max_cost = 0
for entry in self.data:
totaltime = int(entry.totaltime * 1000)
max_cost = max(max_cost, totaltime)
print >> self.out_file, 'summary: %d' % (max_cost,)
def _entry(self, entry):
out_file = self.out_file
code = entry.code
#print >> out_file, 'ob=%s' % (code.co_filename,)
if isinstance(code, str):
print >> out_file, 'fi=~'
else:
print >> out_file, 'fi=%s' % (code.co_filename,)
print >> out_file, 'fn=%s' % (label(code),)
inlinetime = int(entry.inlinetime * 1000)
if isinstance(code, str):
print >> out_file, '0 ', inlinetime
else:
print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
# recursive calls are counted in entry.calls
if entry.calls:
calls = entry.calls
else:
calls = []
if isinstance(code, str):
lineno = 0
else:
lineno = code.co_firstlineno
for subentry in calls:
self._subentry(lineno, subentry)
print >> out_file
def _subentry(self, lineno, subentry):
out_file = self.out_file
code = subentry.code
#print >> out_file, 'cob=%s' % (code.co_filename,)
print >> out_file, 'cfn=%s' % (label(code),)
if isinstance(code, str):
print >> out_file, 'cfi=~'
print >> out_file, 'calls=%d 0' % (subentry.callcount,)
else:
print >> out_file, 'cfi=%s' % (code.co_filename,)
print >> out_file, 'calls=%d %d' % (
subentry.callcount, code.co_firstlineno)
totaltime = int(subentry.totaltime * 1000)
print >> out_file, '%d %d' % (lineno, totaltime)
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--noreload', action='store_false', dest='use_reloader', default=True,
help='Tells Django to NOT use the auto-reloader.'),
make_option('--adminmedia', dest='admin_media_path', default='',
help='Specifies the directory from which to serve admin media.'),
make_option('--prof-path', dest='prof_path', default='/tmp',
help='Specifies the directory which to save profile information in.'),
make_option('--nomedia', action='store_true', dest='no_media', default=False,
help='Do not profile MEDIA_URL and ADMIN_MEDIA_URL'),
make_option('--use-cprofile', action='store_true', dest='use_cprofile', default=False,
help='Use cProfile if available, this is disabled per default because of incompatibilities.'),
make_option('--kcachegrind', action='store_true', dest='use_lsprof', default=False,
help='Create kcachegrind compatible lsprof files, this requires and automatically enables cProfile.'),
)
help = "Starts a lightweight Web server with profiling enabled."
args = '[optional port number, or ipaddr:port]'
# Validation is called explicitly each time the server is reloaded.
requires_model_validation = False
def handle(self, addrport='', *args, **options):
import django
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
from django.core.handlers.wsgi import WSGIHandler
if args:
raise CommandError('Usage is runserver %s' % self.args)
if not addrport:
addr = ''
port = '8000'
else:
try:
addr, port = addrport.split(':')
except ValueError:
addr, port = '', addrport
if not addr:
addr = '127.0.0.1'
if not port.isdigit():
raise CommandError("%r is not a valid port number." % port)
use_reloader = options.get('use_reloader', True)
admin_media_path = options.get('admin_media_path', '')
shutdown_message = options.get('shutdown_message', '')
no_media = options.get('no_media', False)
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
def inner_run():
from django.conf import settings
import os
import time
import hotshot
USE_CPROFILE = options.get('use_cprofile', False)
USE_LSPROF = options.get('use_lsprof', False)
if USE_LSPROF:
USE_CPROFILE = True
if USE_CPROFILE:
try:
import cProfile
USE_CPROFILE = True
except ImportError:
print "cProfile disabled, module cannot be imported!"
USE_CPROFILE = False
if USE_LSPROF and not USE_CPROFILE:
raise SystemExit("Kcachegrind compatible output format required cProfile from Python 2.5")
prof_path = options.get('prof_path', '/tmp')
def make_profiler_handler(inner_handler):
def handler(environ, start_response):
path_info = environ['PATH_INFO']
# normally /media/ is MEDIA_URL, but in case still check it in case it's differently
# should be hardly a penalty since it's an OR expression.
# TODO: fix this to check the configuration settings and not make assumpsions about where media are on the url
if no_media and (path_info.startswith('/media') or path_info.startswith(settings.MEDIA_URL)):
return inner_handler(environ, start_response)
path_name = path_info.strip("/").replace('/', '.') or "root"
profname = "%s.%s.prof" % (path_name, datetime.now().isoformat())
profname = os.path.join(prof_path, profname)
if USE_CPROFILE:
prof = cProfile.Profile()
else:
prof = hotshot.Profile(profname)
start = datetime.now()
try:
return prof.runcall(inner_handler, environ, start_response)
finally:
# seeing how long the request took is important!
elap = datetime.now() - start
elapms = elap.seconds * 1000.0 + elap.microseconds / 1000.0
if USE_LSPROF:
kg = KCacheGrind(prof)
kg.output(file(profname, 'w'))
elif USE_CPROFILE:
prof.dump_stats(profname)
profname2 = "%s.%06dms.%s.prof" % (path_name, elapms, datetime.now().isoformat())
profname2 = os.path.join(prof_path, profname2)
os.rename(profname, profname2)
return handler
print "Validating models..."
self.validate(display_num_errors=True)
print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
print "Development server is running at http://%s:%s/" % (addr, port)
print "Quit the server with %s." % quit_command
try:
path = admin_media_path or django.__path__[0] + '/contrib/admin/media'
handler = make_profiler_handler(AdminMediaHandler(WSGIHandler(), path))
run(addr, int(port), handler)
except WSGIServerException, e:
# Use helpful error messages instead of ugly tracebacks.
ERRORS = {
13: "You don't have permission to access that port.",
98: "That port is already in use.",
99: "That IP address can't be assigned-to.",
}
try:
error_text = ERRORS[e.args[0].args[0]]
except (AttributeError, KeyError):
error_text = str(e)
sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
# Need to use an OS exit because sys.exit doesn't work in a thread
os._exit(1)
except KeyboardInterrupt:
if shutdown_message:
print shutdown_message
sys.exit(0)
if use_reloader:
from django.utils import autoreload
autoreload.main(inner_run)
else:
inner_run()

View File

@ -0,0 +1,167 @@
from django.core.management.base import BaseCommand
from django.core.management.color import no_style
from optparse import make_option
import sys
import os
import imp
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
def vararg_callback(option, opt_str, opt_value, parser):
parser.rargs.insert(0, opt_value)
value = []
for arg in parser.rargs:
# stop on --foo like options
if arg[:2] == "--" and len(arg) > 2:
break
# stop on -a like options
if arg[:1] == "-":
break
value.append(arg)
del parser.rargs[:len(value)]
setattr(parser.values, option.dest, value)
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--fixtures', action='store_true', dest='infixtures', default=False,
help='Only look in app.fixtures subdir'),
make_option('--noscripts', action='store_true', dest='noscripts', default=False,
help='Look in app.scripts subdir'),
make_option('-s', '--silent', action='store_true', dest='silent', default=False,
help='Run silently, do not show errors and tracebacks'),
make_option('--no-traceback', action='store_true', dest='no_traceback', default=False,
help='Do not show tracebacks'),
make_option('--script-args', action='callback', callback=vararg_callback, type='string',
help='Space-separated argument list to be passed to the scripts. Note that the '
'same arguments will be passed to all named scripts.'),
)
help = 'Runs a script in django context.'
args = "script [script ...]"
def handle(self, *scripts, **options):
from django.db.models import get_apps
NOTICE = self.style.SQL_TABLE
NOTICE2 = self.style.SQL_FIELD
ERROR = self.style.ERROR
ERROR2 = self.style.NOTICE
subdirs = []
if not options.get('noscripts'):
subdirs.append('scripts')
if options.get('infixtures'):
subdirs.append('fixtures')
verbosity = int(options.get('verbosity', 1))
show_traceback = options.get('traceback', True)
if show_traceback is None:
# XXX: traceback is set to None from Django ?
show_traceback = True
no_traceback = options.get('no_traceback', False)
if no_traceback:
show_traceback = False
silent = options.get('silent', False)
if silent:
verbosity = 0
if len(subdirs) < 1:
print NOTICE("No subdirs to run left.")
return
if len(scripts) < 1:
print ERROR("Script name required.")
return
def run_script(mod, *script_args):
try:
mod.run(*script_args)
except Exception, e:
if silent:
return
if verbosity > 0:
print ERROR("Exception while running run() in '%s'" % mod.__name__)
if show_traceback:
raise
def my_import(mod):
if verbosity > 1:
print NOTICE("Check for %s" % mod)
# check if module exists before importing
try:
path = None
full_path = mod.split('.')
for package in mod.split('.')[:-1]:
module_tuple = imp.find_module(package, path)
path = imp.load_module(package, *module_tuple).__path__
imp.find_module(mod.split('.')[-1], path)
except (ImportError, AttributeError):
return False
t = __import__(mod, [], [], [" "])
#if verbosity > 1:
# print NOTICE("Found script %s ..." % mod)
if hasattr(t, "run"):
if verbosity > 1:
print NOTICE2("Found script '%s' ..." % mod)
#if verbosity > 1:
# print NOTICE("found run() in %s. executing..." % mod)
return t
else:
if verbosity > 1:
print ERROR2("Find script '%s' but no run() function found." % mod)
def find_modules_for_script(script):
""" find script module which contains 'run' attribute """
modules = []
# first look in apps
for app in get_apps():
app_name = app.__name__.split(".")[:-1] # + ['fixtures']
for subdir in subdirs:
mod = my_import(".".join(app_name + [subdir, script]))
if mod:
modules.append(mod)
# try app.DIR.script import
sa = script.split(".")
for subdir in subdirs:
nn = ".".join(sa[:-1] + [subdir, sa[-1]])
mod = my_import(nn)
if mod:
modules.append(mod)
# try direct import
if script.find(".") != -1:
mod = my_import(script)
if mod:
modules.append(mod)
return modules
if options.get('script_args'):
script_args = options['script_args']
else:
script_args = []
for script in scripts:
modules = find_modules_for_script(script)
if not modules:
if verbosity > 0 and not silent:
print ERROR("No module for script '%s' found" % script)
for mod in modules:
if verbosity > 1:
print NOTICE2("Running script '%s' ..." % mod.__name__)
run_script(mod, *script_args)
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
make_option('--verbosity', '-v', action="store", dest="verbosity",
default='1', type='choice', choices=['0', '1', '2'],
help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"),
)

View File

@ -0,0 +1,97 @@
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from optparse import make_option
import os
import sys
try:
from django.contrib.staticfiles.handlers import StaticFilesHandler
USE_STATICFILES = 'django.contrib.staticfiles' in settings.INSTALLED_APPS
except ImportError, e:
USE_STATICFILES = False
def null_technical_500_response(request, exc_type, exc_value, tb):
raise exc_type, exc_value, tb
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--noreload', action='store_false', dest='use_reloader', default=True,
help='Tells Django to NOT use the auto-reloader.'),
make_option('--browser', action='store_true', dest='open_browser',
help='Tells Django to open a browser.'),
make_option('--adminmedia', dest='admin_media_path', default='',
help='Specifies the directory from which to serve admin media.'),
make_option('--threaded', action='store_true', dest='threaded',
help='Run in multithreaded mode.'),
)
if USE_STATICFILES:
option_list += (
make_option('--nostatic', action="store_false", dest='use_static_handler', default=True,
help='Tells Django to NOT automatically serve static files at STATIC_URL.'),
make_option('--insecure', action="store_true", dest='insecure_serving', default=False,
help='Allows serving static files even if DEBUG is False.'),
)
help = "Starts a lightweight Web server for development."
args = '[optional port number, or ipaddr:port]'
# Validation is called explicitly each time the server is reloaded.
requires_model_validation = False
def handle(self, addrport='', *args, **options):
import django
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
from django.core.handlers.wsgi import WSGIHandler
try:
from werkzeug import run_simple, DebuggedApplication
except ImportError:
raise CommandError("Werkzeug is required to use runserver_plus. Please visit http://werkzeug.pocoo.org/download")
# usurp django's handler
from django.views import debug
debug.technical_500_response = null_technical_500_response
if args:
raise CommandError('Usage is runserver %s' % self.args)
if not addrport:
addr = ''
port = '8000'
else:
try:
addr, port = addrport.split(':')
except ValueError:
addr, port = '', addrport
if not addr:
addr = '127.0.0.1'
if not port.isdigit():
raise CommandError("%r is not a valid port number." % port)
threaded = options.get('threaded', False)
use_reloader = options.get('use_reloader', True)
open_browser = options.get('open_browser', False)
admin_media_path = options.get('admin_media_path', '')
shutdown_message = options.get('shutdown_message', '')
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
def inner_run():
print "Validating models..."
self.validate(display_num_errors=True)
print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
print "Development server is running at http://%s:%s/" % (addr, port)
print "Using the Werkzeug debugger (http://werkzeug.pocoo.org/)"
print "Quit the server with %s." % quit_command
path = admin_media_path or django.__path__[0] + '/contrib/admin/media'
handler = AdminMediaHandler(WSGIHandler(), path)
if USE_STATICFILES:
use_static_handler = options.get('use_static_handler', True)
insecure_serving = options.get('insecure_serving', False)
if use_static_handler and (settings.DEBUG or insecure_serving) and 'django.contrib.staticfiles' in settings.INSTALLED_APPS:
handler = StaticFilesHandler(handler)
if open_browser:
import webbrowser
url = "http://%s:%s/" % (addr, port)
webbrowser.open(url)
run_simple(addr, int(port), DebuggedApplication(handler, True),
use_reloader=use_reloader, use_debugger=True, threaded=threaded)
inner_run()

View File

@ -0,0 +1,76 @@
"""
set_fake_emails.py
Give all users a new email account. Useful for testing in a
development environment. As such, this command is only available when
setting.DEBUG is True.
"""
from optparse import make_option
from django.conf import settings
from django.core.management.base import NoArgsCommand, CommandError
DEFAULT_FAKE_EMAIL = '%(username)s@example.com'
class Command(NoArgsCommand):
option_list = NoArgsCommand.option_list + (
make_option('--email', dest='default_email', default=DEFAULT_FAKE_EMAIL,
help='Use this as the new email format.'),
make_option('-a', '--no-admin', action="store_true", dest='no_admin', default=False,
help='Do not change administrator accounts'),
make_option('-s', '--no-staff', action="store_true", dest='no_staff', default=False,
help='Do not change staff accounts'),
make_option('--include', dest='include_regexp', default=None,
help='Include usernames matching this regexp.'),
make_option('--exclude', dest='exclude_regexp', default=None,
help='Exclude usernames matching this regexp.'),
make_option('--include-groups', dest='include_groups', default=None,
help='Include users matching this group. (use comma seperation for multiple groups)'),
make_option('--exclude-groups', dest='exclude_groups', default=None,
help='Exclude users matching this group. (use comma seperation for multiple groups)'),
)
help = '''DEBUG only: give all users a new email based on their account data ("%s" by default). Possible parameters are: username, first_name, last_name''' % (DEFAULT_FAKE_EMAIL, )
requires_model_validation = False
def handle_noargs(self, **options):
if not settings.DEBUG:
raise CommandError('Only available in debug mode')
from django.contrib.auth.models import User, Group
email = options.get('default_email', DEFAULT_FAKE_EMAIL)
include_regexp = options.get('include_regexp', None)
exclude_regexp = options.get('exclude_regexp', None)
include_groups = options.get('include_groups', None)
exclude_groups = options.get('exclude_groups', None)
no_admin = options.get('no_admin', False)
no_staff = options.get('no_staff', False)
users = User.objects.all()
if no_admin:
users = users.exclude(is_superuser=True)
if no_staff:
users = users.exclude(is_staff=True)
if exclude_groups:
groups = Group.objects.filter(name__in=exclude_groups.split(","))
if groups:
users = users.exclude(groups__in=groups)
else:
raise CommandError("No group matches filter: %s" % exclude_groups)
if include_groups:
groups = Group.objects.filter(name__in=include_groups.split(","))
if groups:
users = users.filter(groups__in=groups)
else:
raise CommandError("No groups matches filter: %s" % include_groups)
if exclude_regexp:
users = users.exclude(username__regex=exclude_regexp)
if include_regexp:
users = users.filter(username__regex=include_regexp)
for user in users:
user.email = email % {'username': user.username,
'first_name': user.first_name,
'last_name': user.last_name}
user.save()
print 'Changed %d emails' % users.count()

View File

@ -0,0 +1,44 @@
"""
set_fake_passwords.py
Reset all user passwords to a common value. Useful for testing in a
development environment. As such, this command is only available when
setting.DEBUG is True.
"""
from optparse import make_option
from django.conf import settings
from django.core.management.base import NoArgsCommand, CommandError
DEFAULT_FAKE_PASSWORD = 'password'
class Command(NoArgsCommand):
option_list = NoArgsCommand.option_list + (
make_option('--prompt', dest='prompt_passwd', default=False, action='store_true',
help='Prompts for the new password to apply to all users'),
make_option('--password', dest='default_passwd', default=DEFAULT_FAKE_PASSWORD,
help='Use this as default password.'),
)
help = 'DEBUG only: sets all user passwords to a common value ("%s" by default)' % (DEFAULT_FAKE_PASSWORD, )
requires_model_validation = False
def handle_noargs(self, **options):
if not settings.DEBUG:
raise CommandError('Only available in debug mode')
from django.contrib.auth.models import User
if options.get('prompt_passwd', False):
from getpass import getpass
passwd = getpass('Password: ')
if not passwd:
raise CommandError('You must enter a valid password')
else:
passwd = options.get('default_passwd', DEFAULT_FAKE_PASSWORD)
user = User()
user.set_password(passwd)
count = User.objects.all().update(password=user.password)
print 'Reset %d passwords' % count

View File

@ -0,0 +1,151 @@
import os
from django.core.management.base import NoArgsCommand
from optparse import make_option
class Command(NoArgsCommand):
option_list = NoArgsCommand.option_list + (
make_option('--ipython', action='store_true', dest='ipython',
help='Tells Django to use IPython, not BPython.'),
make_option('--plain', action='store_true', dest='plain',
help='Tells Django to use plain Python, not BPython nor IPython.'),
make_option('--no-pythonrc', action='store_true', dest='no_pythonrc',
help='Tells Django to use plain Python, not IPython.'),
make_option('--print-sql', action='store_true', default=False,
help="Print SQL queries as they're executed"),
make_option('--dont-load', action='append', dest='dont_load', default=[],
help='Ignore autoloading of some apps/models. Can be used several times.'),
)
help = "Like the 'shell' command but autoloads the models of all installed Django apps."
requires_model_validation = True
def handle_noargs(self, **options):
# XXX: (Temporary) workaround for ticket #1796: force early loading of all
# models from installed apps. (this is fixed by now, but leaving it here
# for people using 0.96 or older trunk (pre [5919]) versions.
from django.db.models.loading import get_models, get_apps
loaded_models = get_models()
use_ipython = options.get('ipython', False)
use_plain = options.get('plain', False)
use_pythonrc = not options.get('no_pythonrc', True)
if options.get("print_sql", False):
# Code from http://gist.github.com/118990
from django.db.backends import util
try:
import sqlparse
except ImportError:
sqlparse = None
class PrintQueryWrapper(util.CursorDebugWrapper):
def execute(self, sql, params=()):
try:
return self.cursor.execute(sql, params)
finally:
raw_sql = self.db.ops.last_executed_query(self.cursor, sql, params)
if sqlparse:
print sqlparse.format(raw_sql, reindent=True)
else:
print raw_sql
print
util.CursorDebugWrapper = PrintQueryWrapper
# Set up a dictionary to serve as the environment for the shell, so
# that tab completion works on objects that are imported at runtime.
# See ticket 5082.
from django.conf import settings
imported_objects = {'settings': settings}
dont_load_cli = options.get('dont_load') # optparse will set this to [] if it doensnt exists
dont_load_conf = getattr(settings, 'SHELL_PLUS_DONT_LOAD', [])
dont_load = dont_load_cli + dont_load_conf
model_aliases = getattr(settings, 'SHELL_PLUS_MODEL_ALIASES', {})
for app_mod in get_apps():
app_models = get_models(app_mod)
if not app_models:
continue
app_name = app_mod.__name__.split('.')[-2]
if app_name in dont_load:
continue
app_aliases = model_aliases.get(app_name, {})
model_labels = []
for model in app_models:
try:
imported_object = getattr(__import__(app_mod.__name__, {}, {}, model.__name__), model.__name__)
model_name = model.__name__
if "%s.%s" % (app_name, model_name) in dont_load:
continue
alias = app_aliases.get(model_name, model_name)
imported_objects[alias] = imported_object
if model_name == alias:
model_labels.append(model_name)
else:
model_labels.append("%s (as %s)" % (model_name, alias))
except AttributeError, e:
print self.style.ERROR("Failed to import '%s' from '%s' reason: %s" % (model.__name__, app_name, str(e)))
continue
print self.style.SQL_COLTYPE("From '%s' autoload: %s" % (app_mod.__name__.split('.')[-2], ", ".join(model_labels)))
try:
if use_plain:
# Don't bother loading B/IPython, because the user wants plain Python.
raise ImportError
try:
if use_ipython:
# User wants IPython
raise ImportError
from bpython import embed
embed(imported_objects)
except ImportError:
try:
from IPython import embed
embed(user_ns=imported_objects)
except ImportError:
# IPython < 0.11
# Explicitly pass an empty list as arguments, because otherwise
# IPython would use sys.argv from this script.
try:
from IPython.Shell import IPShell
shell = IPShell(argv=[], user_ns=imported_objects)
shell.mainloop()
except ImportError:
# IPython not found at all, raise ImportError
raise
except ImportError:
# Using normal Python shell
import code
try:
# Try activating rlcompleter, because it's handy.
import readline
except ImportError:
pass
else:
# We don't have to wrap the following import in a 'try', because
# we already know 'readline' was imported successfully.
import rlcompleter
readline.set_completer(rlcompleter.Completer(imported_objects).complete)
readline.parse_and_bind("tab:complete")
# We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
# conventions and get $PYTHONSTARTUP first then import user.
if use_pythonrc:
pythonrc = os.environ.get("PYTHONSTARTUP")
if pythonrc and os.path.isfile(pythonrc):
try:
execfile(pythonrc)
except NameError:
pass
# This will import .pythonrc.py as a side-effect
import user
code.interact(local=imported_objects)

View File

@ -0,0 +1,101 @@
from django.conf import settings
from django.template import get_library
import os
import inspect
from django.core.management.base import BaseCommand
from django.core.management import color
from django.utils import termcolors
def color_style():
style = color.color_style()
style.FILTER = termcolors.make_style(fg='yellow', opts=('bold',))
style.MODULE_NAME = termcolors.make_style(fg='green', opts=('bold',))
style.TAG = termcolors.make_style(fg='red', opts=('bold',))
style.TAGLIB = termcolors.make_style(fg='blue', opts=('bold',))
return style
def format_block(block, nlspaces=0):
'''Format the given block of text, trimming leading/trailing
empty lines and any leading whitespace that is common to all lines.
The purpose is to let us list a code block as a multiline,
triple-quoted Python string, taking care of
indentation concerns.
http://code.activestate.com/recipes/145672/'''
import re
# separate block into lines
lines = str(block).split('\n')
# remove leading/trailing empty lines
while lines and not lines[0]:
del lines[0]
while lines and not lines[-1]:
del lines[-1]
# look at first line to see how much indentation to trim
ws = re.match(r'\s*', lines[0]).group(0)
if ws:
lines = map(lambda x: x.replace(ws, '', 1), lines)
# remove leading/trailing blank lines (after leading ws removal)
# we do this again in case there were pure-whitespace lines
while lines and not lines[0]:
del lines[0]
while lines and not lines[-1]:
del lines[-1]
# account for user-specified leading spaces
flines = ['%s%s' % (' ' * nlspaces, line) for line in lines]
return '\n'.join(flines) + '\n'
class Command(BaseCommand):
help = "Displays template tags and filters available in the current project."
results = ""
def add_result(self, s, depth=0):
self.results += '\n%s\n' % s.rjust(depth * 4 + len(s))
def handle(self, *args, **options):
if args:
appname, = args
style = color_style()
if settings.ADMIN_FOR:
settings_modules = [__import__(m, {}, {}, ['']) for m in settings.ADMIN_FOR]
else:
settings_modules = [settings]
for settings_mod in settings_modules:
for app in settings_mod.INSTALLED_APPS:
try:
templatetag_mod = __import__(app + '.templatetags', {}, {}, [''])
except ImportError:
continue
mod_path = inspect.getabsfile(templatetag_mod)
mod_files = os.listdir(os.path.dirname(mod_path))
tag_files = [i.rstrip('.py') for i in mod_files if i.endswith('.py') and i[0] != '_']
app_labeled = False
for taglib in tag_files:
try:
lib = get_library("django.templatetags.%s" % taglib)
except:
continue
if not app_labeled:
self.add_result('\nApp: %s' % style.MODULE_NAME(app))
app_labeled = True
self.add_result('load: %s' % style.TAGLIB(taglib), 1)
for items, label, style_func in [(lib.tags, 'Tag:', style.TAG),
(lib.filters, 'Filter:', style.FILTER)]:
for item in items:
self.add_result('%s %s' % (label, style_func(item)), 2)
doc = inspect.getdoc(items[item])
if doc:
self.add_result(format_block(doc, 12))
return self.results
# return "\n".join(results)

View File

@ -0,0 +1,77 @@
from django.conf import settings
from django.core.exceptions import ViewDoesNotExist
from django.core.management.base import BaseCommand
try:
# 2008-05-30 admindocs found in newforms-admin brand
from django.contrib.admindocs.views import simplify_regex
except ImportError:
# fall back to trunk, pre-NFA merge
from django.contrib.admin.views.doc import simplify_regex
import re
from django_extensions.management.color import color_style
def extract_views_from_urlpatterns(urlpatterns, base=''):
"""
Return a list of views from a list of urlpatterns.
Each object in the returned list is a two-tuple: (view_func, regex)
"""
views = []
for p in urlpatterns:
if hasattr(p, '_get_callback'):
try:
views.append((p._get_callback(), base + p.regex.pattern, p.name))
except ViewDoesNotExist:
continue
elif hasattr(p, '_get_url_patterns'):
try:
patterns = p.url_patterns
except ImportError:
continue
views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
else:
raise TypeError, _("%s does not appear to be a urlpattern object") % p
return views
class Command(BaseCommand):
help = "Displays all of the url matching routes for the project."
requires_model_validation = True
def handle(self, *args, **options):
if args:
appname, = args
style = color_style()
if settings.ADMIN_FOR:
settings_modules = [__import__(m, {}, {}, ['']) for m in settings.ADMIN_FOR]
else:
settings_modules = [settings]
views = []
for settings_mod in settings_modules:
try:
urlconf = __import__(settings_mod.ROOT_URLCONF, {}, {}, [''])
except Exception, e:
if options.get('traceback', None):
import traceback
traceback.print_exc()
print style.ERROR("Error occurred while trying to load %s: %s" % (settings_mod.ROOT_URLCONF, str(e)))
continue
view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
for (func, regex, url_name) in view_functions:
if hasattr(func, '__name__'):
func_name = func.__name__
elif hasattr(func, '__class__'):
func_name = '%s()' % func.__class__.__name__
else:
func_name = re.sub(r' at 0x[0-9a-f]+', '', repr(func))
views.append("%(url)s\t%(module)s.%(name)s\t%(url_name)s" % {'name': style.MODULE_NAME(func_name),
'module': style.MODULE(func.__module__),
'url_name': style.URL_NAME(url_name or ''),
'url': style.URL(simplify_regex(regex))})
return "\n".join([v for v in views])

View File

@ -0,0 +1,70 @@
from django.core.management.base import NoArgsCommand
from django.conf import settings
import sys
class Command(NoArgsCommand):
help = """Generates the SQL to create your database for you, as specified in settings.py
The envisioned use case is something like this:
./manage.py sqlcreate [--router=<routername>] | mysql -u <db_administrator> -p
./manage.py sqlcreate [--router=<routername>] | psql -U <db_administrator> -W"""
requires_model_validation = False
can_import_settings = True
def set_db_settings(self, *args, **options):
if django.get_version() >= "1.2":
router = options.get('router')
if router == None:
return False
# retrieve this with the 'using' argument
dbinfo = settings.DATABASES.get(router)
settings.DATABASE_ENGINE = dbinfo.get('ENGINE').split('.')[-1]
settings.DATABASE_USER = dbinfo.get('USER')
settings.DATABASE_PASSWORD = dbinfo.get('PASSWORD')
settings.DATABASE_NAME = dbinfo.get('NAME')
settings.DATABASE_HOST = dbinfo.get('HOST')
settings.DATABASE_PORT = dbinfo.get('PORT')
return True
else:
# settings are set for django < 1.2 no modification needed
return True
def handle_noargs(self, **options):
if django.get_version() >= "1.2":
got_db_settings = self.set_db_settings(*args, **options)
if not got_db_settings:
raise CommandError("You are using Django %s which requires to specify the db-router.\nPlease specify the router by adding --router=<routername> to this command." % django.get_version())
#print "%s %s %s %s" % (settings.DATABASE_ENGINE, settings.DATABASE_NAME, settings.DATABASE_USER, settings.DATABASE_PASSWORD)
engine = settings.DATABASE_ENGINE
dbname = settings.DATABASE_NAME
dbuser = settings.DATABASE_USER
dbpass = settings.DATABASE_PASSWORD
dbhost = settings.DATABASE_HOST
# django settings file tells you that localhost should be specified by leaving
# the DATABASE_HOST blank
if not dbhost:
dbhost = 'localhost'
if engine == 'mysql':
sys.stderr.write("""-- WARNING!: https://docs.djangoproject.com/en/dev/ref/databases/#collation-settings
-- Please read this carefully! Collation will be set to utf8_bin to have case-sensitive data.
""");
print "CREATE DATABASE %s CHARACTER SET utf8 COLLATE utf8_bin;" % dbname
print "GRANT ALL PRIVILEGES ON %s.* to '%s'@'%s' identified by '%s';" % (
dbname, dbuser, dbhost, dbpass)
elif engine == 'postgresql_psycopg2':
print "CREATE USER %s WITH ENCRYPTED PASSWORD '%s';" % (dbuser, dbpass)
print "CREATE DATABASE %s WITH ENCODING 'UTF-8' OWNER \"%s\";" % (dbname, dbuser)
#print "GRANT ALL PRIVILEGES ON DATABASE %s TO %s" % (dbname, dbuser)
elif engine == 'sqlite3':
sys.stderr.write("-- manage.py syncdb will automatically create a sqlite3 database file.\n")
else:
# CREATE DATABASE is not SQL standard, but seems to be supported by most.
sys.stderr.write("-- Don't know how to handle '%s' falling back to SQL.\n" % engine)
print "CREATE DATABASE %s;" % dbname
print "GRANT ALL PRIVILEGES ON DATABASE %s to %s" % (dbname, dbuser)

View File

@ -0,0 +1,627 @@
"""
sqldiff.py - Prints the (approximated) difference between models and database
TODO:
- better support for relations
- better support for constraints (mainly postgresql?)
- support for table spaces with postgresql
- when a table is not managed (meta.managed==False) then only do a one-way
sqldiff ? show differences from db->table but not the other way around since
it's not managed.
KNOWN ISSUES:
- MySQL has by far the most problems with introspection. Please be
carefull when using MySQL with sqldiff.
- Booleans are reported back as Integers, so there's know way to know if
there was a real change.
- Varchar sizes are reported back without unicode support so their size
may change in comparison to the real length of the varchar.
- Some of the 'fixes' to counter these problems might create false
positives or false negatives.
"""
from django.core.management.base import BaseCommand
from django.core.management import sql as _sql
from django.core.management import CommandError
from django.core.management.color import no_style
from django.db import transaction, connection
from django.db.models.fields import IntegerField
from optparse import make_option
ORDERING_FIELD = IntegerField('_order', null=True)
def flatten(l, ltypes=(list, tuple)):
ltype = type(l)
l = list(l)
i = 0
while i < len(l):
while isinstance(l[i], ltypes):
if not l[i]:
l.pop(i)
i -= 1
break
else:
l[i:i + 1] = l[i]
i += 1
return ltype(l)
def all_local_fields(meta):
all_fields = meta.local_fields[:]
for parent in meta.parents:
all_fields.extend(all_local_fields(parent._meta))
return all_fields
class SQLDiff(object):
DATA_TYPES_REVERSE_OVERRIDE = {}
DIFF_TYPES = [
'error',
'comment',
'table-missing-in-db',
'field-missing-in-db',
'field-missing-in-model',
'index-missing-in-db',
'index-missing-in-model',
'unique-missing-in-db',
'unique-missing-in-model',
'field-type-differ',
'field-parameter-differ',
]
DIFF_TEXTS = {
'error': 'error: %(0)s',
'comment': 'comment: %(0)s',
'table-missing-in-db': "table '%(0)s' missing in database",
'field-missing-in-db': "field '%(1)s' defined in model but missing in database",
'field-missing-in-model': "field '%(1)s' defined in database but missing in model",
'index-missing-in-db': "field '%(1)s' INDEX defined in model but missing in database",
'index-missing-in-model': "field '%(1)s' INDEX defined in database schema but missing in model",
'unique-missing-in-db': "field '%(1)s' UNIQUE defined in model but missing in database",
'unique-missing-in-model': "field '%(1)s' UNIQUE defined in database schema but missing in model",
'field-type-differ': "field '%(1)s' not of same type: db='%(3)s', model='%(2)s'",
'field-parameter-differ': "field '%(1)s' parameters differ: db='%(3)s', model='%(2)s'",
}
SQL_FIELD_MISSING_IN_DB = lambda self, style, qn, args: "%s %s\n\t%s %s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD('ADD'), style.SQL_FIELD(qn(args[1])), style.SQL_COLTYPE(args[2]))
SQL_FIELD_MISSING_IN_MODEL = lambda self, style, qn, args: "%s %s\n\t%s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD('DROP COLUMN'), style.SQL_FIELD(qn(args[1])))
SQL_INDEX_MISSING_IN_DB = lambda self, style, qn, args: "%s %s\n\t%s %s (%s);" % (style.SQL_KEYWORD('CREATE INDEX'), style.SQL_TABLE(qn("%s_idx" % '_'.join(args[0:2]))), style.SQL_KEYWORD('ON'), style.SQL_TABLE(qn(args[0])), style.SQL_FIELD(qn(args[1])))
# FIXME: need to lookup index name instead of just appending _idx to table + fieldname
SQL_INDEX_MISSING_IN_MODEL = lambda self, style, qn, args: "%s %s;" % (style.SQL_KEYWORD('DROP INDEX'), style.SQL_TABLE(qn("%s_idx" % '_'.join(args[0:2]))))
SQL_UNIQUE_MISSING_IN_DB = lambda self, style, qn, args: "%s %s\n\t%s %s (%s);" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD('ADD'), style.SQL_KEYWORD('UNIQUE'), style.SQL_FIELD(qn(args[1])))
# FIXME: need to lookup unique constraint name instead of appending _key to table + fieldname
SQL_UNIQUE_MISSING_IN_MODEL = lambda self, style, qn, args: "%s %s\n\t%s %s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD('DROP'), style.SQL_KEYWORD('CONSTRAINT'), style.SQL_TABLE(qn("%s_key" % ('_'.join(args[:2])))))
SQL_FIELD_TYPE_DIFFER = lambda self, style, qn, args: "%s %s\n\t%s %s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD("MODIFY"), style.SQL_FIELD(qn(args[1])), style.SQL_COLTYPE(args[2]))
SQL_FIELD_PARAMETER_DIFFER = lambda self, style, qn, args: "%s %s\n\t%s %s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD("MODIFY"), style.SQL_FIELD(qn(args[1])), style.SQL_COLTYPE(args[2]))
SQL_ERROR = lambda self, style, qn, args: style.NOTICE('-- Error: %s' % style.ERROR(args[0]))
SQL_COMMENT = lambda self, style, qn, args: style.NOTICE('-- Comment: %s' % style.SQL_TABLE(args[0]))
SQL_TABLE_MISSING_IN_DB = lambda self, style, qn, args: style.NOTICE('-- Table missing: %s' % args[0])
def __init__(self, app_models, options):
self.app_models = app_models
self.options = options
self.dense = options.get('dense_output', False)
try:
self.introspection = connection.introspection
except AttributeError:
from django.db import get_introspection_module
self.introspection = get_introspection_module()
self.cursor = connection.cursor()
self.django_tables = self.get_django_tables(options.get('only_existing', True))
self.db_tables = self.introspection.get_table_list(self.cursor)
self.differences = []
self.unknown_db_fields = {}
self.DIFF_SQL = {
'error': self.SQL_ERROR,
'comment': self.SQL_COMMENT,
'table-missing-in-db': self.SQL_TABLE_MISSING_IN_DB,
'field-missing-in-db': self.SQL_FIELD_MISSING_IN_DB,
'field-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
'index-missing-in-db': self.SQL_INDEX_MISSING_IN_DB,
'index-missing-in-model': self.SQL_INDEX_MISSING_IN_MODEL,
'unique-missing-in-db': self.SQL_UNIQUE_MISSING_IN_DB,
'unique-missing-in-model': self.SQL_UNIQUE_MISSING_IN_MODEL,
'field-type-differ': self.SQL_FIELD_TYPE_DIFFER,
'field-parameter-differ': self.SQL_FIELD_PARAMETER_DIFFER,
}
def add_app_model_marker(self, app_label, model_name):
self.differences.append((app_label, model_name, []))
def add_difference(self, diff_type, *args):
assert diff_type in self.DIFF_TYPES, 'Unknown difference type'
self.differences[-1][-1].append((diff_type, args))
def get_django_tables(self, only_existing):
try:
django_tables = self.introspection.django_table_names(only_existing=only_existing)
except AttributeError:
# backwards compatibility for before introspection refactoring (r8296)
try:
django_tables = _sql.django_table_names(only_existing=only_existing)
except AttributeError:
# backwards compatibility for before svn r7568
django_tables = _sql.django_table_list(only_existing=only_existing)
return django_tables
def sql_to_dict(self, query, param):
""" sql_to_dict(query, param) -> list of dicts
code from snippet at http://www.djangosnippets.org/snippets/1383/
"""
cursor = connection.cursor()
cursor.execute(query, param)
fieldnames = [name[0] for name in cursor.description]
result = []
for row in cursor.fetchall():
rowset = []
for field in zip(fieldnames, row):
rowset.append(field)
result.append(dict(rowset))
return result
def get_field_model_type(self, field):
return field.db_type(connection=connection)
def get_field_db_type(self, description, field=None, table_name=None):
from django.db import models
# DB-API cursor.description
#(name, type_code, display_size, internal_size, precision, scale, null_ok) = description
type_code = description[1]
if type_code in self.DATA_TYPES_REVERSE_OVERRIDE:
reverse_type = self.DATA_TYPES_REVERSE_OVERRIDE[type_code]
else:
try:
try:
reverse_type = self.introspection.data_types_reverse[type_code]
except AttributeError:
# backwards compatibility for before introspection refactoring (r8296)
reverse_type = self.introspection.DATA_TYPES_REVERSE.get(type_code)
except KeyError:
# type_code not found in data_types_reverse map
key = (self.differences[-1][:2], description[:2])
if key not in self.unknown_db_fields:
self.unknown_db_fields[key] = 1
self.add_difference('comment', "Unknown database type for field '%s' (%s)" % (description[0], type_code))
return None
kwargs = {}
if isinstance(reverse_type, tuple):
kwargs.update(reverse_type[1])
reverse_type = reverse_type[0]
if reverse_type == "CharField" and description[3]:
kwargs['max_length'] = description[3]
if reverse_type == "DecimalField":
kwargs['max_digits'] = description[4]
kwargs['decimal_places'] = description[5] and abs(description[5]) or description[5]
if description[6]:
kwargs['blank'] = True
if not reverse_type in ('TextField', 'CharField'):
kwargs['null'] = True
if '.' in reverse_type:
from django.utils import importlib
# TODO: when was importlib added to django.utils ? and do we
# need to add backwards compatibility code ?
module_path, package_name = reverse_type.rsplit('.', 1)
module = importlib.import_module(module_path)
field_db_type = getattr(module, package_name)(**kwargs).db_type(connection=connection)
else:
field_db_type = getattr(models, reverse_type)(**kwargs).db_type(connection=connection)
return field_db_type
def strip_parameters(self, field_type):
if field_type and field_type != 'double precision':
return field_type.split(" ")[0].split("(")[0]
return field_type
def find_unique_missing_in_db(self, meta, table_indexes, table_name):
for field in all_local_fields(meta):
if field.unique:
attname = field.db_column or field.attname
if attname in table_indexes and table_indexes[attname]['unique']:
continue
self.add_difference('unique-missing-in-db', table_name, attname)
def find_unique_missing_in_model(self, meta, table_indexes, table_name):
# TODO: Postgresql does not list unique_togethers in table_indexes
# MySQL does
fields = dict([(field.db_column or field.name, field.unique) for field in all_local_fields(meta)])
for att_name, att_opts in table_indexes.iteritems():
if att_opts['unique'] and att_name in fields and not fields[att_name]:
if att_name in flatten(meta.unique_together):
continue
self.add_difference('unique-missing-in-model', table_name, att_name)
def find_index_missing_in_db(self, meta, table_indexes, table_name):
for field in all_local_fields(meta):
if field.db_index:
attname = field.db_column or field.attname
if not attname in table_indexes:
self.add_difference('index-missing-in-db', table_name, attname)
def find_index_missing_in_model(self, meta, table_indexes, table_name):
fields = dict([(field.name, field) for field in all_local_fields(meta)])
for att_name, att_opts in table_indexes.iteritems():
if att_name in fields:
field = fields[att_name]
if field.db_index:
continue
if att_opts['primary_key'] and field.primary_key:
continue
if att_opts['unique'] and field.unique:
continue
if att_opts['unique'] and att_name in flatten(meta.unique_together):
continue
self.add_difference('index-missing-in-model', table_name, att_name)
def find_field_missing_in_model(self, fieldmap, table_description, table_name):
for row in table_description:
if row[0] not in fieldmap:
self.add_difference('field-missing-in-model', table_name, row[0])
def find_field_missing_in_db(self, fieldmap, table_description, table_name):
db_fields = [row[0] for row in table_description]
for field_name, field in fieldmap.iteritems():
if field_name not in db_fields:
self.add_difference('field-missing-in-db', table_name, field_name,
field.db_type(connection=connection))
def find_field_type_differ(self, meta, table_description, table_name, func=None):
db_fields = dict([(row[0], row) for row in table_description])
for field in all_local_fields(meta):
if field.name not in db_fields:
continue
description = db_fields[field.name]
model_type = self.strip_parameters(self.get_field_model_type(field))
db_type = self.strip_parameters(self.get_field_db_type(description, field))
# use callback function if defined
if func:
model_type, db_type = func(field, description, model_type, db_type)
if not model_type == db_type:
self.add_difference('field-type-differ', table_name, field.name, model_type, db_type)
def find_field_parameter_differ(self, meta, table_description, table_name, func=None):
db_fields = dict([(row[0], row) for row in table_description])
for field in all_local_fields(meta):
if field.name not in db_fields:
continue
description = db_fields[field.name]
model_type = self.get_field_model_type(field)
db_type = self.get_field_db_type(description, field, table_name)
if not self.strip_parameters(model_type) == self.strip_parameters(db_type):
continue
# use callback function if defined
if func:
model_type, db_type = func(field, description, model_type, db_type)
if not model_type == db_type:
self.add_difference('field-parameter-differ', table_name, field.name, model_type, db_type)
@transaction.commit_manually
def find_differences(self):
cur_app_label = None
for app_model in self.app_models:
meta = app_model._meta
table_name = meta.db_table
app_label = meta.app_label
if cur_app_label != app_label:
# Marker indicating start of difference scan for this table_name
self.add_app_model_marker(app_label, app_model.__name__)
#if not table_name in self.django_tables:
if not table_name in self.db_tables:
# Table is missing from database
self.add_difference('table-missing-in-db', table_name)
continue
table_indexes = self.introspection.get_indexes(self.cursor, table_name)
fieldmap = dict([(field.db_column or field.get_attname(), field) for field in all_local_fields(meta)])
# add ordering field if model uses order_with_respect_to
if meta.order_with_respect_to:
fieldmap['_order'] = ORDERING_FIELD
try:
table_description = self.introspection.get_table_description(self.cursor, table_name)
except Exception, e:
self.add_difference('error', 'unable to introspect table: %s' % str(e).strip())
transaction.rollback() # reset transaction
continue
else:
transaction.commit()
# Fields which are defined in database but not in model
# 1) find: 'unique-missing-in-model'
self.find_unique_missing_in_model(meta, table_indexes, table_name)
# 2) find: 'index-missing-in-model'
self.find_index_missing_in_model(meta, table_indexes, table_name)
# 3) find: 'field-missing-in-model'
self.find_field_missing_in_model(fieldmap, table_description, table_name)
# Fields which are defined in models but not in database
# 4) find: 'field-missing-in-db'
self.find_field_missing_in_db(fieldmap, table_description, table_name)
# 5) find: 'unique-missing-in-db'
self.find_unique_missing_in_db(meta, table_indexes, table_name)
# 6) find: 'index-missing-in-db'
self.find_index_missing_in_db(meta, table_indexes, table_name)
# Fields which have a different type or parameters
# 7) find: 'type-differs'
self.find_field_type_differ(meta, table_description, table_name)
# 8) find: 'type-parameter-differs'
self.find_field_parameter_differ(meta, table_description, table_name)
def print_diff(self, style=no_style()):
""" print differences to stdout """
if self.options.get('sql', True):
self.print_diff_sql(style)
else:
self.print_diff_text(style)
def print_diff_text(self, style):
cur_app_label = None
for app_label, model_name, diffs in self.differences:
if not diffs:
continue
if not self.dense and cur_app_label != app_label:
print style.NOTICE("+ Application:"), style.SQL_TABLE(app_label)
cur_app_label = app_label
if not self.dense:
print style.NOTICE("|-+ Differences for model:"), style.SQL_TABLE(model_name)
for diff in diffs:
diff_type, diff_args = diff
text = self.DIFF_TEXTS[diff_type] % dict((str(i), style.SQL_TABLE(e)) for i, e in enumerate(diff_args))
text = "'".join(i % 2 == 0 and style.ERROR(e) or e for i, e in enumerate(text.split("'")))
if not self.dense:
print style.NOTICE("|--+"), text
else:
print style.NOTICE("App"), style.SQL_TABLE(app_label), style.NOTICE('Model'), style.SQL_TABLE(model_name), text
def print_diff_sql(self, style):
cur_app_label = None
qn = connection.ops.quote_name
has_differences = max([len(diffs) for app_label, model_name, diffs in self.differences])
if not has_differences:
if not self.dense:
print style.SQL_KEYWORD("-- No differences")
else:
print style.SQL_KEYWORD("BEGIN;")
for app_label, model_name, diffs in self.differences:
if not diffs:
continue
if not self.dense and cur_app_label != app_label:
print style.NOTICE("-- Application: %s" % style.SQL_TABLE(app_label))
cur_app_label = app_label
if not self.dense:
print style.NOTICE("-- Model: %s" % style.SQL_TABLE(model_name))
for diff in diffs:
diff_type, diff_args = diff
text = self.DIFF_SQL[diff_type](style, qn, diff_args)
if self.dense:
text = text.replace("\n\t", " ")
print text
print style.SQL_KEYWORD("COMMIT;")
class GenericSQLDiff(SQLDiff):
pass
class MySQLDiff(SQLDiff):
# All the MySQL hacks together create something of a problem
# Fixing one bug in MySQL creates another issue. So just keep in mind
# that this is way unreliable for MySQL atm.
def get_field_db_type(self, description, field=None, table_name=None):
from MySQLdb.constants import FIELD_TYPE
# weird bug? in mysql db-api where it returns three times the correct value for field length
# if i remember correctly it had something todo with unicode strings
# TODO: Fix this is a more meaningful and better understood manner
description = list(description)
if description[1] not in [FIELD_TYPE.TINY, FIELD_TYPE.SHORT]: # exclude tinyints from conversion.
description[3] = description[3] / 3
description[4] = description[4] / 3
db_type = super(MySQLDiff, self).get_field_db_type(description)
if not db_type:
return
if field:
if field.primary_key and (db_type == 'integer' or db_type == 'bigint'):
db_type += ' AUTO_INCREMENT'
# MySQL isn't really sure about char's and varchar's like sqlite
field_type = self.get_field_model_type(field)
# Fix char/varchar inconsistencies
if self.strip_parameters(field_type) == 'char' and self.strip_parameters(db_type) == 'varchar':
db_type = db_type.lstrip("var")
# They like to call 'bool's 'tinyint(1)' and introspection makes that a integer
# just convert it back to it's proper type, a bool is a bool and nothing else.
if db_type == 'integer' and description[1] == FIELD_TYPE.TINY and description[4] == 1:
db_type = 'bool'
if db_type == 'integer' and description[1] == FIELD_TYPE.SHORT:
db_type = 'smallint UNSIGNED' # FIXME: what about if it's not UNSIGNED ?
return db_type
class SqliteSQLDiff(SQLDiff):
# Unique does not seem to be implied on Sqlite for Primary_key's
# if this is more generic among databases this might be usefull
# to add to the superclass's find_unique_missing_in_db method
def find_unique_missing_in_db(self, meta, table_indexes, table_name):
for field in all_local_fields(meta):
if field.unique:
attname = field.db_column or field.attname
if attname in table_indexes and table_indexes[attname]['unique']:
continue
if attname in table_indexes and table_indexes[attname]['primary_key']:
continue
self.add_difference('unique-missing-in-db', table_name, attname)
# Finding Indexes by using the get_indexes dictionary doesn't seem to work
# for sqlite.
def find_index_missing_in_db(self, meta, table_indexes, table_name):
pass
def find_index_missing_in_model(self, meta, table_indexes, table_name):
pass
def get_field_db_type(self, description, field=None, table_name=None):
db_type = super(SqliteSQLDiff, self).get_field_db_type(description)
if not db_type:
return
if field:
field_type = self.get_field_model_type(field)
# Fix char/varchar inconsistencies
if self.strip_parameters(field_type) == 'char' and self.strip_parameters(db_type) == 'varchar':
db_type = db_type.lstrip("var")
return db_type
class PostgresqlSQLDiff(SQLDiff):
DATA_TYPES_REVERSE_OVERRIDE = {
1042: 'CharField',
# postgis types (TODO: support is very incomplete)
17506: 'django.contrib.gis.db.models.fields.PointField',
55902: 'django.contrib.gis.db.models.fields.MultiPolygonField',
}
# Hopefully in the future we can add constraint checking and other more
# advanced checks based on this database.
SQL_LOAD_CONSTRAINTS = """
SELECT nspname, relname, conname, attname, pg_get_constraintdef(pg_constraint.oid)
FROM pg_constraint
INNER JOIN pg_attribute ON pg_constraint.conrelid = pg_attribute.attrelid AND pg_attribute.attnum = any(pg_constraint.conkey)
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname;
"""
SQL_FIELD_TYPE_DIFFER = lambda self, style, qn, args: "%s %s\n\t%s %s %s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD('ALTER'), style.SQL_FIELD(qn(args[1])), style.SQL_KEYWORD("TYPE"), style.SQL_COLTYPE(args[2]))
SQL_FIELD_PARAMETER_DIFFER = lambda self, style, qn, args: "%s %s\n\t%s %s %s %s;" % (style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(args[0])), style.SQL_KEYWORD('ALTER'), style.SQL_FIELD(qn(args[1])), style.SQL_KEYWORD("TYPE"), style.SQL_COLTYPE(args[2]))
def __init__(self, app_models, options):
SQLDiff.__init__(self, app_models, options)
self.check_constraints = {}
self.load_constraints()
def load_constraints(self):
for dct in self.sql_to_dict(self.SQL_LOAD_CONSTRAINTS, []):
key = (dct['nspname'], dct['relname'], dct['attname'])
if 'CHECK' in dct['pg_get_constraintdef']:
self.check_constraints[key] = dct
def get_field_db_type(self, description, field=None, table_name=None):
db_type = super(PostgresqlSQLDiff, self).get_field_db_type(description)
if not db_type:
return
if field:
if field.primary_key and db_type == 'integer':
db_type = 'serial'
if table_name:
tablespace = field.db_tablespace
if tablespace == "":
tablespace = "public"
check_constraint = self.check_constraints.get((tablespace, table_name, field.attname), {}).get('pg_get_constraintdef', None)
if check_constraint:
check_constraint = check_constraint.replace("((", "(")
check_constraint = check_constraint.replace("))", ")")
check_constraint = '("'.join([')' in e and '" '.join(e.split(" ", 1)) or e for e in check_constraint.split("(")])
# TODO: might be more then one constraint in definition ?
db_type += ' ' + check_constraint
return db_type
"""
def find_field_type_differ(self, meta, table_description, table_name):
def callback(field, description, model_type, db_type):
if field.primary_key and db_type=='integer':
db_type = 'serial'
return model_type, db_type
super(PostgresqlSQLDiff, self).find_field_type_differs(meta, table_description, table_name, callback)
"""
DATABASE_SQLDIFF_CLASSES = {
'postgresql_psycopg2' : PostgresqlSQLDiff,
'postgresql': PostgresqlSQLDiff,
'mysql': MySQLDiff,
'sqlite3': SqliteSQLDiff,
'oracle': GenericSQLDiff
}
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--all-applications', '-a', action='store_true', dest='all_applications',
help="Automaticly include all application from INSTALLED_APPS."),
make_option('--not-only-existing', '-e', action='store_false', dest='only_existing',
help="Check all tables that exist in the database, not only tables that should exist based on models."),
make_option('--dense-output', '-d', action='store_true', dest='dense_output',
help="Shows the output in dense format, normally output is spreaded over multiple lines."),
make_option('--output_text', '-t', action='store_false', dest='sql', default=True,
help="Outputs the differences as descriptive text instead of SQL"),
)
help = """Prints the (approximated) difference between models and fields in the database for the given app name(s).
It indicates how columns in the database are different from the sql that would
be generated by Django. This command is not a database migration tool. (Though
it can certainly help) It's purpose is to show the current differences as a way
to check/debug ur models compared to the real database tables and columns."""
output_transaction = False
args = '<appname appname ...>'
def handle(self, *app_labels, **options):
from django import VERSION
if VERSION[:2] < (1, 0):
raise CommandError("SQLDiff only support Django 1.0 or higher!")
from django.db import models
from django.conf import settings
if settings.DATABASE_ENGINE == 'dummy':
# This must be the "dummy" database backend, which means the user
# hasn't set DATABASE_ENGINE.
raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" +
"because you haven't specified the DATABASE_ENGINE setting.\n" +
"Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.")
if options.get('all_applications', False):
app_models = models.get_models()
else:
if not app_labels:
raise CommandError('Enter at least one appname.')
try:
app_list = [models.get_app(app_label) for app_label in app_labels]
except (models.ImproperlyConfigured, ImportError), e:
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
app_models = []
for app in app_list:
app_models.extend(models.get_models(app))
## remove all models that are not managed by Django
#app_models = [model for model in app_models if getattr(model._meta, 'managed', True)]
if not app_models:
raise CommandError('Unable to execute sqldiff no models founds.')
engine = settings.DATABASE_ENGINE
if not engine:
engine = connection.__module__.split('.')[-2]
cls = DATABASE_SQLDIFF_CLASSES.get(engine, GenericSQLDiff)
sqldiff_instance = cls(app_models, options)
sqldiff_instance.find_differences()
sqldiff_instance.print_diff(self.style)
return

View File

@ -0,0 +1,271 @@
"""
Sync Media to S3
================
Django command that scans all files in your settings.MEDIA_ROOT folder and
uploads them to S3 with the same directory structure.
This command can optionally do the following but it is off by default:
* gzip compress any CSS and Javascript files it finds and adds the appropriate
'Content-Encoding' header.
* set a far future 'Expires' header for optimal caching.
Note: This script requires the Python boto library and valid Amazon Web
Services API keys.
Required settings.py variables:
AWS_ACCESS_KEY_ID = ''
AWS_SECRET_ACCESS_KEY = ''
AWS_BUCKET_NAME = ''
Command options are:
-p PREFIX, --prefix=PREFIX
The prefix to prepend to the path on S3.
--gzip Enables gzipping CSS and Javascript files.
--expires Enables setting a far future expires header.
--force Skip the file mtime check to force upload of all
files.
--filter-list Override default directory and file exclusion
filters. (enter as comma seperated line)
TODO:
* Use fnmatch (or regex) to allow more complex FILTER_LIST rules.
"""
import datetime
import email
import mimetypes
import optparse
import os
import sys
import time
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
# Make sure boto is available
try:
import boto
import boto.exception
except ImportError:
raise ImportError, "The boto Python library is not installed."
class Command(BaseCommand):
# Extra variables to avoid passing these around
AWS_ACCESS_KEY_ID = ''
AWS_SECRET_ACCESS_KEY = ''
AWS_BUCKET_NAME = ''
DIRECTORY = ''
FILTER_LIST = ['.DS_Store', '.svn', '.hg', '.git', 'Thumbs.db']
GZIP_CONTENT_TYPES = (
'text/css',
'application/javascript',
'application/x-javascript',
'text/javascript'
)
upload_count = 0
skip_count = 0
option_list = BaseCommand.option_list + (
optparse.make_option('-p', '--prefix',
dest='prefix',
default=getattr(settings, 'SYNC_MEDIA_S3_PREFIX', ''),
help="The prefix to prepend to the path on S3."),
optparse.make_option('-d', '--dir',
dest='dir', default=settings.MEDIA_ROOT,
help="The root directory to use instead of your MEDIA_ROOT"),
optparse.make_option('--gzip',
action='store_true', dest='gzip', default=False,
help="Enables gzipping CSS and Javascript files."),
optparse.make_option('--expires',
action='store_true', dest='expires', default=False,
help="Enables setting a far future expires header."),
optparse.make_option('--force',
action='store_true', dest='force', default=False,
help="Skip the file mtime check to force upload of all files."),
optparse.make_option('--filter-list', dest='filter_list',
action='store', default='',
help="Override default directory and file exclusion filters. (enter as comma seperated line)"),
)
help = 'Syncs the complete MEDIA_ROOT structure and files to S3 into the given bucket name.'
args = 'bucket_name'
can_import_settings = True
def handle(self, *args, **options):
# Check for AWS keys in settings
if not hasattr(settings, 'AWS_ACCESS_KEY_ID') or \
not hasattr(settings, 'AWS_SECRET_ACCESS_KEY'):
raise CommandError('Missing AWS keys from settings file. Please' +
'supply both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.')
else:
self.AWS_ACCESS_KEY_ID = settings.AWS_ACCESS_KEY_ID
self.AWS_SECRET_ACCESS_KEY = settings.AWS_SECRET_ACCESS_KEY
if not hasattr(settings, 'AWS_BUCKET_NAME'):
raise CommandError('Missing bucket name from settings file. Please' +
' add the AWS_BUCKET_NAME to your settings file.')
else:
if not settings.AWS_BUCKET_NAME:
raise CommandError('AWS_BUCKET_NAME cannot be empty.')
self.AWS_BUCKET_NAME = settings.AWS_BUCKET_NAME
if not hasattr(settings, 'MEDIA_ROOT'):
raise CommandError('MEDIA_ROOT must be set in your settings.')
else:
if not settings.MEDIA_ROOT:
raise CommandError('MEDIA_ROOT must be set in your settings.')
self.verbosity = int(options.get('verbosity'))
self.prefix = options.get('prefix')
self.do_gzip = options.get('gzip')
self.do_expires = options.get('expires')
self.do_force = options.get('force')
self.DIRECTORY = options.get('dir')
self.FILTER_LIST = getattr(settings, 'FILTER_LIST', self.FILTER_LIST)
filter_list = options.get('filter_list')
if filter_list:
# command line option overrides default filter_list and
# settings.filter_list
self.FILTER_LIST = filter_list.split(',')
# Now call the syncing method to walk the MEDIA_ROOT directory and
# upload all files found.
self.sync_s3()
print
print "%d files uploaded." % (self.upload_count)
print "%d files skipped." % (self.skip_count)
def sync_s3(self):
"""
Walks the media directory and syncs files to S3
"""
bucket, key = self.open_s3()
os.path.walk(self.DIRECTORY, self.upload_s3,
(bucket, key, self.AWS_BUCKET_NAME, self.DIRECTORY))
def compress_string(self, s):
"""Gzip a given string."""
import gzip
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
zbuf = StringIO()
zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
zfile.write(s)
zfile.close()
return zbuf.getvalue()
def open_s3(self):
"""
Opens connection to S3 returning bucket and key
"""
conn = boto.connect_s3(self.AWS_ACCESS_KEY_ID, self.AWS_SECRET_ACCESS_KEY)
try:
bucket = conn.get_bucket(self.AWS_BUCKET_NAME)
except boto.exception.S3ResponseError:
bucket = conn.create_bucket(self.AWS_BUCKET_NAME)
return bucket, boto.s3.key.Key(bucket)
def upload_s3(self, arg, dirname, names):
"""
This is the callback to os.path.walk and where much of the work happens
"""
bucket, key, bucket_name, root_dir = arg
# Skip directories we don't want to sync
if os.path.basename(dirname) in self.FILTER_LIST:
# prevent walk from processing subfiles/subdirs below the ignored one
del names[:]
return
# Later we assume the MEDIA_ROOT ends with a trailing slash
if not root_dir.endswith(os.path.sep):
root_dir = root_dir + os.path.sep
for file in names:
headers = {}
if file in self.FILTER_LIST:
continue # Skip files we don't want to sync
filename = os.path.join(dirname, file)
if os.path.isdir(filename):
continue # Don't try to upload directories
file_key = filename[len(root_dir):]
if self.prefix:
file_key = '%s/%s' % (self.prefix, file_key)
# Check if file on S3 is older than local file, if so, upload
if not self.do_force:
s3_key = bucket.get_key(file_key)
if s3_key:
s3_datetime = datetime.datetime(*time.strptime(
s3_key.last_modified, '%a, %d %b %Y %H:%M:%S %Z')[0:6])
local_datetime = datetime.datetime.utcfromtimestamp(
os.stat(filename).st_mtime)
if local_datetime < s3_datetime:
self.skip_count += 1
if self.verbosity > 1:
print "File %s hasn't been modified since last " \
"being uploaded" % (file_key)
continue
# File is newer, let's process and upload
if self.verbosity > 0:
print "Uploading %s..." % (file_key)
content_type = mimetypes.guess_type(filename)[0]
if content_type:
headers['Content-Type'] = content_type
file_obj = open(filename, 'rb')
file_size = os.fstat(file_obj.fileno()).st_size
filedata = file_obj.read()
if self.do_gzip:
# Gzipping only if file is large enough (>1K is recommended)
# and only if file is a common text type (not a binary file)
if file_size > 1024 and content_type in self.GZIP_CONTENT_TYPES:
filedata = self.compress_string(filedata)
headers['Content-Encoding'] = 'gzip'
if self.verbosity > 1:
print "\tgzipped: %dk to %dk" % \
(file_size / 1024, len(filedata) / 1024)
if self.do_expires:
# HTTP/1.0
headers['Expires'] = '%s GMT' % (email.Utils.formatdate(
time.mktime((datetime.datetime.now() +
datetime.timedelta(days=365 * 2)).timetuple())))
# HTTP/1.1
headers['Cache-Control'] = 'max-age %d' % (3600 * 24 * 365 * 2)
if self.verbosity > 1:
print "\texpires: %s" % (headers['Expires'])
print "\tcache-control: %s" % (headers['Cache-Control'])
try:
key.name = file_key
key.set_contents_from_string(filedata, headers, replace=True)
key.set_acl('public-read')
except boto.exception.S3CreateError, e:
print "Failed: %s" % e
except Exception, e:
print e
raise
else:
self.upload_count += 1
file_obj.close()
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
optparse.make_option('-v', '--verbosity',
dest='verbosity', default=1, action='count',
help="Verbose mode. Multiple -v options increase the verbosity."),
)

View File

@ -0,0 +1,220 @@
"""
SyncData
========
Django command similar to 'loaddata' but also deletes.
After 'syncdata' has run, the database will have the same data as the fixture - anything
missing will of been added, anything different will of been updated,
and anything extra will of been deleted.
"""
from django.core.management.base import BaseCommand
from django.core.management.color import no_style
from optparse import make_option
import sys
import os
class Command(BaseCommand):
""" syncdata command """
help = 'Makes the current database have the same data as the fixture(s), no more, no less.'
args = "fixture [fixture ...]"
def remove_objects_not_in(self, objects_to_keep, verbosity):
"""
Deletes all the objects in the database that are not in objects_to_keep.
- objects_to_keep: A map where the keys are classes, and the values are a
set of the objects of that class we should keep.
"""
for class_ in objects_to_keep.keys():
current = class_.objects.all()
current_ids = set([x.id for x in current])
keep_ids = set([x.id for x in objects_to_keep[class_]])
remove_these_ones = current_ids.difference(keep_ids)
if remove_these_ones:
for obj in current:
if obj.id in remove_these_ones:
obj.delete()
if verbosity >= 2:
print "Deleted object: %s" % unicode(obj)
if verbosity > 0 and remove_these_ones:
num_deleted = len(remove_these_ones)
if num_deleted > 1:
type_deleted = unicode(class_._meta.verbose_name_plural)
else:
type_deleted = unicode(class_._meta.verbose_name)
print "Deleted %s %s" % (str(num_deleted), type_deleted)
def handle(self, *fixture_labels, **options):
""" Main method of a Django command """
from django.db.models import get_apps
from django.core import serializers
from django.db import connection, transaction
from django.conf import settings
self.style = no_style()
verbosity = int(options.get('verbosity', 1))
show_traceback = options.get('traceback', False)
# Keep a count of the installed objects and fixtures
fixture_count = 0
object_count = 0
objects_per_fixture = []
models = set()
humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
# Get a cursor (even though we don't need one yet). This has
# the side effect of initializing the test database (if
# it isn't already initialized).
cursor = connection.cursor()
# Start transaction management. All fixtures are installed in a
# single transaction to ensure that all references are resolved.
transaction.commit_unless_managed()
transaction.enter_transaction_management()
transaction.managed(True)
app_fixtures = [os.path.join(os.path.dirname(app.__file__), 'fixtures') \
for app in get_apps()]
for fixture_label in fixture_labels:
parts = fixture_label.split('.')
if len(parts) == 1:
fixture_name = fixture_label
formats = serializers.get_public_serializer_formats()
else:
fixture_name, format = '.'.join(parts[:-1]), parts[-1]
if format in serializers.get_public_serializer_formats():
formats = [format]
else:
formats = []
if formats:
if verbosity > 1:
print "Loading '%s' fixtures..." % fixture_name
else:
sys.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s is not a known " + \
"serialization format." % (fixture_name, format))
)
transaction.rollback()
transaction.leave_transaction_management()
return
if os.path.isabs(fixture_name):
fixture_dirs = [fixture_name]
else:
fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
for fixture_dir in fixture_dirs:
if verbosity > 1:
print "Checking %s for fixtures..." % humanize(fixture_dir)
label_found = False
for format in formats:
serializer = serializers.get_serializer(format)
if verbosity > 1:
print "Trying %s for %s fixture '%s'..." % \
(humanize(fixture_dir), format, fixture_name)
try:
full_path = os.path.join(fixture_dir, '.'.join([fixture_name, format]))
fixture = open(full_path, 'r')
if label_found:
fixture.close()
print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." %
(fixture_name, humanize(fixture_dir)))
transaction.rollback()
transaction.leave_transaction_management()
return
else:
fixture_count += 1
objects_per_fixture.append(0)
if verbosity > 0:
print "Installing %s fixture '%s' from %s." % \
(format, fixture_name, humanize(fixture_dir))
try:
objects_to_keep = {}
objects = serializers.deserialize(format, fixture)
for obj in objects:
object_count += 1
objects_per_fixture[-1] += 1
class_ = obj.object.__class__
if not class_ in objects_to_keep:
objects_to_keep[class_] = set()
objects_to_keep[class_].add(obj.object)
models.add(class_)
obj.save()
self.remove_objects_not_in(objects_to_keep, verbosity)
label_found = True
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
import traceback
fixture.close()
transaction.rollback()
transaction.leave_transaction_management()
if show_traceback:
traceback.print_exc()
else:
sys.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" %
(full_path, traceback.format_exc())))
return
fixture.close()
except:
if verbosity > 1:
print "No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir))
# If any of the fixtures we loaded contain 0 objects, assume that an
# error was encountered during fixture loading.
if 0 in objects_per_fixture:
sys.stderr.write(
self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" %
(fixture_name)))
transaction.rollback()
transaction.leave_transaction_management()
return
# If we found even one object in a fixture, we need to reset the
# database sequences.
if object_count > 0:
sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
if sequence_sql:
if verbosity > 1:
print "Resetting sequences"
for line in sequence_sql:
cursor.execute(line)
transaction.commit()
transaction.leave_transaction_management()
if object_count == 0:
if verbosity > 1:
print "No fixtures found."
else:
if verbosity > 0:
print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count)
# Close the DB connection. This is required as a workaround for an
# edge case in MySQL: if the same connection is used to
# create tables, load data, and query, the query can return
# incorrect results. See Django #7572, MySQL #37735.
connection.close()
# Backwards compatibility for Django r9110
if not [opt for opt in Command.option_list if opt.dest == 'verbosity']:
Command.option_list += (
make_option('--verbosity', '-v', action="store", dest="verbosity",
default='1', type='choice', choices=['0', '1', '2'],
help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"),
)

View File

@ -0,0 +1,45 @@
from collections import defaultdict
import os
from django.conf import settings
from django.core.management.base import NoArgsCommand
from django.db import models
from django.db.models.loading import cache
class Command(NoArgsCommand):
help = "Prints a list of all files in MEDIA_ROOT that are not referenced in the database."
def handle_noargs(self, **options):
if settings.MEDIA_ROOT == '':
print "MEDIA_ROOT is not set, nothing to do"
return
# Get a list of all files under MEDIA_ROOT
media = []
for root, dirs, files in os.walk(settings.MEDIA_ROOT):
for f in files:
media.append(os.path.abspath(os.path.join(root, f)))
# Get list of all fields (value) for each model (key)
# that is a FileField or subclass of a FileField
model_dict = defaultdict(list)
for app in cache.get_apps():
model_list = cache.get_models(app)
for model in model_list:
for field in model._meta.fields:
if issubclass(field.__class__, models.FileField):
model_dict[model].append(field)
# Get a list of all files referenced in the database
referenced = []
for model in model_dict.iterkeys():
all = model.objects.all().iterator()
for object in all:
for field in model_dict[model]:
referenced.append(os.path.abspath(getattr(object, field.name).path))
# Print each file in MEDIA_ROOT that is not referenced in the database
for m in media:
if m not in referenced:
print m

View File

@ -0,0 +1,21 @@
from django.core.management.base import BaseCommand
from django.db.models import get_models, get_app
from django.contrib.auth.management import create_permissions
class Command(BaseCommand):
args = '<app app ...>'
help = 'reloads permissions for specified apps, or all apps if no args are specified'
def handle(self, *args, **options):
if not args:
apps = []
for model in get_models():
apps.append(get_app(model._meta.app_label))
else:
apps = []
for arg in args:
apps.append(get_app(arg))
for app in apps:
create_permissions(app, get_models(), options.get('verbosity', 0))

View File

@ -0,0 +1,175 @@
"""
django_extensions.management.jobs
"""
import os
from imp import find_module
_jobs = None
def noneimplementation(meth):
return None
class JobError(Exception):
pass
class BaseJob(object):
help = "undefined job description."
when = None
def execute(self):
raise NotImplementedError("Job needs to implement the execute method")
class MinutelyJob(BaseJob):
when = "minutely"
class HourlyJob(BaseJob):
when = "hourly"
class DailyJob(BaseJob):
when = "daily"
class WeeklyJob(BaseJob):
when = "weekly"
class MonthlyJob(BaseJob):
when = "monthly"
class YearlyJob(BaseJob):
when = "yearly"
def my_import(name):
imp = __import__(name)
mods = name.split('.')
if len(mods) > 1:
for mod in mods[1:]:
imp = getattr(imp, mod)
return imp
def find_jobs(jobs_dir):
try:
return [f[:-3] for f in os.listdir(jobs_dir) \
if not f.startswith('_') and f.endswith(".py")]
except OSError:
return []
def find_job_module(app_name, when=None):
parts = app_name.split('.')
parts.append('jobs')
if when:
parts.append(when)
parts.reverse()
path = None
while parts:
part = parts.pop()
f, path, descr = find_module(part, path and [path] or None)
return path
def import_job(app_name, name, when=None):
jobmodule = "%s.jobs.%s%s" % (app_name, when and "%s." % when or "", name)
job_mod = my_import(jobmodule)
# todo: more friendly message for AttributeError if job_mod does not exist
try:
job = job_mod.Job
except:
raise JobError("Job module %s does not contain class instance named 'Job'" % jobmodule)
if when and not (job.when == when or job.when == None):
raise JobError("Job %s is not a %s job." % (jobmodule, when))
return job
def get_jobs(when=None, only_scheduled=False):
"""
Returns a dictionary mapping of job names together with their respective
application class.
"""
# FIXME: HACK: make sure the project dir is on the path when executed as ./manage.py
import sys
try:
cpath = os.path.dirname(os.path.realpath(sys.argv[0]))
ppath = os.path.dirname(cpath)
if ppath not in sys.path:
sys.path.append(ppath)
except:
pass
_jobs = {}
if True:
from django.conf import settings
for app_name in settings.INSTALLED_APPS:
scandirs = (None, 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly')
if when:
scandirs = None, when
for subdir in scandirs:
try:
path = find_job_module(app_name, subdir)
for name in find_jobs(path):
if (app_name, name) in _jobs:
raise JobError("Duplicate job %s" % name)
job = import_job(app_name, name, subdir)
if only_scheduled and job.when == None:
# only include jobs which are scheduled
continue
if when and job.when != when:
# generic job not in same schedule
continue
_jobs[(app_name, name)] = job
except ImportError:
# No job module -- continue scanning
pass
return _jobs
def get_job(app_name, job_name):
jobs = get_jobs()
if app_name:
return jobs[(app_name, job_name)]
else:
for a, j in jobs.keys():
if j == job_name:
return jobs[(a, j)]
raise KeyError("Job not found: %s" % job_name)
def print_jobs(when=None, only_scheduled=False, show_when=True, \
show_appname=False, show_header=True):
jobmap = get_jobs(when, only_scheduled=only_scheduled)
print "Job List: %i jobs" % len(jobmap)
jlist = jobmap.keys()
jlist.sort()
appname_spacer = "%%-%is" % max(len(e[0]) for e in jlist)
name_spacer = "%%-%is" % max(len(e[1]) for e in jlist)
when_spacer = "%%-%is" % max(len(e.when) for e in jobmap.values() if e.when)
if show_header:
line = " "
if show_appname:
line += appname_spacer % "appname" + " - "
line += name_spacer % "jobname"
if show_when:
line += " - " + when_spacer % "when"
line += " - help"
print line
print "-" * 80
for app_name, job_name in jlist:
job = jobmap[(app_name, job_name)]
line = " "
if show_appname:
line += appname_spacer % app_name + " - "
line += name_spacer % job_name
if show_when:
line += " - " + when_spacer % (job.when and job.when or "")
line += " - " + job.help
print line

View File

@ -0,0 +1,305 @@
#!/usr/bin/env python
"""Django model to DOT (Graphviz) converter
by Antonio Cavedoni <antonio@cavedoni.org>
Make sure your DJANGO_SETTINGS_MODULE is set to your project or
place this script in the same directory of the project and call
the script like this:
$ python modelviz.py [-h] [-a] [-d] [-g] [-n] [-L <language>] [-i <model_names>] <app_label> ... <app_label> > <filename>.dot
$ dot <filename>.dot -Tpng -o <filename>.png
options:
-h, --help
show this help message and exit.
-a, --all_applications
show models from all applications.
-d, --disable_fields
don't show the class member fields.
-g, --group_models
draw an enclosing box around models from the same app.
-i, --include_models=User,Person,Car
only include selected models in graph.
-n, --verbose_names
use verbose_name for field and models.
-L, --language
specify language used for verrbose_name localization
-x, --exclude_columns
exclude specific column(s) from the graph.
-X, --exclude_models
exclude specific model(s) from the graph.
"""
__version__ = "0.9"
__svnid__ = "$Id$"
__license__ = "Python"
__author__ = "Antonio Cavedoni <http://cavedoni.com/>"
__contributors__ = [
"Stefano J. Attardi <http://attardi.org/>",
"limodou <http://www.donews.net/limodou/>",
"Carlo C8E Miron",
"Andre Campos <cahenan@gmail.com>",
"Justin Findlay <jfindlay@gmail.com>",
"Alexander Houben <alexander@houben.ch>",
"Bas van Oostveen <v.oostveen@gmail.com>",
]
import os
import sys
import getopt
from django.core.management import setup_environ
try:
import settings
except ImportError:
pass
else:
setup_environ(settings)
from django.utils.translation import activate as activate_language
from django.utils.safestring import mark_safe
from django.template import Template, Context, loader
from django.db import models
from django.db.models import get_models
from django.db.models.fields.related import \
ForeignKey, OneToOneField, ManyToManyField
try:
from django.db.models.fields.generic import GenericRelation
except ImportError:
from django.contrib.contenttypes.generic import GenericRelation
def parse_file_or_list(arg):
if not arg:
return []
if not ',' in arg and os.path.isfile(arg):
return [e.strip() for e in open(arg).readlines()]
return arg.split(',')
def generate_dot(app_labels, **kwargs):
disable_fields = kwargs.get('disable_fields', False)
include_models = parse_file_or_list(kwargs.get('include_models', ""))
all_applications = kwargs.get('all_applications', False)
use_subgraph = kwargs.get('group_models', False)
verbose_names = kwargs.get('verbose_names', False)
language = kwargs.get('language', None)
if language is not None:
activate_language(language)
exclude_columns = parse_file_or_list(kwargs.get('exclude_columns', ""))
exclude_models = parse_file_or_list(kwargs.get('exclude_models', ""))
def skip_field(field):
if exclude_columns:
if verbose_names and field.verbose_name:
if field.verbose_name in exclude_columns:
return True
if field.name in exclude_columns:
return True
return False
t = loader.get_template('django_extensions/graph_models/head.html')
c = Context({})
dot = t.render(c)
apps = []
if all_applications:
apps = models.get_apps()
for app_label in app_labels:
app = models.get_app(app_label)
if not app in apps:
apps.append(app)
graphs = []
for app in apps:
graph = Context({
'name': '"%s"' % app.__name__,
'app_name': "%s" % '.'.join(app.__name__.split('.')[:-1]),
'cluster_app_name': "cluster_%s" % app.__name__.replace(".", "_"),
'disable_fields': disable_fields,
'use_subgraph': use_subgraph,
'models': []
})
for appmodel in get_models(app):
abstracts = [e.__name__ for e in appmodel.__bases__ if hasattr(e, '_meta') and e._meta.abstract]
# collect all attribs of abstract superclasses
def getBasesAbstractFields(c):
_abstract_fields = []
for e in c.__bases__:
if hasattr(e, '_meta') and e._meta.abstract:
_abstract_fields.extend(e._meta.fields)
_abstract_fields.extend(getBasesAbstractFields(e))
return _abstract_fields
abstract_fields = getBasesAbstractFields(appmodel)
model = {
'app_name': appmodel.__module__.replace(".", "_"),
'name': appmodel.__name__,
'abstracts': abstracts,
'fields': [],
'relations': []
}
# consider given model name ?
def consider(model_name):
if exclude_models and model_name in exclude_models:
return False
return not include_models or model_name in include_models
if not consider(appmodel._meta.object_name):
continue
if verbose_names and appmodel._meta.verbose_name:
model['label'] = appmodel._meta.verbose_name
else:
model['label'] = model['name']
# model attributes
def add_attributes(field):
if verbose_names and field.verbose_name:
label = field.verbose_name
else:
label = field.name
model['fields'].append({
'name': field.name,
'label': label,
'type': type(field).__name__,
'blank': field.blank,
'abstract': field in abstract_fields,
})
for field in appmodel._meta.fields:
if skip_field(field):
continue
add_attributes(field)
if appmodel._meta.many_to_many:
for field in appmodel._meta.many_to_many:
if skip_field(field):
continue
add_attributes(field)
# relations
def add_relation(field, extras=""):
if verbose_names and field.verbose_name:
label = field.verbose_name
else:
label = field.name
# show related field name
if hasattr(field, 'related_query_name'):
label += ' (%s)' % field.related_query_name()
_rel = {
'target_app': field.rel.to.__module__.replace('.', '_'),
'target': field.rel.to.__name__,
'type': type(field).__name__,
'name': field.name,
'label': label,
'arrows': extras,
'needs_node': True
}
if _rel not in model['relations'] and consider(_rel['target']):
model['relations'].append(_rel)
for field in appmodel._meta.fields:
if skip_field(field):
continue
if isinstance(field, OneToOneField):
add_relation(field, '[arrowhead=none arrowtail=none]')
elif isinstance(field, ForeignKey):
add_relation(field)
if appmodel._meta.many_to_many:
for field in appmodel._meta.many_to_many:
if skip_field(field):
continue
if isinstance(field, ManyToManyField):
if (getattr(field, 'creates_table', False) or # django 1.1.
(hasattr(field.rel.through, '_meta') and field.rel.through._meta.auto_created)): # django 1.2
add_relation(field, '[arrowhead=normal arrowtail=normal]')
elif isinstance(field, GenericRelation):
add_relation(field, mark_safe('[style="dotted"] [arrowhead=normal arrowtail=normal]'))
graph['models'].append(model)
graphs.append(graph)
nodes = []
for graph in graphs:
nodes.extend([e['name'] for e in graph['models']])
for graph in graphs:
# don't draw duplication nodes because of relations
for model in graph['models']:
for relation in model['relations']:
if relation['target'] in nodes:
relation['needs_node'] = False
# render templates
t = loader.get_template('django_extensions/graph_models/body.html')
dot += '\n' + t.render(graph)
for graph in graphs:
t = loader.get_template('django_extensions/graph_models/rel.html')
dot += '\n' + t.render(graph)
t = loader.get_template('django_extensions/graph_models/tail.html')
c = Context({})
dot += '\n' + t.render(c)
return dot
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "hadgi:L:x:X:",
["help", "all_applications", "disable_fields", "group_models", "include_models=", "verbose_names", "language=", "exclude_columns=", "exclude_models="])
except getopt.GetoptError, error:
print __doc__
sys.exit(error)
kwargs = {}
for opt, arg in opts:
if opt in ("-h", "--help"):
print __doc__
sys.exit()
if opt in ("-a", "--all_applications"):
kwargs['all_applications'] = True
if opt in ("-d", "--disable_fields"):
kwargs['disable_fields'] = True
if opt in ("-g", "--group_models"):
kwargs['group_models'] = True
if opt in ("-i", "--include_models"):
kwargs['include_models'] = arg
if opt in ("-n", "--verbose-names"):
kwargs['verbose_names'] = True
if opt in ("-L", "--language"):
kwargs['language'] = arg
if opt in ("-x", "--exclude_columns"):
kwargs['exclude_columns'] = arg
if opt in ("-X", "--exclude_models"):
kwargs['exclude_models'] = arg
if not args and not kwargs.get('all_applications', False):
print __doc__
sys.exit()
print generate_dot(args, **kwargs)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,11 @@
"""
signals we use to trigger regular batch jobs
"""
from django.dispatch import Signal
run_minutely_jobs = Signal()
run_hourly_jobs = Signal()
run_daily_jobs = Signal()
run_weekly_jobs = Signal()
run_monthly_jobs = Signal()
run_yearly_jobs = Signal()

View File

@ -0,0 +1,8 @@
from django.conf import settings
import os
def get_project_root():
""" get the project root directory """
settings_mod = __import__(settings.SETTINGS_MODULE, {}, {}, [''])
return os.path.dirname(os.path.abspath(settings_mod.__file__))

View File

@ -0,0 +1,239 @@
"""
MongoDB model fields emulating Django Extensions' additional model fields
These fields are essentially identical to existing Extensions fields, but South hooks have been removed (since mongo requires no schema migration)
"""
from django.template.defaultfilters import slugify
from django import forms
from mongoengine.fields import StringField, DateTimeField
import datetime
import re
from django.utils.translation import ugettext_lazy as _
try:
import uuid
except ImportError:
from django_extensions.utils import uuid
class SlugField(StringField):
description = _("String (up to %(max_length)s)")
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 50)
# Set db_index=True unless it's been set manually.
if 'db_index' not in kwargs:
kwargs['db_index'] = True
super(SlugField, self).__init__(*args, **kwargs)
def get_internal_type(self):
return "SlugField"
def formfield(self, **kwargs):
defaults = {'form_class': forms.SlugField}
defaults.update(kwargs)
return super(SlugField, self).formfield(**defaults)
class AutoSlugField(SlugField):
""" AutoSlugField, adapted for MongoDB
By default, sets editable=False, blank=True.
Required arguments:
populate_from
Specifies which field or list of fields the slug is populated from.
Optional arguments:
separator
Defines the used separator (default: '-')
overwrite
If set to True, overwrites the slug on every save (default: False)
Inspired by SmileyChris' Unique Slugify snippet:
http://www.djangosnippets.org/snippets/690/
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('blank', True)
kwargs.setdefault('editable', False)
populate_from = kwargs.pop('populate_from', None)
if populate_from is None:
raise ValueError("missing 'populate_from' argument")
else:
self._populate_from = populate_from
self.separator = kwargs.pop('separator', u'-')
self.overwrite = kwargs.pop('overwrite', False)
super(AutoSlugField, self).__init__(*args, **kwargs)
def _slug_strip(self, value):
"""
Cleans up a slug by removing slug separator characters that occur at
the beginning or end of a slug.
If an alternate separator is used, it will also replace any instances
of the default '-' separator with the new separator.
"""
re_sep = '(?:-|%s)' % re.escape(self.separator)
value = re.sub('%s+' % re_sep, self.separator, value)
return re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
def slugify_func(self, content):
return slugify(content)
def create_slug(self, model_instance, add):
# get fields to populate from and slug field to set
if not isinstance(self._populate_from, (list, tuple)):
self._populate_from = (self._populate_from, )
slug_field = model_instance._meta.get_field(self.attname)
if add or self.overwrite:
# slugify the original field content and set next step to 2
slug_for_field = lambda field: self.slugify_func(getattr(model_instance, field))
slug = self.separator.join(map(slug_for_field, self._populate_from))
next = 2
else:
# get slug from the current model instance and calculate next
# step from its number, clean-up
slug = self._slug_strip(getattr(model_instance, self.attname))
next = slug.split(self.separator)[-1]
if next.isdigit():
slug = self.separator.join(slug.split(self.separator)[:-1])
next = int(next)
else:
next = 2
# strip slug depending on max_length attribute of the slug field
# and clean-up
slug_len = slug_field.max_length
if slug_len:
slug = slug[:slug_len]
slug = self._slug_strip(slug)
original_slug = slug
# exclude the current model instance from the queryset used in finding
# the next valid slug
queryset = model_instance.__class__._default_manager.all()
if model_instance.pk:
queryset = queryset.exclude(pk=model_instance.pk)
# form a kwarg dict used to impliment any unique_together contraints
kwargs = {}
for params in model_instance._meta.unique_together:
if self.attname in params:
for param in params:
kwargs[param] = getattr(model_instance, param, None)
kwargs[self.attname] = slug
# increases the number while searching for the next valid slug
# depending on the given slug, clean-up
while not slug or queryset.filter(**kwargs):
slug = original_slug
end = '%s%s' % (self.separator, next)
end_len = len(end)
if slug_len and len(slug)+end_len > slug_len:
slug = slug[:slug_len-end_len]
slug = self._slug_strip(slug)
slug = '%s%s' % (slug, end)
kwargs[self.attname] = slug
next += 1
return slug
def pre_save(self, model_instance, add):
value = unicode(self.create_slug(model_instance, add))
setattr(model_instance, self.attname, value)
return value
def get_internal_type(self):
return "SlugField"
class CreationDateTimeField(DateTimeField):
""" CreationDateTimeField
By default, sets editable=False, blank=True, default=datetime.now
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('default', datetime.datetime.now)
DateTimeField.__init__(self, *args, **kwargs)
def get_internal_type(self):
return "DateTimeField"
class ModificationDateTimeField(CreationDateTimeField):
""" ModificationDateTimeField
By default, sets editable=False, blank=True, default=datetime.now
Sets value to datetime.now() on each save of the model.
"""
def pre_save(self, model, add):
value = datetime.datetime.now()
setattr(model, self.attname, value)
return value
def get_internal_type(self):
return "DateTimeField"
class UUIDVersionError(Exception):
pass
class UUIDField(StringField):
""" UUIDField
By default uses UUID version 1 (generate from host ID, sequence number and current time)
The field support all uuid versions which are natively supported by the uuid python module.
For more information see: http://docs.python.org/lib/module-uuid.html
"""
def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs):
kwargs['max_length'] = 36
self.auto = auto
self.version = version
if version==1:
self.node, self.clock_seq = node, clock_seq
elif version==3 or version==5:
self.namespace, self.name = namespace, name
StringField.__init__(self, verbose_name, name, **kwargs)
def get_internal_type(self):
return StringField.__name__
def contribute_to_class(self, cls, name):
if self.primary_key:
assert not cls._meta.has_auto_field, "A model can't have more than one AutoField: %s %s %s; have %s" % (self,cls,name,cls._meta.auto_field)
super(UUIDField, self).contribute_to_class(cls, name)
cls._meta.has_auto_field = True
cls._meta.auto_field = self
else:
super(UUIDField, self).contribute_to_class(cls, name)
def create_uuid(self):
if not self.version or self.version==4:
return uuid.uuid4()
elif self.version==1:
return uuid.uuid1(self.node, self.clock_seq)
elif self.version==2:
raise UUIDVersionError("UUID version 2 is not supported.")
elif self.version==3:
return uuid.uuid3(self.namespace, self.name)
elif self.version==5:
return uuid.uuid5(self.namespace, self.name)
else:
raise UUIDVersionError("UUID version %s is not valid." % self.version)
def pre_save(self, model_instance, add):
if self.auto and add:
value = unicode(self.create_uuid())
setattr(model_instance, self.attname, value)
return value
else:
value = super(UUIDField, self).pre_save(model_instance, add)
if self.auto and not value:
value = unicode(self.create_uuid())
setattr(model_instance, self.attname, value)
return value

View File

@ -0,0 +1,59 @@
"""
Encrypted fields from Django Extensions, modified for use with mongoDB
"""
from mongoengine.base import BaseField
from django.core.exceptions import ImproperlyConfigured
from django import forms
from django.conf import settings
try:
from keyczar import keyczar
except ImportError:
raise ImportError('Using an encrypted field requires the Keyczar module. You can obtain Keyczar from http://www.keyczar.org/.')
class BaseEncryptedField(BaseField):
prefix = 'enc_str:::'
def __init__(self, *args, **kwargs):
if not hasattr(settings, 'ENCRYPTED_FIELD_KEYS_DIR'):
raise ImproperlyConfigured('You must set settings.ENCRYPTED_FIELD_KEYS_DIR to your Keyczar keys directory.')
self.crypt = keyczar.Crypter.Read(settings.ENCRYPTED_FIELD_KEYS_DIR)
super(BaseEncryptedField, self).__init__(*args, **kwargs)
def to_python(self, value):
if (value.startswith(self.prefix)):
retval = self.crypt.Decrypt(value[len(self.prefix):])
else:
retval = value
return retval
def get_db_prep_value(self, value):
if not value.startswith(self.prefix):
value = self.prefix + self.crypt.Encrypt(value)
return value
class EncryptedTextField(BaseEncryptedField):
def get_internal_type(self):
return 'StringField'
def formfield(self, **kwargs):
defaults = {'widget': forms.Textarea}
defaults.update(kwargs)
return super(EncryptedTextField, self).formfield(**defaults)
class EncryptedCharField(BaseEncryptedField):
def __init__(self, max_length=None, *args, **kwargs):
if max_length:
max_length += len(self.prefix)
super(EncryptedCharField, self).__init__(max_length=max_length, *args, **kwargs)
def get_internal_type(self):
return "StringField"
def formfield(self, **kwargs):
defaults = {'max_length': self.max_length}
defaults.update(kwargs)
return super(EncryptedCharField, self).formfield(**defaults)

View File

@ -0,0 +1,75 @@
"""
JSONField automatically serializes most Python terms to JSON data.
Creates a TEXT field with a default value of "{}". See test_json.py for
more information.
from django.db import models
from django_extensions.db.fields import json
class LOL(models.Model):
extra = json.JSONField()
"""
import datetime
from decimal import Decimal
from django.conf import settings
from django.utils import simplejson
from django.utils.encoding import smart_unicode
from mongoengine.fields import StringField
class JSONEncoder(simplejson.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return str(obj)
elif isinstance(obj, datetime.datetime):
assert settings.TIME_ZONE == 'UTC'
return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
return simplejson.JSONEncoder.default(self, obj)
def dumps(value):
assert isinstance(value, dict)
return JSONEncoder().encode(value)
def loads(txt):
value = simplejson.loads(
txt,
parse_float = Decimal,
encoding = settings.DEFAULT_CHARSET)
assert isinstance(value, dict)
return value
class JSONDict(dict):
"""
Hack so repr() called by dumpdata will output JSON instead of
Python formatted data. This way fixtures will work!
"""
def __repr__(self):
return dumps(self)
class JSONField(StringField):
"""JSONField is a generic textfield that neatly serializes/unserializes
JSON objects seamlessly. Main thingy must be a dict object."""
def __init__(self, *args, **kwargs):
if 'default' not in kwargs:
kwargs['default'] = '{}'
StringField.__init__(self, *args, **kwargs)
def to_python(self, value):
"""Convert our string value to JSON after we load it from the DB"""
if not value:
return {}
elif isinstance(value, basestring):
res = loads(value)
assert isinstance(res, dict)
return JSONDict(**res)
else:
return value
def get_db_prep_save(self, value):
"""Convert our JSON object to a string before we save"""
if not value:
return super(JSONField, self).get_db_prep_save("")
else:
return super(JSONField, self).get_db_prep_save(dumps(value))

View File

@ -0,0 +1,69 @@
"""
Django Extensions abstract base mongoengine Document classes.
"""
import datetime
from mongoengine.document import Document
from mongoengine.fields import StringField, IntField, DateTimeField
from mongoengine.queryset import QuerySetManager
from django.utils.translation import ugettext_lazy as _
from django_extensions.mongodb.fields import (ModificationDateTimeField,
CreationDateTimeField, AutoSlugField)
class TimeStampedModel(Document):
""" TimeStampedModel
An abstract base class model that provides self-managed "created" and
"modified" fields.
"""
created = CreationDateTimeField(_('created'))
modified = ModificationDateTimeField(_('modified'))
class Meta:
abstract = True
class TitleSlugDescriptionModel(Document):
""" TitleSlugDescriptionModel
An abstract base class model that provides title and description fields
and a self-managed "slug" field that populates from the title.
"""
title = StringField(_('title'), max_length=255)
slug = AutoSlugField(_('slug'), populate_from='title')
description = StringField(_('description'), blank=True, null=True)
class Meta:
abstract = True
class ActivatorModelManager(QuerySetManager):
""" ActivatorModelManager
Manager to return instances of ActivatorModel: SomeModel.objects.active() / .inactive()
"""
def active(self):
""" Returns active instances of ActivatorModel: SomeModel.objects.active() """
return super(ActivatorModelManager, self).get_query_set().filter(status=1)
def inactive(self):
""" Returns inactive instances of ActivatorModel: SomeModel.objects.inactive() """
return super(ActivatorModelManager, self).get_query_set().filter(status=0)
class ActivatorModel(Document):
""" ActivatorModel
An abstract base class model that provides activate and deactivate fields.
"""
STATUS_CHOICES = (
(0, _('Inactive')),
(1, _('Active')),
)
status = IntField(_('status'), choices=STATUS_CHOICES,
default=1)
activate_date = DateTimeField(blank=True, null=True,
help_text=_('keep empty for an immediate activation'))
deactivate_date = DateTimeField(blank=True, null=True,
help_text=_('keep empty for indefinite activation'))
objects = ActivatorModelManager()
class Meta:
abstract = True
def save(self, *args, **kwargs):
if not self.activate_date:
self.activate_date = datetime.datetime.now()
super(ActivatorModel, self).save(*args, **kwargs)

View File

@ -0,0 +1,7 @@
from django.conf import settings
REPLACEMENTS = {
}
add_replacements = getattr(settings, 'EXTENSIONS_REPLACEMENTS', {})
REPLACEMENTS.update(add_replacements)

View File

@ -0,0 +1,92 @@
"""
Similar to syntax_color.py but this is intended more for being able to
copy+paste actual code into your Django templates without needing to
escape or anything crazy.
http://lobstertech.com/2008/aug/30/django_syntax_highlight_template_tag/
Example:
{% load highlighting %}
<style>
@import url("http://lobstertech.com/media/css/highlight.css");
.highlight { background: #f8f8f8; }
.highlight { font-size: 11px; margin: 1em; border: 1px solid #ccc;
border-left: 3px solid #F90; padding: 0; }
.highlight pre { padding: 1em; overflow: auto; line-height: 120%; margin: 0; }
.predesc { margin: 1.5em 1.5em -2.5em 1em; text-align: right;
font: bold 12px Tahoma, Arial, sans-serif;
letter-spacing: 1px; color: #333; }
</style>
<h2>check out this code</h2>
{% highlight 'python' 'Excerpt: blah.py' %}
def need_food(self):
print "Love is <colder> than &death&"
{% endhighlight %}
"""
from pygments import highlight as pyghighlight
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.formatters import HtmlFormatter
from django.conf import settings
from django import template
from django.template import Template, Context, Node, Variable
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter
@stringfilter
def parse_template(value):
return mark_safe(Template(value).render(Context()))
parse_template.is_safe = True
class CodeNode(Node):
def __init__(self, language, nodelist, name=''):
self.language = Variable(language)
self.nodelist = nodelist
if name:
self.name = Variable(name)
else:
self.name = None
def render(self, context):
code = self.nodelist.render(context).strip()
lexer = get_lexer_by_name(self.language.resolve(context))
formatter = HtmlFormatter(linenos=False)
html = ""
if self.name:
name = self.name.resolve(context)
html = '<div class="predesc"><span>%s</span></div>' % (name)
return html + pyghighlight(code, lexer, formatter)
@register.tag
def highlight(parser, token):
"""
Allows you to put a highlighted source code <pre> block in your code.
This takes two arguments, the language and a little explaination message
that will be generated before the code. The second argument is optional.
Your code will be fed through pygments so you can use any language it
supports.
{% load highlighting %}
{% highlight 'python' 'Excerpt: blah.py' %}
def need_food(self):
print "Love is colder than death"
{% endhighlight %}
"""
nodelist = parser.parse(('endhighlight',))
parser.delete_first_token()
bits = token.split_contents()[1:]
if len(bits) < 1:
raise TemplateSyntaxError("'highlight' statement requires an argument")
return CodeNode(bits[0], nodelist, *bits[1:])

View File

@ -0,0 +1,97 @@
r"""
Template filter for rendering a string with syntax highlighting.
It relies on Pygments to accomplish this.
Some standard usage examples (from within Django templates).
Coloring a string with the Python lexer:
{% load syntax_color %}
{{ code_string|colorize:"python" }}
You may use any lexer in Pygments. The complete list of which
can be found [on the Pygments website][1].
[1]: http://pygments.org/docs/lexers/
You may also have Pygments attempt to guess the correct lexer for
a particular string. However, if may not be able to choose a lexer,
in which case it will simply return the string unmodified. This is
less efficient compared to specifying the lexer to use.
{{ code_string|colorize }}
You may also render the syntax highlighed text with line numbers.
{% load syntax_color %}
{{ some_code|colorize_table:"html+django" }}
{{ let_pygments_pick_for_this_code|colorize_table }}
Please note that before you can load the ``syntax_color`` template filters
you will need to add the ``django_extensions.utils`` application to the
``INSTALLED_APPS``setting in your project's ``settings.py`` file.
"""
__author__ = 'Will Larson <lethain@gmail.com>'
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
from django.core.exceptions import ImproperlyConfigured
try:
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import get_lexer_by_name, guess_lexer, ClassNotFound
except ImportError:
raise ImproperlyConfigured(
"Please install 'pygments' library to use syntax_color.")
register = template.Library()
@register.simple_tag
def pygments_css():
return HtmlFormatter().get_style_defs('.highlight')
def generate_pygments_css(path=None):
if path is None:
import os
path = os.path.join(os.getcwd(), 'pygments.css')
f = open(path, 'w')
f.write(pygments_css())
f.close()
def get_lexer(value, arg):
if arg is None:
return guess_lexer(value)
return get_lexer_by_name(arg)
@register.filter(name='colorize')
@stringfilter
def colorize(value, arg=None):
try:
return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter()))
except ClassNotFound:
return value
@register.filter(name='colorize_table')
@stringfilter
def colorize_table(value, arg=None):
try:
return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(linenos='table')))
except ClassNotFound:
return value
@register.filter(name='colorize_noclasses')
@stringfilter
def colorize_noclasses(value, arg=None):
try:
return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(noclasses=True)))
except ClassNotFound:
return value

View File

@ -0,0 +1,22 @@
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
def truncateletters(value, arg):
"""
Truncates a string after a certain number of letters
Argument: Number of letters to truncate after
"""
from django_extensions.utils.text import truncate_letters
try:
length = int(arg)
except ValueError: # invalid literal for int()
return value # Fail silently
return truncate_letters(value, length)
truncateletters.is_safe = True
truncateletters = stringfilter(truncateletters)
register.filter(truncateletters)

View File

@ -0,0 +1,61 @@
from django.template import Library
from django.utils.encoding import force_unicode
import re
register = Library()
re_widont = re.compile(r'\s+(\S+\s*)$')
re_widont_html = re.compile(r'([^<>\s])\s+([^<>\s]+\s*)(</?(?:address|blockquote|br|dd|div|dt|fieldset|form|h[1-6]|li|noscript|p|td|th)[^>]*>|$)', re.IGNORECASE)
def widont(value, count=1):
"""
Adds an HTML non-breaking space between the final two words of the string to
avoid "widowed" words.
Examples:
>>> print widont('Test me out')
Test me&nbsp;out
>>> widont('It works with trailing spaces too ')
u'It works with trailing spaces&nbsp;too '
>>> print widont('NoEffect')
NoEffect
"""
def replace(matchobj):
return u'&nbsp;%s' % matchobj.group(1)
for i in range(count):
value = re_widont.sub(replace, force_unicode(value))
return value
def widont_html(value):
"""
Adds an HTML non-breaking space between the final two words at the end of
(and in sentences just outside of) block level tags to avoid "widowed"
words.
Examples:
>>> print widont_html('<h2>Here is a simple example </h2> <p>Single</p>')
<h2>Here is a simple&nbsp;example </h2> <p>Single</p>
>>> print widont_html('<p>test me<br /> out</p><h2>Ok?</h2>Not in a p<p title="test me">and this</p>')
<p>test&nbsp;me<br /> out</p><h2>Ok?</h2>Not in a&nbsp;p<p title="test me">and&nbsp;this</p>
>>> print widont_html('leading text <p>test me out</p> trailing text')
leading&nbsp;text <p>test me&nbsp;out</p> trailing&nbsp;text
"""
def replace(matchobj):
return u'%s&nbsp;%s%s' % matchobj.groups()
return re_widont_html.sub(replace, force_unicode(value))
register.filter(widont)
register.filter(widont_html)
if __name__ == "__main__":
def _test():
import doctest
doctest.testmod()
_test()

View File

@ -0,0 +1,13 @@
from django.db import models
from django_extensions.tests.utils import UTILS_TRUNCATE_LETTERS_TESTS
from django_extensions.tests.utils import UTILS_UUID_TESTS
try:
from django_extensions.tests.encrypted_fields import EncryptedFieldsTestCase
from django_extensions.tests.models import Secret
except ImportError:
pass
__test__ = {
'UTILS_TRUNCATE_LETTERS_TESTS': UTILS_TRUNCATE_LETTERS_TESTS,
'UTILS_UUID_TESTS': UTILS_UUID_TESTS,
}

View File

@ -0,0 +1,74 @@
import unittest
from django.db import connection
from django.conf import settings
from django.core.management import call_command
from django.db.models import loading
# Only perform encrypted fields tests if keyczar is present
# Resolves http://github.com/django-extensions/django-extensions/issues/#issue/17
try:
from keyczar import keyczar
from django_extensions.tests.models import Secret
from django_extensions.db.fields.encrypted import EncryptedTextField, EncryptedCharField
keyczar_active = True
except ImportError:
keyczar_active = False
class EncryptedFieldsTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
if keyczar_active:
self.crypt = keyczar.Crypter.Read(settings.ENCRYPTED_FIELD_KEYS_DIR)
super(EncryptedFieldsTestCase, self).__init__(*args, **kwargs)
def setUp(self):
self.old_installed_apps = settings.INSTALLED_APPS
settings.INSTALLED_APPS.append('django_extensions.tests')
loading.cache.loaded = False
call_command('syncdb', verbosity=0)
def tearDown(self):
settings.INSTALLED_APPS = self.old_installed_apps
def testCharFieldCreate(self):
if not keyczar_active:
return
test_val = "Test Secret"
secret = Secret.objects.create(name=test_val)
cursor = connection.cursor()
query = "SELECT name FROM %s WHERE id = %d" % (Secret._meta.db_table, secret.id)
cursor.execute(query)
db_val, = cursor.fetchone()
decrypted_val = self.crypt.Decrypt(db_val[len(EncryptedCharField.prefix):])
self.assertEqual(test_val, decrypted_val)
def testCharFieldRead(self):
if not keyczar_active:
return
test_val = "Test Secret"
secret = Secret.objects.create(name=test_val)
retrieved_secret = Secret.objects.get(id=secret.id)
self.assertEqual(test_val, retrieved_secret.name)
def testTextFieldCreate(self):
if not keyczar_active:
return
test_val = "Test Secret"
secret = Secret.objects.create(text=test_val)
cursor = connection.cursor()
query = "SELECT text FROM %s WHERE id = %d" % (Secret._meta.db_table, secret.id)
cursor.execute(query)
db_val, = cursor.fetchone()
decrypted_val = self.crypt.Decrypt(db_val[len(EncryptedCharField.prefix):])
self.assertEqual(test_val, decrypted_val)
def testTextFieldRead(self):
if not keyczar_active:
return
test_val = "Test Secret"
secret = Secret.objects.create(text=test_val)
retrieved_secret = Secret.objects.get(id=secret.id)
self.assertEqual(test_val, retrieved_secret.text)

View File

@ -0,0 +1,17 @@
from django.db import models
try:
from django_extensions.db.fields.encrypted import EncryptedTextField, EncryptedCharField
except ImportError:
class EncryptedCharField():
def __init__(self, **kwargs):
pass
class EncryptedTextField():
def __init__(self, **kwargs):
pass
class Secret(models.Model):
name = EncryptedCharField(blank=True, max_length=255)
text = EncryptedTextField(blank=True)

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
UTILS_TRUNCATE_LETTERS_TESTS = """
>>> from django_extensions.utils.text import truncate_letters
>>> truncate_letters("hello tests", 100)
u'hello tests'
>>> truncate_letters("hello tests", 5)
u'hello...'
>>> for i in range(10,-1,-1): truncate_letters("hello tests", i),i
(u'hello test...', 10)
(u'hello tes...', 9)
(u'hello te...', 8)
(u'hello t...', 7)
(u'hello ...', 6)
(u'hello...', 5)
(u'hell...', 4)
(u'hel...', 3)
(u'he...', 2)
(u'h...', 1)
(u'...', 0)
>>> truncate_letters("峠 (とうげ tōge - mountain pass)", 10)
u'\u5ce0 (\u3068\u3046\u3052 t\u014dg...'
"""
UTILS_UUID_TESTS = """
>>> from django_extensions.utils import uuid
# make a UUID using an MD5 hash of a namespace UUID and a name
>>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
# make a UUID using a SHA-1 hash of a namespace UUID and a name
>>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
# make a UUID from a string of hex digits (braces and hyphens ignored)
>>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
# convert a UUID to a string of hex digits in standard form
>>> str(x)
'00010203-0405-0607-0809-0a0b0c0d0e0f'
# get the raw 16 bytes of the UUID
>>> x.bytes
'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f'
# make a UUID from a 16-byte string
>>> uuid.UUID(bytes=x.bytes)
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
"""

View File

@ -0,0 +1,214 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
##Author Igor Támara igor@tamarapatino.org
##Use this little program as you wish, if you
#include it in your work, let others know you
#are using it preserving this note, you have
#the right to make derivative works, Use it
#at your own risk.
#Tested to work on(etch testing 13-08-2007):
# Python 2.4.4 (#2, Jul 17 2007, 11:56:54)
# [GCC 4.1.3 20070629 (prerelease) (Debian 4.1.2-13)] on linux2
dependclasses = ["User", "Group", "Permission", "Message"]
import codecs
import sys
import gzip
from xml.dom.minidom import *
import re
#Type dictionary translation types SQL -> Django
tsd = {
"text": "TextField",
"date": "DateField",
"varchar": "CharField",
"int": "IntegerField",
"float": "FloatField",
"serial": "AutoField",
"boolean": "BooleanField",
"numeric": "FloatField",
"timestamp": "DateTimeField",
"bigint": "IntegerField",
"datetime": "DateTimeField",
"date": "DateField",
"time": "TimeField",
"bool": "BooleanField",
"int": "IntegerField",
}
#convert varchar -> CharField
v2c = re.compile('varchar\((\d+)\)')
def index(fks, id):
"""Looks for the id on fks, fks is an array of arrays, each array has on [1]
the id of the class in a dia diagram. When not present returns None, else
it returns the position of the class with id on fks"""
for i, j in fks.items():
if fks[i][1] == id:
return i
return None
def addparentstofks(rels, fks):
"""Gets a list of relations, between parents and sons and a dict of
clases named in dia, and modifies the fks to add the parent as fk to get
order on the output of classes and replaces the base class of the son, to
put the class parent name.
"""
for j in rels:
son = index(fks, j[1])
parent = index(fks, j[0])
fks[son][2] = fks[son][2].replace("models.Model", parent)
if parent not in fks[son][0]:
fks[son][0].append(parent)
def dia2django(archivo):
models_txt = ''
f = codecs.open(archivo, "rb")
#dia files are gzipped
data = gzip.GzipFile(fileobj=f).read()
ppal = parseString(data)
#diagram -> layer -> object -> UML - Class -> name, (attribs : composite -> name,type)
datos = ppal.getElementsByTagName("dia:diagram")[0].getElementsByTagName("dia:layer")[0].getElementsByTagName("dia:object")
clases = {}
herit = []
imports = u""
for i in datos:
#Look for the classes
if i.getAttribute("type") == "UML - Class":
myid = i.getAttribute("id")
for j in i.childNodes:
if j.nodeType == Node.ELEMENT_NODE and j.hasAttributes():
if j.getAttribute("name") == "name":
actclas = j.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
myname = "\nclass %s(models.Model) :\n" % actclas
clases[actclas] = [[], myid, myname, 0]
if j.getAttribute("name") == "attributes":
for l in j.getElementsByTagName("dia:composite"):
if l.getAttribute("type") == "umlattribute":
#Look for the attribute name and type
for k in l.getElementsByTagName("dia:attribute"):
if k.getAttribute("name") == "name":
nc = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
elif k.getAttribute("name") == "type":
tc = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
elif k.getAttribute("name") == "value":
val = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
if val == '##':
val = ''
elif k.getAttribute("name") == "visibility" and k.getElementsByTagName("dia:enum")[0].getAttribute("val") == "2":
if tc.replace(" ", "").lower().startswith("manytomanyfield("):
#If we find a class not in our model that is marked as being to another model
newc = tc.replace(" ", "")[16:-1]
if dependclasses.count(newc) == 0:
dependclasses.append(newc)
if tc.replace(" ", "").lower().startswith("foreignkey("):
#If we find a class not in our model that is marked as being to another model
newc = tc.replace(" ", "")[11:-1]
if dependclasses.count(newc) == 0:
dependclasses.append(newc)
#Mapping SQL types to Django
varch = v2c.search(tc)
if tc.replace(" ", "").startswith("ManyToManyField("):
myfor = tc.replace(" ", "")[16:-1]
if actclas == myfor:
#In case of a recursive type, we use 'self'
tc = tc.replace(myfor, "'self'")
elif clases[actclas][0].count(myfor) == 0:
#Adding related class
if myfor not in dependclasses:
#In case we are using Auth classes or external via protected dia visibility
clases[actclas][0].append(myfor)
tc = "models." + tc
if len(val) > 0:
tc = tc.replace(")", "," + val + ")")
elif tc.find("Field") != -1:
if tc.count("()") > 0 and len(val) > 0:
tc = "models.%s" % tc.replace(")", "," + val + ")")
else:
tc = "models.%s(%s)" % (tc, val)
elif tc.replace(" ", "").startswith("ForeignKey("):
myfor = tc.replace(" ", "")[11:-1]
if actclas == myfor:
#In case of a recursive type, we use 'self'
tc = tc.replace(myfor, "'self'")
elif clases[actclas][0].count(myfor) == 0:
#Adding foreign classes
if myfor not in dependclasses:
#In case we are using Auth classes
clases[actclas][0].append(myfor)
tc = "models." + tc
if len(val) > 0:
tc = tc.replace(")", "," + val + ")")
elif varch == None:
tc = "models." + tsd[tc.strip().lower()] + "(" + val + ")"
else:
tc = "models.CharField(max_length=" + varch.group(1) + ")"
if len(val) > 0:
tc = tc.replace(")", ", " + val + " )")
if not (nc == "id" and tc == "AutoField()"):
clases[actclas][2] = clases[actclas][2] + (" %s = %s\n" % (nc, tc))
elif i.getAttribute("type") == "UML - Generalization":
mycons = ['A', 'A']
a = i.getElementsByTagName("dia:connection")
for j in a:
if len(j.getAttribute("to")):
mycons[int(j.getAttribute("handle"))] = j.getAttribute("to")
print mycons
if not 'A' in mycons:
herit.append(mycons)
elif i.getAttribute("type") == "UML - SmallPackage":
a = i.getElementsByTagName("dia:string")
for j in a:
if len(j.childNodes[0].data[1:-1]):
imports += u"from %s.models import *" % j.childNodes[0].data[1:-1]
addparentstofks(herit, clases)
#Ordering the appearance of classes
#First we make a list of the classes each classs is related to.
ordered = []
for j, k in clases.iteritems():
k[2] = k[2] + "\n def __unicode__(self):\n return u\"\"\n"
for fk in k[0]:
if fk not in dependclasses:
clases[fk][3] += 1
ordered.append([j] + k)
i = 0
while i < len(ordered):
mark = i
j = i + 1
while j < len(ordered):
if ordered[i][0] in ordered[j][1]:
mark = j
j += 1
if mark == i:
i += 1
else:
# swap %s in %s" % ( ordered[i] , ordered[mark]) to make ordered[i] to be at the end
if ordered[i][0] in ordered[mark][1] and ordered[mark][0] in ordered[i][1]:
#Resolving simplistic circular ForeignKeys
print "Not able to resolve circular ForeignKeys between %s and %s" % (ordered[i][1], ordered[mark][0])
break
a = ordered[i]
ordered[i] = ordered[mark]
ordered[mark] = a
if i == len(ordered) - 1:
break
ordered.reverse()
if imports:
models_txt = str(imports)
for i in ordered:
models_txt += '%s\n' % str(i[3])
return models_txt
if __name__ == '__main__':
if len(sys.argv) == 2:
dia2django(sys.argv[1])
else:
print " Use:\n \n " + sys.argv[0] + " diagram.dia\n\n"

View File

@ -0,0 +1,14 @@
from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy
def truncate_letters(s, num):
""" truncates a string to a number of letters, similar to truncate_words """
s = force_unicode(s)
length = int(num)
if len(s) > length:
s = s[:length]
if not s.endswith('...'):
s += '...'
return s
truncate_letters = allow_lazy(truncate_letters, unicode)

View File

@ -0,0 +1,564 @@
r"""UUID objects (universally unique identifiers) according to RFC 4122.
This module provides immutable UUID objects (class UUID) and the functions
uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
UUIDs as specified in RFC 4122.
If all you want is a unique ID, you should probably call uuid1() or uuid4().
Note that uuid1() may compromise privacy since it creates a UUID containing
the computer's network address. uuid4() creates a random UUID.
Typical usage:
>>> import uuid
# make a UUID based on the host ID and current time
>>> uuid.uuid1()
UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
# make a UUID using an MD5 hash of a namespace UUID and a name
>>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
# make a random UUID
>>> uuid.uuid4()
UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
# make a UUID using a SHA-1 hash of a namespace UUID and a name
>>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
# make a UUID from a string of hex digits (braces and hyphens ignored)
>>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
# convert a UUID to a string of hex digits in standard form
>>> str(x)
'00010203-0405-0607-0809-0a0b0c0d0e0f'
# get the raw 16 bytes of the UUID
>>> x.bytes
'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
# make a UUID from a 16-byte string
>>> uuid.UUID(bytes=x.bytes)
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
"""
__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
'reserved for NCS compatibility', 'specified in RFC 4122',
'reserved for Microsoft compatibility', 'reserved for future definition'
]
class UUID(object):
"""Instances of the UUID class represent UUIDs as specified in RFC 4122.
UUID objects are immutable, hashable, and usable as dictionary keys.
Converting a UUID to a string with str() yields something in the form
'12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts
five possible forms: a similar string of hexadecimal digits, or a tuple
of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
48-bit values respectively) as an argument named 'fields', or a string
of 16 bytes (with all the integer fields in big-endian order) as an
argument named 'bytes', or a string of 16 bytes (with the first three
fields in little-endian order) as an argument named 'bytes_le', or a
single 128-bit integer as an argument named 'int'.
UUIDs have these read-only attributes:
bytes the UUID as a 16-byte string (containing the six
integer fields in big-endian byte order)
bytes_le the UUID as a 16-byte string (with time_low, time_mid,
and time_hi_version in little-endian byte order)
fields a tuple of the six integer fields of the UUID,
which are also available as six individual attributes
and two derived attributes:
time_low the first 32 bits of the UUID
time_mid the next 16 bits of the UUID
time_hi_version the next 16 bits of the UUID
clock_seq_hi_variant the next 8 bits of the UUID
clock_seq_low the next 8 bits of the UUID
node the last 48 bits of the UUID
time the 60-bit timestamp
clock_seq the 14-bit sequence number
hex the UUID as a 32-character hexadecimal string
int the UUID as a 128-bit integer
urn the UUID as a URN as specified in RFC 4122
variant the UUID variant (one of the constants RESERVED_NCS,
RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
version the UUID version number (1 through 5, meaningful only
when the variant is RFC_4122)
"""
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
int=None, version=None):
r"""Create a UUID from either a string of 32 hexadecimal digits,
a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
in little-endian order as the 'bytes_le' argument, a tuple of six
integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
the 'fields' argument, or a single 128-bit integer as the 'int'
argument. When a string of hex digits is given, curly braces,
hyphens, and a URN prefix are all optional. For example, these
expressions all yield the same UUID:
UUID('{12345678-1234-5678-1234-567812345678}')
UUID('12345678123456781234567812345678')
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
UUID(bytes='\x12\x34\x56\x78'*4)
UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
'\x12\x34\x56\x78\x12\x34\x56\x78')
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
UUID(int=0x12345678123456781234567812345678)
Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must
be given. The 'version' argument is optional; if given, the resulting
UUID will have its variant and version set according to RFC 4122,
overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.
"""
if [hex, bytes, bytes_le, fields, int].count(None) != 4:
raise TypeError('need one of hex, bytes, bytes_le, fields, or int')
if hex is not None:
hex = hex.replace('urn:', '').replace('uuid:', '')
hex = hex.strip('{}').replace('-', '')
if len(hex) != 32:
raise ValueError('badly formed hexadecimal UUID string')
int = long(hex, 16)
if bytes_le is not None:
if len(bytes_le) != 16:
raise ValueError('bytes_le is not a 16-char string')
bytes = (bytes_le[3] + bytes_le[2] + bytes_le[1] + bytes_le[0] +
bytes_le[5] + bytes_le[4] + bytes_le[7] + bytes_le[6] +
bytes_le[8:])
if bytes is not None:
if len(bytes) != 16:
raise ValueError('bytes is not a 16-char string')
int = long(('%02x' * 16) % tuple(map(ord, bytes)), 16)
if fields is not None:
if len(fields) != 6:
raise ValueError('fields is not a 6-tuple')
(time_low, time_mid, time_hi_version,
clock_seq_hi_variant, clock_seq_low, node) = fields
if not 0 <= time_low < 1 << 32L:
raise ValueError('field 1 out of range (need a 32-bit value)')
if not 0 <= time_mid < 1 << 16L:
raise ValueError('field 2 out of range (need a 16-bit value)')
if not 0 <= time_hi_version < 1 << 16L:
raise ValueError('field 3 out of range (need a 16-bit value)')
if not 0 <= clock_seq_hi_variant < 1 << 8L:
raise ValueError('field 4 out of range (need an 8-bit value)')
if not 0 <= clock_seq_low < 1 << 8L:
raise ValueError('field 5 out of range (need an 8-bit value)')
if not 0 <= node < 1 << 48L:
raise ValueError('field 6 out of range (need a 48-bit value)')
clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low
int = ((time_low << 96L) | (time_mid << 80L) |
(time_hi_version << 64L) | (clock_seq << 48L) | node)
if int is not None:
if not 0 <= int < 1 << 128L:
raise ValueError('int is out of range (need a 128-bit value)')
if version is not None:
if not 1 <= version <= 5:
raise ValueError('illegal version number')
# Set the variant to RFC 4122.
int &= ~(0xc000 << 48L)
int |= 0x8000 << 48L
# Set the version number.
int &= ~(0xf000 << 64L)
int |= version << 76L
self.__dict__['int'] = int
def __cmp__(self, other):
if isinstance(other, UUID):
return cmp(self.int, other.int)
return NotImplemented
def __hash__(self):
return hash(self.int)
def __int__(self):
return self.int
def __repr__(self):
return 'UUID(%r)' % str(self)
def __setattr__(self, name, value):
raise TypeError('UUID objects are immutable')
def __str__(self):
hex = '%032x' % self.int
return '%s-%s-%s-%s-%s' % (
hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
def get_bytes(self):
bytes = ''
for shift in range(0, 128, 8):
bytes = chr((self.int >> shift) & 0xff) + bytes
return bytes
bytes = property(get_bytes)
def get_bytes_le(self):
bytes = self.bytes
return (bytes[3] + bytes[2] + bytes[1] + bytes[0] +
bytes[5] + bytes[4] + bytes[7] + bytes[6] + bytes[8:])
bytes_le = property(get_bytes_le)
def get_fields(self):
return (self.time_low, self.time_mid, self.time_hi_version,
self.clock_seq_hi_variant, self.clock_seq_low, self.node)
fields = property(get_fields)
def get_time_low(self):
return self.int >> 96L
time_low = property(get_time_low)
def get_time_mid(self):
return (self.int >> 80L) & 0xffff
time_mid = property(get_time_mid)
def get_time_hi_version(self):
return (self.int >> 64L) & 0xffff
time_hi_version = property(get_time_hi_version)
def get_clock_seq_hi_variant(self):
return (self.int >> 56L) & 0xff
clock_seq_hi_variant = property(get_clock_seq_hi_variant)
def get_clock_seq_low(self):
return (self.int >> 48L) & 0xff
clock_seq_low = property(get_clock_seq_low)
def get_time(self):
return (((self.time_hi_version & 0x0fffL) << 48L) |
(self.time_mid << 32L) | self.time_low)
time = property(get_time)
def get_clock_seq(self):
return (((self.clock_seq_hi_variant & 0x3fL) << 8L) |
self.clock_seq_low)
clock_seq = property(get_clock_seq)
def get_node(self):
return self.int & 0xffffffffffff
node = property(get_node)
def get_hex(self):
return '%032x' % self.int
hex = property(get_hex)
def get_urn(self):
return 'urn:uuid:' + str(self)
urn = property(get_urn)
def get_variant(self):
if not self.int & (0x8000 << 48L):
return RESERVED_NCS
elif not self.int & (0x4000 << 48L):
return RFC_4122
elif not self.int & (0x2000 << 48L):
return RESERVED_MICROSOFT
else:
return RESERVED_FUTURE
variant = property(get_variant)
def get_version(self):
# The version bits are only meaningful for RFC 4122 UUIDs.
if self.variant == RFC_4122:
return int((self.int >> 76L) & 0xf)
version = property(get_version)
def _find_mac(command, args, hw_identifiers, get_index):
import os
for dir in ['', '/sbin/', '/usr/sbin']:
executable = os.path.join(dir, command)
if not os.path.exists(executable):
continue
try:
# LC_ALL to get English output, 2>/dev/null to
# prevent output on stderr
cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args)
pipe = os.popen(cmd)
except IOError:
continue
for line in pipe:
words = line.lower().split()
for i in range(len(words)):
if words[i] in hw_identifiers:
return int(words[get_index(i)].replace(':', ''), 16)
return None
def _ifconfig_getnode():
"""Get the hardware address on Unix by running ifconfig."""
# This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
for args in ('', '-a', '-av'):
mac = _find_mac('ifconfig', args, ['hwaddr', 'ether'], lambda i: i + 1)
if mac:
return mac
import socket
ip_addr = socket.gethostbyname(socket.gethostname())
# Try getting the MAC addr from arp based on our IP address (Solaris).
mac = _find_mac('arp', '-an', [ip_addr], lambda i: -1)
if mac:
return mac
# This might work on HP-UX.
mac = _find_mac('lanscan', '-ai', ['lan0'], lambda i: 0)
if mac:
return mac
return None
def _ipconfig_getnode():
"""Get the hardware address on Windows by running ipconfig.exe."""
import os
import re
dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
try:
import ctypes
buffer = ctypes.create_string_buffer(300)
ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
dirs.insert(0, buffer.value.decode('mbcs'))
except:
pass
for dir in dirs:
try:
pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all')
except IOError:
continue
for line in pipe:
value = line.split(':')[-1].strip().lower()
if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
return int(value.replace('-', ''), 16)
def _netbios_getnode():
"""Get the hardware address on Windows using NetBIOS calls.
See http://support.microsoft.com/kb/118623 for details."""
import win32wnet
import netbios
ncb = netbios.NCB()
ncb.Command = netbios.NCBENUM
ncb.Buffer = adapters = netbios.LANA_ENUM()
adapters._pack()
if win32wnet.Netbios(ncb) != 0:
return
adapters._unpack()
for i in range(adapters.length):
ncb.Reset()
ncb.Command = netbios.NCBRESET
ncb.Lana_num = ord(adapters.lana[i])
if win32wnet.Netbios(ncb) != 0:
continue
ncb.Reset()
ncb.Command = netbios.NCBASTAT
ncb.Lana_num = ord(adapters.lana[i])
ncb.Callname = '*'.ljust(16)
ncb.Buffer = status = netbios.ADAPTER_STATUS()
if win32wnet.Netbios(ncb) != 0:
continue
status._unpack()
bytes = map(ord, status.adapter_address)
return ((bytes[0] << 40L) + (bytes[1] << 32L) + (bytes[2] << 24L) +
(bytes[3] << 16L) + (bytes[4] << 8L) + bytes[5])
# Thanks to Thomas Heller for ctypes and for his help with its use here.
# If ctypes is available, use it to find system routines for UUID generation.
_uuid_generate_random = _uuid_generate_time = _UuidCreate = None
try:
import ctypes
import ctypes.util
_buffer = ctypes.create_string_buffer(16)
# The uuid_generate_* routines are provided by libuuid on at least
# Linux and FreeBSD, and provided by libc on Mac OS X.
for libname in ['uuid', 'c']:
try:
lib = ctypes.CDLL(ctypes.util.find_library(libname))
except:
continue
if hasattr(lib, 'uuid_generate_random'):
_uuid_generate_random = lib.uuid_generate_random
if hasattr(lib, 'uuid_generate_time'):
_uuid_generate_time = lib.uuid_generate_time
# On Windows prior to 2000, UuidCreate gives a UUID containing the
# hardware address. On Windows 2000 and later, UuidCreate makes a
# random UUID and UuidCreateSequential gives a UUID containing the
# hardware address. These routines are provided by the RPC runtime.
# NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last
# 6 bytes returned by UuidCreateSequential are fixed, they don't appear
# to bear any relationship to the MAC address of any network device
# on the box.
try:
lib = ctypes.windll.rpcrt4
except:
lib = None
_UuidCreate = getattr(lib, 'UuidCreateSequential',
getattr(lib, 'UuidCreate', None))
except:
pass
def _unixdll_getnode():
"""Get the hardware address on Unix using ctypes."""
_uuid_generate_time(_buffer)
return UUID(bytes=_buffer.raw).node
def _windll_getnode():
"""Get the hardware address on Windows using ctypes."""
if _UuidCreate(_buffer) == 0:
return UUID(bytes=_buffer.raw).node
def _random_getnode():
"""Get a random node ID, with eighth bit set as suggested by RFC 4122."""
import random
return random.randrange(0, 1 << 48L) | 0x010000000000L
_node = None
def getnode():
"""Get the hardware address as a 48-bit positive integer.
The first time this runs, it may launch a separate program, which could
be quite slow. If all attempts to obtain the hardware address fail, we
choose a random 48-bit number with its eighth bit set to 1 as recommended
in RFC 4122.
"""
global _node
if _node is not None:
return _node
import sys
if sys.platform == 'win32':
getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
else:
getters = [_unixdll_getnode, _ifconfig_getnode]
for getter in getters + [_random_getnode]:
try:
_node = getter()
except:
continue
if _node is not None:
return _node
_last_timestamp = None
def uuid1(node=None, clock_seq=None):
"""Generate a UUID from a host ID, sequence number, and the current time.
If 'node' is not given, getnode() is used to obtain the hardware
address. If 'clock_seq' is given, it is used as the sequence number;
otherwise a random 14-bit sequence number is chosen."""
# When the system provides a version-1 UUID generator, use it (but don't
# use UuidCreate here because its UUIDs don't conform to RFC 4122).
if _uuid_generate_time and node is clock_seq is None:
_uuid_generate_time(_buffer)
return UUID(bytes=_buffer.raw)
global _last_timestamp
import time
nanoseconds = int(time.time() * 1e9)
# 0x01b21dd213814000 is the number of 100-ns intervals between the
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
timestamp = int(nanoseconds / 100) + 0x01b21dd213814000L
if timestamp <= _last_timestamp:
timestamp = _last_timestamp + 1
_last_timestamp = timestamp
if clock_seq is None:
import random
clock_seq = random.randrange(1 << 14L) # instead of stable storage
time_low = timestamp & 0xffffffffL
time_mid = (timestamp >> 32L) & 0xffffL
time_hi_version = (timestamp >> 48L) & 0x0fffL
clock_seq_low = clock_seq & 0xffL
clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL
if node is None:
node = getnode()
return UUID(fields=(time_low, time_mid, time_hi_version,
clock_seq_hi_variant, clock_seq_low, node), version=1)
def uuid3(namespace, name):
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
try:
from hashlib import md5
except ImportError:
from md5 import md5
hash = md5(namespace.bytes + name).digest()
return UUID(bytes=hash[:16], version=3)
def uuid4():
"""Generate a random UUID."""
# When the system provides a version-4 UUID generator, use it.
if _uuid_generate_random:
_uuid_generate_random(_buffer)
return UUID(bytes=_buffer.raw)
# Otherwise, get randomness from urandom or the 'random' module.
try:
import os
return UUID(bytes=os.urandom(16), version=4)
except:
import random
bytes = [chr(random.randrange(256)) for i in range(16)]
return UUID(bytes=bytes, version=4)
def uuid5(namespace, name):
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
try:
from hashlib import sha1 as sha
except ImportError:
from sha import sha
hash = sha(namespace.bytes + name).digest()
return UUID(bytes=hash[:16], version=5)
# The following standard UUIDs are for use with uuid3() or uuid5().
NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')

View File

@ -0,0 +1,21 @@
Metadata-Version: 1.0
Name: django-extensions
Version: 0.7.1
Summary: Extensions for Django
Home-page: http://github.com/django-extensions/django-extensions
Author: Bas van Oostveen
Author-email: v.oostveen@gmail.com
License: New BSD License
Description: django-extensions bundles several useful
additions for Django projects. See the project page for more information:
http://github.com/django-extensions/django-extensions
Platform: any
Classifier: Development Status :: 4 - Beta
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Utilities

View File

@ -0,0 +1,108 @@
MANIFEST.in
setup.cfg
setup.py
django_extensions/__init__.py
django_extensions/models.py
django_extensions/settings.py
django_extensions.egg-info/PKG-INFO
django_extensions.egg-info/SOURCES.txt
django_extensions.egg-info/dependency_links.txt
django_extensions.egg-info/top_level.txt
django_extensions/admin/__init__.py
django_extensions/admin/widgets.py
django_extensions/conf/app_template/__init__.py.tmpl
django_extensions/conf/app_template/forms.py.tmpl
django_extensions/conf/app_template/models.py.tmpl
django_extensions/conf/app_template/urls.py.tmpl
django_extensions/conf/app_template/views.py.tmpl
django_extensions/conf/command_template/management/__init__.py.tmpl
django_extensions/conf/command_template/management/commands/__init__.py.tmpl
django_extensions/conf/command_template/management/commands/sample.py.tmpl
django_extensions/conf/jobs_template/jobs/__init__.py.tmpl
django_extensions/conf/jobs_template/jobs/sample.py.tmpl
django_extensions/conf/jobs_template/jobs/daily/__init__.py.tmpl
django_extensions/conf/jobs_template/jobs/hourly/__init__.py.tmpl
django_extensions/conf/jobs_template/jobs/monthly/__init__.py.tmpl
django_extensions/conf/jobs_template/jobs/weekly/__init__.py.tmpl
django_extensions/conf/jobs_template/jobs/yearly/__init__.py.tmpl
django_extensions/db/__init__.py
django_extensions/db/models.py
django_extensions/db/fields/__init__.py
django_extensions/db/fields/encrypted.py
django_extensions/db/fields/json.py
django_extensions/jobs/__init__.py
django_extensions/jobs/daily/__init__.py
django_extensions/jobs/daily/cache_cleanup.py
django_extensions/jobs/daily/daily_cleanup.py
django_extensions/jobs/hourly/__init__.py
django_extensions/jobs/monthly/__init__.py
django_extensions/jobs/weekly/__init__.py
django_extensions/jobs/yearly/__init__.py
django_extensions/management/__init__.py
django_extensions/management/color.py
django_extensions/management/jobs.py
django_extensions/management/modelviz.py
django_extensions/management/signals.py
django_extensions/management/utils.py
django_extensions/management/commands/__init__.py
django_extensions/management/commands/clean_pyc.py
django_extensions/management/commands/compile_pyc.py
django_extensions/management/commands/create_app.py
django_extensions/management/commands/create_command.py
django_extensions/management/commands/create_jobs.py
django_extensions/management/commands/describe_form.py
django_extensions/management/commands/dumpscript.py
django_extensions/management/commands/export_emails.py
django_extensions/management/commands/find_template.py
django_extensions/management/commands/generate_secret_key.py
django_extensions/management/commands/graph_models.py
django_extensions/management/commands/mail_debug.py
django_extensions/management/commands/notes.py
django_extensions/management/commands/passwd.py
django_extensions/management/commands/print_user_for_session.py
django_extensions/management/commands/reset_db.py
django_extensions/management/commands/runjob.py
django_extensions/management/commands/runjobs.py
django_extensions/management/commands/runprofileserver.py
django_extensions/management/commands/runscript.py
django_extensions/management/commands/runserver_plus.py
django_extensions/management/commands/set_fake_emails.py
django_extensions/management/commands/set_fake_passwords.py
django_extensions/management/commands/shell_plus.py
django_extensions/management/commands/show_templatetags.py
django_extensions/management/commands/show_urls.py
django_extensions/management/commands/sqlcreate.py
django_extensions/management/commands/sqldiff.py
django_extensions/management/commands/sync_media_s3.py
django_extensions/management/commands/syncdata.py
django_extensions/management/commands/unreferenced_files.py
django_extensions/management/commands/update_permissions.py
django_extensions/media/django_extensions/css/jquery.autocomplete.css
django_extensions/media/django_extensions/img/indicator.gif
django_extensions/media/django_extensions/js/jquery.ajaxQueue.js
django_extensions/media/django_extensions/js/jquery.autocomplete.js
django_extensions/media/django_extensions/js/jquery.bgiframe.min.js
django_extensions/media/django_extensions/js/jquery.js
django_extensions/mongodb/__init__.py
django_extensions/mongodb/models.py
django_extensions/mongodb/fields/__init__.py
django_extensions/mongodb/fields/encrypted.py
django_extensions/mongodb/fields/json.py
django_extensions/templates/django_extensions/graph_models/body.html
django_extensions/templates/django_extensions/graph_models/head.html
django_extensions/templates/django_extensions/graph_models/rel.html
django_extensions/templates/django_extensions/graph_models/tail.html
django_extensions/templates/django_extensions/widgets/foreignkey_searchinput.html
django_extensions/templatetags/__init__.py
django_extensions/templatetags/highlighting.py
django_extensions/templatetags/syntax_color.py
django_extensions/templatetags/truncate_letters.py
django_extensions/templatetags/widont.py
django_extensions/tests/__init__.py
django_extensions/tests/encrypted_fields.py
django_extensions/tests/models.py
django_extensions/tests/utils.py
django_extensions/utils/__init__.py
django_extensions/utils/dia2django.py
django_extensions/utils/text.py
django_extensions/utils/uuid.py

View File

@ -0,0 +1 @@
django_extensions

View File

@ -0,0 +1,13 @@
VERSION = (0, 7, 1)
# Dynamically calculate the version based on VERSION tuple
if len(VERSION) > 2 and VERSION[2] is not None:
if isinstance(VERSION[2], int):
str_version = "%s.%s.%s" % VERSION[:3]
else:
str_version = "%s.%s_%s" % VERSION[:3]
else:
str_version = "%s.%s" % VERSION[:2]
__version__ = str_version

View File

@ -0,0 +1,147 @@
#
# Autocomplete feature for admin panel
#
# Most of the code has been written by Jannis Leidel and was updated a bit
# for django_extensions.
# http://jannisleidel.com/2008/11/autocomplete-form-widget-foreignkey-model-fields/
#
# to_string_function, Satchmo adaptation and some comments added by emes
# (Michal Salaban)
#
import operator
from django.http import HttpResponse, HttpResponseNotFound
from django.db import models
from django.db.models.query import QuerySet
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from django.utils.text import get_text_list
try:
from functools import update_wrapper
except ImportError:
from django.utils.functional import update_wrapper
from django_extensions.admin.widgets import ForeignKeySearchInput
from django.conf import settings
if 'reversion' in settings.INSTALLED_APPS:
from reversion.admin import VersionAdmin as ModelAdmin
else:
from django.contrib.admin import ModelAdmin
class ForeignKeyAutocompleteAdmin(ModelAdmin):
"""Admin class for models using the autocomplete feature.
There are two additional fields:
- related_search_fields: defines fields of managed model that
have to be represented by autocomplete input, together with
a list of target model fields that are searched for
input string, e.g.:
related_search_fields = {
'author': ('first_name', 'email'),
}
- related_string_functions: contains optional functions which
take target model instance as only argument and return string
representation. By default __unicode__() method of target
object is used.
"""
related_search_fields = {}
related_string_functions = {}
def get_urls(self):
from django.conf.urls.defaults import patterns, url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.module_name
urlpatterns = patterns('',
url(r'foreignkey_autocomplete/$',
wrap(self.foreignkey_autocomplete),
name='%s_%s_autocomplete' % info),
) + super(ForeignKeyAutocompleteAdmin, self).get_urls()
return urlpatterns
def foreignkey_autocomplete(self, request):
"""
Searches in the fields of the given related model and returns the
result as a simple string to be used by the jQuery Autocomplete plugin
"""
query = request.GET.get('q', None)
app_label = request.GET.get('app_label', None)
model_name = request.GET.get('model_name', None)
search_fields = request.GET.get('search_fields', None)
object_pk = request.GET.get('object_pk', None)
try:
to_string_function = self.related_string_functions[model_name]
except KeyError:
to_string_function = lambda x: x.__unicode__()
if search_fields and app_label and model_name and (query or object_pk):
def construct_search(field_name):
# use different lookup methods depending on the notation
if field_name.startswith('^'):
return "%s__istartswith" % field_name[1:]
elif field_name.startswith('='):
return "%s__iexact" % field_name[1:]
elif field_name.startswith('@'):
return "%s__search" % field_name[1:]
else:
return "%s__icontains" % field_name
model = models.get_model(app_label, model_name)
queryset = model._default_manager.all()
data = ''
if query:
for bit in query.split():
or_queries = [models.Q(**{construct_search(
smart_str(field_name)): smart_str(bit)})
for field_name in search_fields.split(',')]
other_qs = QuerySet(model)
other_qs.dup_select_related(queryset)
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
queryset = queryset & other_qs
data = ''.join([u'%s|%s\n' % (
to_string_function(f), f.pk) for f in queryset])
elif object_pk:
try:
obj = queryset.get(pk=object_pk)
except:
pass
else:
data = to_string_function(obj)
return HttpResponse(data)
return HttpResponseNotFound()
def get_help_text(self, field_name, model_name):
searchable_fields = self.related_search_fields.get(field_name, None)
if searchable_fields:
help_kwargs = {
'model_name': model_name,
'field_list': get_text_list(searchable_fields, _('and')),
}
return _('Use the left field to do %(model_name)s lookups in the fields %(field_list)s.') % help_kwargs
return ''
def formfield_for_dbfield(self, db_field, **kwargs):
"""
Overrides the default widget for Foreignkey fields if they are
specified in the related_search_fields class attribute.
"""
if (isinstance(db_field, models.ForeignKey) and
db_field.name in self.related_search_fields):
model_name = db_field.rel.to._meta.object_name
help_text = self.get_help_text(db_field.name, model_name)
if kwargs.get('help_text'):
help_text = u'%s %s' % (kwargs['help_text'], help_text)
kwargs['widget'] = ForeignKeySearchInput(db_field.rel,
self.related_search_fields[db_field.name])
kwargs['help_text'] = help_text
return super(ForeignKeyAutocompleteAdmin,
self).formfield_for_dbfield(db_field, **kwargs)

View File

@ -0,0 +1,77 @@
from django import forms
from django.conf import settings
from django.utils.safestring import mark_safe
from django.utils.text import truncate_words
from django.template.loader import render_to_string
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
class ForeignKeySearchInput(ForeignKeyRawIdWidget):
"""
A Widget for displaying ForeignKeys in an autocomplete search input
instead in a <select> box.
"""
# Set in subclass to render the widget with a different template
widget_template = None
# Set this to the patch of the search view
search_path = '../foreignkey_autocomplete/'
class Media:
css = {
'all': ('django_extensions/css/jquery.autocomplete.css',)
}
js = (
'django_extensions/js/jquery.js',
'django_extensions/js/jquery.bgiframe.min.js',
'django_extensions/js/jquery.ajaxQueue.js',
'django_extensions/js/jquery.autocomplete.js',
)
def label_for_value(self, value):
key = self.rel.get_related_field().name
obj = self.rel.to._default_manager.get(**{key: value})
return truncate_words(obj, 14)
def __init__(self, rel, search_fields, attrs=None):
self.search_fields = search_fields
super(ForeignKeySearchInput, self).__init__(rel, attrs)
def render(self, name, value, attrs=None):
if attrs is None:
attrs = {}
output = [super(ForeignKeySearchInput, self).render(name, value, attrs)]
opts = self.rel.to._meta
app_label = opts.app_label
model_name = opts.object_name.lower()
related_url = '../../../%s/%s/' % (app_label, model_name)
params = self.url_parameters()
if params:
url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
else:
url = ''
if not 'class' in attrs:
attrs['class'] = 'vForeignKeyRawIdAdminField'
# Call the TextInput render method directly to have more control
output = [forms.TextInput.render(self, name, value, attrs)]
if value:
label = self.label_for_value(value)
else:
label = u''
context = {
'url': url,
'related_url': related_url,
'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX,
'search_path': self.search_path,
'search_fields': ','.join(self.search_fields),
'model_name': model_name,
'app_label': app_label,
'label': label,
'name': name,
}
output.append(render_to_string(self.widget_template or (
'django_extensions/widgets/%s/%s/foreignkey_searchinput.html' % (app_label, model_name),
'django_extensions/widgets/%s/foreignkey_searchinput.html' % app_label,
'django_extensions/widgets/foreignkey_searchinput.html',
), context))
output.reverse()
return mark_safe(u''.join(output))

View File

@ -0,0 +1,3 @@
from django import forms
# place form definition here

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,3 @@
from django.conf.urls.defaults import *
# place app url patterns here

View File

@ -0,0 +1 @@
# Create your views here.

View File

@ -0,0 +1,7 @@
from django.core.management.base import {{ base_command }}
class Command({{ base_command }}):
help = "My shiny new management command."
def {{ handle_method }}:
raise NotImplementedError()

View File

@ -0,0 +1,8 @@
from django_extensions.management.jobs import BaseJob
class Job(BaseJob):
help = "My sample job."
def execute(self):
# executing empty sample job
pass

Some files were not shown because too many files have changed in this diff Show More