From 47a4f24e515ab422b5980ba6a7f8c9995af53070 Mon Sep 17 00:00:00 2001 From: sax Date: Tue, 27 Mar 2012 09:56:41 +0700 Subject: [PATCH] added missed files --- iadmin/actions/csv2/__init__.py | 232 ++++++++++++++ iadmin/actions/csv2/utils.py | 303 ++++++++++++++++++ iadmin/models.py | 11 +- .../img/changelist-filter-button-bg.jpg | Bin 0 -> 2614 bytes iadmin/templates/iadmin/ibase.html | 77 +++++ project/manage.py | 9 + 6 files changed, 631 insertions(+), 1 deletion(-) create mode 100644 iadmin/actions/csv2/__init__.py create mode 100644 iadmin/actions/csv2/utils.py create mode 100644 iadmin/static/iadmin/img/changelist-filter-button-bg.jpg create mode 100644 iadmin/templates/iadmin/ibase.html create mode 100755 project/manage.py diff --git a/iadmin/actions/csv2/__init__.py b/iadmin/actions/csv2/__init__.py new file mode 100644 index 0000000..522c28b --- /dev/null +++ b/iadmin/actions/csv2/__init__.py @@ -0,0 +1,232 @@ +#from functools import update_wrapper +#from django import template +#from django.conf import settings +#from django.conf.urls.defaults import url, patterns +#import tempfile +#from django.contrib import messages +#from django.core.exceptions import ObjectDoesNotExist, ValidationError +#from django.core.files.uploadedfile import TemporaryUploadedFile +#from django.core.urlresolvers import reverse +#from django.db.models.fields.related import ForeignKey +#from django.db.models.loading import get_model +#from django.db.utils import IntegrityError +#from django.http import HttpResponseRedirect +#from django.shortcuts import render_to_response, redirect +#import os +#from iadmin.plugins import IAdminPlugin +#from iadmin.plugins.csv.utils import update_model, open_csv +#from .utils import ImportForm, csv_processor_factory +# +# +#class CSVImporter(IAdminPlugin): +# template_step1 = None +# template_step2 = None +# template_step3 = None +# +# def _get_base_context(self, request, app=None, model=None): +# return template.RequestContext(request, {'app_label': (app or '').lower(), +# 'model_name': (model or '').lower(), +# 'root_path': self.admin_site.root_path or '', +# 'lbl_next': 'Next >>', +# 'lbl_back': '<< Back', +# 'back': request.META['HTTP_REFERER'], +# }, +# current_app=self.name) +# +# def __get_mapping(self, form): +# mapping = {} +# for i, f in enumerate(form._fields): +# field_name = form.cleaned_data['fld_%s' % i] +# column = form.cleaned_data['col_%s' % i] +# rex = form.cleaned_data['rex_%s' % i] +# lk = form.cleaned_data['lkf_%s' % i] +# key = form.cleaned_data['key_%s' % i] +# +# if column >= 0: +# Field, _, _, _ = form._model._meta.get_field_by_name(field_name) +# mapping[field_name] = [column, rex, bool(key), lk, Field] +# return mapping +# +# def _step_1(self, request, app=None, model=None, temp_file_name=None): +# context = self._get_base_context(request, app, model) +# if request.method == 'POST': +# form = ImportForm(app, model, request.POST, request.FILES) +# if form.is_valid(): +# f = request.FILES['csv'] +# fd = open(temp_file_name, 'wb') +# for chunk in f.chunks(): +# fd.write(chunk) +# fd.close() +# if settings.DEBUG: +# messages.info(request, temp_file_name) +# app_name, model_name = form.cleaned_data['model'].split(':') +# goto_url = reverse('%s:model_import_csv' % self.name, +# kwargs={'app': app_name, 'model': model_name, 'page': 2}) +# return HttpResponseRedirect(goto_url) +# else: +# form = ImportForm(app, model, initial={'page': 1}) +# +# context.update({'page': 1, 'form': form, }) +# +# return render_to_response(self.template_step1 or [ +# "iadmin/%s/%s/import_csv_1.html" % (app, model), +# "iadmin/%s/import_csv_1.html" % app, +# "iadmin/import_csv_1.html" +# ], context) +# +# +# def _process_row(self, row, mapping): +# record = {} +# key = {} +# for field_name, (col, rex, is_key, lk, Field) in mapping.items(): +# try: +# raw_val = rex.search(row[col]).group(1) +# field_value = None +# if isinstance(Field, ForeignKey): +# try: +# field_value = Field.rel.to.objects.get(**{lk: raw_val}) +# except Field.rel.to.DoesNotExist: +# pass +# else: +# field_value = Field.to_python(raw_val) +# record[field_name] = field_value +# if is_key: +# key[field_name] = record[field_name] +# except AttributeError, e: +# raise AttributeError('Error processing "%s": Invalid regex' % field_name) +# except Exception, e: +# raise e.__class__('Error processing "%s"' % field_name, e) +# return record, key +# +# def _step_2(self, request, app_name=None, model_name=None, temp_file_name=None): +# records = [] +# context = self._get_base_context(request, app_name, model_name) +# try: +# Form = csv_processor_factory(app_name, model_name, temp_file_name) +# if request.method == 'POST': +# form = Form(request.POST, request.FILES) +# if form.is_valid(): +# mapping = self.__get_mapping(form) +# Model = get_model(app_name, model_name) +# with open_csv(temp_file_name) as csv: +# if form.cleaned_data['header']: +# csv.next() +# for i, row in enumerate(csv): +# if i > 20 and not form.cleaned_data['preview_all']: +# break +# try: +# sample = Model() +# record, key = self._process_row(row, mapping) +# exists = key and Model.objects.filter(**key).exists() or False +# if key and exists: +# sample = Model.objects.get(**key) +# else: +# sample = Model() +# sample = update_model(request, sample, record, mapping) +# records.append([sample, None, row]) +# except (ValidationError, AttributeError), e: +# records.append([sample, str(e), row]) +# except (ValueError, ObjectDoesNotExist, ValidationError), e: +# #messages.error(request, '%s' % e) +# records.append([sample, str(e)]) +# return self._step_3(request, app_name, model_name, temp_file_name, {'records': records, +# 'form': form}) +# +# else: +# form = Form() +# +# context.update({'page': 2, +# 'form': form, +# 'back': reverse('%s:model_import_csv' % self.name, +# kwargs={'app': app_name, 'model': model_name, 'page': 1}), +# 'fields': form._fields, +# 'sample': form._head(), +# }) +# return render_to_response(self.template_step2 or [ +# "iadmin/%s/%s/import_csv_2.html" % (app_name, model_name.lower()), +# "iadmin/%s/import_csv_2.html" % app_name, +# "iadmin/import_csv_2.html" +# ], context) +# +# except IOError, e: +# messages.error(request, str(e)) +# return redirect('%s:model_import_csv' % self.name, app=app_name, model=model_name, page=1) +# +# def _step_3(self, request, app_name=None, model_name=None, temp_file_name=None, extra_context=None ): +# context = self._get_base_context(request, app_name, model_name) +# Model = get_model(app_name, model_name) +# extra_context = extra_context or {} +# if 'apply' in request.POST: +# Form = csv_processor_factory(app_name, model_name, temp_file_name) +# form = Form(request.POST, request.FILES) +# if form.is_valid(): +# mapping = self.__get_mapping(form) +# Model = get_model(app_name, model_name) +# with open_csv(temp_file_name) as csv: +# if form.cleaned_data['header']: +# csv.next() +# for i, row in enumerate(csv): +# record, key = self._process_row(row, mapping) +# try: +# if key: +# if form.cleaned_data['create_missing']: +# sample, _ = Model.objects.get_or_create(**key) +# else: +# sample = Model.objects.get(**key) +# else: +# sample = Model() +# sample = update_model(request, sample, record, mapping) +# sample.save() +# except (IntegrityError, ObjectDoesNotExist), e: +# messages.error(request, '%s: %s' % (str(e), row) ) +# return redirect('%s:%s_%s_changelist' % (self.name, app_name, model_name.lower())) +# else: +# pass +# context.update({'page': 3, +# 'fields': Model._meta.fields, +# 'back': reverse('%s:model_import_csv' % self.name, +# kwargs={'app': app_name, 'model': model_name, 'page': 2}), +# 'lbl_next': 'Apply', +# }) +# context.update(extra_context) +# return render_to_response(self.template_step3 or [ +# "iadmin/%s/%s/import_csv_3.html" % (app_name, model_name.lower()), +# "iadmin/%s/import_csv_3.html" % app_name, +# "iadmin/import_csv_3.html" +# ], context) +# +# +# def import_csv(self, request, page=1, app=None, model=None): +# temp_file_name = os.path.join(tempfile.gettempdir(), 'iadmin_import_%s_%s.temp~' % ( +# request.user.username, hash(request.user.password))) +# if int(page) == 1: +# return self._step_1(request, app, model, temp_file_name=temp_file_name) +# elif int(page) == 2: +# if not 'HTTP_REFERER' in request.META: +# return redirect('%s:model_import_csv' % self.name, app=app, model=model, page=1) +# # todo: check referer +# return self._step_2(request, app, model, temp_file_name=temp_file_name) +# elif int(page) == 3: +# return self._step_3(request, app, model, temp_file_name=temp_file_name) +# raise Exception(page) +# +# def get_urls(self): +# def wrap(view, cacheable=False): +# def wrapper(*args, **kwargs): +# return self.admin_site.admin_view(view, cacheable)(*args, **kwargs) +# +# return update_wrapper(wrapper, view) +# +# return patterns('', +# url(r'^import/1$', +# wrap(self.import_csv), +# name='import_csv'), +# +# url(r'^(?P\w+)/(?P\w+)/import/(?P\d)', +# wrap(self.import_csv), +# name='model_import_csv'), +# +# url(r'^(?P\w+)/import/(?P\d)', +# wrap(self.import_csv), +# name='app_import_csv'), +# ) \ No newline at end of file diff --git a/iadmin/actions/csv2/utils.py b/iadmin/actions/csv2/utils.py new file mode 100644 index 0000000..2bb1f9a --- /dev/null +++ b/iadmin/actions/csv2/utils.py @@ -0,0 +1,303 @@ +from _csv import Error +from contextlib import contextmanager +import csv +from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError +from django.db.models.fields.related import ForeignKey +from django.forms.fields import CharField, BooleanField +from django.db.models.loading import get_models, get_apps, get_app, get_model +from django.forms.fields import ChoiceField, FileField +from django.forms.forms import Form, DeclarativeFieldsMetaclass, BoundField +from django.forms.widgets import Input, HiddenInput, MultipleHiddenInput +from django.utils.encoding import force_unicode +from django.utils.html import conditional_escape +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +import re + +__author__ = 'sax' + +class CsvFileField(FileField): + def clean(self, data, initial=None): + if not data and initial: + return initial + ret = super(FileField, self).clean(data) + if ret: + try: + _dialect = csv.Sniffer().sniff(data.read(2048)) + data.seek(0) + csv_reader = csv.reader(data, dialect=_dialect) + for i in range(10): + csv_reader.next() + except Error, e: + raise ValidationError("Unable to load csv file (%s)" % e) + return ret + + +class ColumnField(ChoiceField): + def to_python(self, value): + "Returns a Unicode object." + return int(value) + + def valid_value(self, value): + return True + + +class RegexField(CharField): + def clean(self, value): + value = super(RegexField, self).clean(value) + if value: + try: + if not ('(' or '(') in value: + raise ValidationError + return re.compile(value) + except: + raise ValidationError(_("'%s' is not a valid regex pattern" % value)) + return value + + +class Lookup(dict): + """ + a dictionary which can lookup value by key, or keys by value + """ + + def __init__(self, items=None): + """items can be a list of pair_lists or a dictionary""" + dict.__init__(self, items or []) + + def get_key(self, value): + """find the key(s) as a list given a value""" + return [item[0] for item in self.items() if item[1] == value][0] + + def get_value(self, key): + """find the value given a key""" + return self[key] + + +def _get_all_models(filter_app_name=None): + all_models = [] + if filter_app_name: + apps = [get_app(filter_app_name)] + else: + apps = get_apps() + from django.contrib.admin import site + + for app in apps: + for mod in get_models(app): + if mod in site._registry: + all_models.append("%s:%s" % (mod._meta.app_label, force_unicode(mod._meta.verbose_name))) + return zip(all_models, all_models) + + +def get_valid_choice(value, choices): + for k, v in choices: + if str(v) == str(value): + return True, v + + return False, None + + +def update_model(request, original, updater, mapping): + for fname, v in updater.items(): + _u, _u, _u, lookup_name, Field = mapping[fname] + if isinstance(Field, ForeignKey): + if lookup_name: + try: + v = Field.rel.to.objects.get(**{lookup_name:v} ) + except ObjectDoesNotExist, e: + raise ObjectDoesNotExist('%s %s' % (e, v)) + setattr(original, fname, v) + return original + + +def set_model_attribute(instance, name, value, rex=None): + if value == 'None': + value = None + field, model, direct, m2m = instance._meta.get_field_by_name(name) + + if isinstance(field, ForeignKey): + m = re.compile(rex).match(value) + + elif hasattr(field, 'flatchoices'): + choices = Lookup(getattr(field, 'flatchoices')) + if value in choices.values(): + value = choices.get_key(value) + setattr(instance, name, value) + + +class ImportForm(Form): + model = ChoiceField() + csv = CsvFileField() + + def __init__(self, app, model, data=None, files=None, auto_id='id_%s', prefix=None, initial=None): + super(ImportForm, self).__init__(data, files, auto_id, prefix, initial) + if self.data: + app, model = self.data['model'].split(':') + + if model: + m = "%s:%s" % ( app, model) + self.fields['model'].choices = [(m, m)] + self.fields['model'].widget = Input({'readonly': 'readonly'}) + self.initial['model'] = m + elif app: + self.fields['model'].choices = _get_all_models(app) + else: + self.fields['model'].choices = _get_all_models() + + + + + +def csv_processor_factory(app_name, model_name, csv_filename): + """ + factory for Model specific CSVPRocessorForm + """ + rows = [] + fd = open(csv_filename, 'rb') + dialect = csv.Sniffer().sniff(fd.read(2048)) + fd.seek(0) + csv_reader = csv.reader(fd, dialect=dialect) + for i in range(10): + rows.append(csv_reader.next()) + fd.close() + columns_count = len(rows[0]) + + model = get_model(app_name, model_name) + + model_fields = [('', '-- ignore --')] + [(f.name, f.name) for f in model._meta.fields] + columns_def = [(-1, '-- ignore --')] + [(i, "Column %s" % i) for i in range(columns_count)] + + class_name = "%s%sImportForm" % (app_name, model_name) + attrs = { +# 'header': BooleanField(label='Header', initial=False, required=False), +# 'validate': BooleanField(label='Form validation', initial=False, required=False), +# 'preview_all': BooleanField(label='Preview all records', initial=False, required=False), +# 'create_missing': BooleanField(label='Create missing rows', initial=False, required=False), + 'columns_count': columns_count, + 'sample': rows, + '_model': model, + '_fields': model._meta.fields, + '_filename': csv_filename, + '_dialect': dialect + } + + for i, f in enumerate(model._meta.fields): + # column, field, regex to manipulate column value, lookup field name for foreign-keys, primary key flag + attrs['col_%s' % i] = ColumnField(choices=columns_def, required=False) + attrs['fld_%s' % i] = ChoiceField(choices=model_fields, required=False) + attrs['rex_%s' % i] = RegexField(label='', initial='(.*)', required=False) + attrs['lkf_%s' % i] = CharField(required=False) + attrs['key_%s' % i] = BooleanField(label='', initial=False, required=False) + + return DeclarativeFieldsMetaclass(str(class_name), (CSVPRocessorForm,), attrs) + + +@contextmanager +def open_csv(filename): + fd = open(filename, 'rb') + dialect = csv.Sniffer().sniff(fd.read(2048)) + fd.seek(0) + csv_reader = csv.reader(fd, dialect=dialect) + yield csv_reader + fd.close() + + +class CSVPRocessorForm(Form): + header = BooleanField(label='Header', initial=False, required=False) + validate = BooleanField(label='Form validation', initial=False, required=False) + preview_all = BooleanField(label='Preview all records', initial=False, required=False) + create_missing = BooleanField(label='Create missing rows', initial=False, required=False) + + def _head(self, rows=10): + with open_csv(self._filename) as csv: + output = [] + for i in range(rows): + output.append(csv.next()) + return output + + def clean(self): + found = False + # todo: we should try to create a dummy model to force some validation ?? + for i, f in enumerate(self._fields): + fld = 'fld_%s' % i + col = 'col_%s' % i + lkf = 'lkf_%s' % i + column = self.cleaned_data[col] + field_name = self.cleaned_data[fld] + lookup_name = self.cleaned_data[lkf] + if column>=0 or field_name: + found = True + if not ( column>=0 and field_name): + self._errors[fld] = self.error_class([_("Please set both 'column' and 'field'")]) + raise ValidationError("Please fix errors below") + Field,_u,_u,_u = self._model._meta.get_field_by_name(field_name) + if isinstance(Field, ForeignKey): + if not lookup_name: + self._errors[fld] = self.error_class([_('Please set lookup field name for "%s"') % field_name]) + else: + try: + Field.rel.to._meta.get_field_by_name(lookup_name) + except Exception, e: + self._errors[fld] = self.error_class([e]) + + + if not found: + raise ValidationError("Please set columns mapping") + return self.cleaned_data + + def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): + top_errors = self.non_field_errors() # Errors that should be displayed above all fields. + output, hidden_fields = [], [] + for name in ('header', 'preview_all', 'validate', 'create_missing'): + field = self.fields[name] + bf = BoundField(self, field, name) + bf_errors = self.error_class( + [conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. + if bf_errors: + top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) + output.append('%s%s' % (bf.label, unicode(bf))) + + output.append(u'%s%s%s%s%s' % ( + _('Column'), _('Field'), _('Regex'), _('Lookup Field'), _('pk'))) + + for i, f in enumerate(self._fields): + line = [] + error_line = [] + rowid = self.fields['col_%s' % i].label + for n in ('col_%s', 'fld_%s', 'rex_%s', 'lkf_%s', 'key_%s'): + name = n % i + field = self.fields[name] + bf = BoundField(self, field, name) + bf_errors = self.error_class( + [conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. + error_line.append(force_unicode(bf_errors), ) + line.append('%(field)s' % + {'field': unicode(bf), + 'class': n[:3]} + ) + output.append('%s' % ''.join(error_line)) + output.append('%(line)s' % {'line': ''.join(line), 'rowid': rowid}) + + if top_errors: + output.insert(0, error_row % force_unicode(top_errors)) + + return mark_safe(u'\n'.join(output)) + + def as_hidden(self): + output, hidden_fields = [], [] + for name, field in self.fields.items(): + field.widget = HiddenInput({'readonly': 'readonly'}) + bf = BoundField(self, field, name) + output.append(unicode(bf)) + return mark_safe(u'\n'.join(output)) + + def as_table(self): + "Returns this form rendered as HTML s -- excluding the
." + return self._html_output( + normal_row=u'%(label)s%(errors)s%(field)s%(help_text)s', + error_row=u'%s', + row_ender=u'', + help_text_html=u'
%s', + errors_on_separate_row=False) + diff --git a/iadmin/models.py b/iadmin/models.py index 23369b7..532f42b 100644 --- a/iadmin/models.py +++ b/iadmin/models.py @@ -12,12 +12,21 @@ def create_extra_permission(sender, **kwargs): from django.contrib.auth.models import Permission from django.db.models.loading import get_models from django.contrib.contenttypes.models import ContentType + for model in get_models(sender): for action in ('view', 'export', 'massupdate', 'import'): opts = model._meta codename = _get_permission_codename(action, opts) - label = u'Can %s %s' % (action, opts.verbose_name_raw) + label = u'Can %s %s' % (action, opts.verbose_name_raw) ct = ContentType.objects.get_for_model(model) Permission.objects.get_or_create(codename=codename, content_type=ct, defaults={'name': label}) + # restrictions + for model in get_models(sender): + opts = model._meta + codename = u'read_only_%s' % opts.object_name.lower() + label = u'Can only read %s' % opts.verbose_name_raw + ct = ContentType.objects.get_for_model(model) + Permission.objects.get_or_create(codename=codename, content_type=ct, defaults={'name': label}) + signals.post_syncdb.connect(create_extra_permission) diff --git a/iadmin/static/iadmin/img/changelist-filter-button-bg.jpg b/iadmin/static/iadmin/img/changelist-filter-button-bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..abe3d8fde0dba147d310c8540c04d0746f9a9a64 GIT binary patch literal 2614 zcmd5+do)S8d2C`bq-iiSJL;T0bxv*B{iFTd@45H;{eJHKe(&RZuCztk36xw` z{>E&074+JC?tf0Q153zs#BXstC1-i1Boh`Sr0JEI^AYiT|4{L<_=+dlVz*aWyhbo zfe#>Cf{-!##3?t7g&`o5cOW8E0wEZwIwSdPm$KO~O2@8HSK0>TWjuli6M-!d3utkg zvoKhw8hlZ~Xud{l?GA*dCs2Rvy6E8In3N`V9>v1(D&?cthdBbT!^XNt@O?Z1A1q`wB;Zu`g2aoYo-X{hkP2M_)k6)FjR%(^WM62;x{j-B1X>_ZH#oje-3 zEnTj7^T_hniVY^;kPaX9R1|_J4qRTP#RsownB8&KJm_iXC!Bqq%Y*~MfuaWl`}~vF zvc^oV7Z@(3hB^9CeJuQa+|E0*4Aq;l;*@hPNrVU^NiJRBuFpQD>%Lr3Ma1MA5$;A= zUl!Qj@1V5@jY^Eg24lZ-#~%i%wX|sXQ$30bvORm67nQ1xE4wr29a@;Gpo`T_QeQ)# z?|jNi;J%DZcVKjx4YyQSNt(Mjr!-e;pCDv6L~-ujPoYDdm^c#n^nhW;Qb>C#QjjYJ z9=&UKtul9X-4e`*U`MB4O}^7td?e7>V1Xl^#?YU8r#lgE7}K-QbRZ%&L#$9n8toMp zZF_o)@F&gMs{C1_2qa{ zMt)rp>)}Kp-OK37nASp{qnmAWgn`4^K9>fT23;aG#(MC%U2d1wKZv>PW24 z2&l|`ak6ap60z-#nNtbLQ~CbS>8@xs=L9T?*pVBr6|Cdrmq2dLRGqFtsVV1XQldEa zNkT07X&2s_r{lEyfc`65ycS<6tGrtE^VE#1(F;n#=l4==4*9KC^Y`$zFLX;UTchW` z*Q!PvDFt)H`BLDkZY5C(6)MyS3rqHOn+sM|I=6cSZ*FrLVEQ%so;?^5&^A9+3PRm% zmF%Q|b()@+6UJ#Ry;&q@GqJm5XJSv4HP27aD`~NTO4gAT`p?ODt4H=pitj}o((=+0ZtQ6GxNdqosLNb1Gqki`s3(%=%?;2Uda~B0r&5P`;BKvd z>C1V%w4Q~u40>XT|E7uKlLtdpOo@>%FR;SbA8V+lcAIqUNa4GVe8+Jsb9^7$!4mh4 zL$`!_)@$hZBLAHjhP8Ej>pA_L5U*9Bs~X8kAh&hvOv|KL#4JUb?}=~;xysnIm7i6| zXK2^;X~%nrW><(pS|g21nUM__{5QH~8Jh%qo_#|;vp%FUb$t|JXe3``uwSuN;>As* yN`YcZaqSo>n0nLqv?i8Z75^1o_;0yq1N7O646KhLQoqx_P%q$vkLIgYY3pAlA4Sao literal 0 HcmV?d00001 diff --git a/iadmin/templates/iadmin/ibase.html b/iadmin/templates/iadmin/ibase.html new file mode 100644 index 0000000..3b50adc --- /dev/null +++ b/iadmin/templates/iadmin/ibase.html @@ -0,0 +1,77 @@ +{% load admin_static %}{% load url from future %} + + +{% block title %}{% endblock %} + +{% block extrastyle %}{% endblock %} + +{% if LANGUAGE_BIDI %}{% endif %} + +{% block extrahead %}{% endblock %} +{% block blockbots %}{% endblock %} + +{% load i18n %} + + + + +
+ + {% if not is_popup %} + + + + {% block breadcrumbs %} + + {% endblock %} + {% endif %} + + {% block messages %} + {% if messages %} +
    {% for message in messages %} + {{ message }} + {% endfor %}
+ {% endif %} + {% endblock messages %} + + +
+ {% block pretitle %}{% endblock %} + {% block content_title %}{% if title %}

{{ title }}

{% endif %}{% endblock %} + {% block content %} + {% block object-tools %}{% endblock %} + {{ content }} + {% endblock %} + {% block sidebar %}{% endblock %} +
+
+ + + {% block footer %}{% endblock %} +
+ + + + diff --git a/project/manage.py b/project/manage.py new file mode 100755 index 0000000..5ebfd84 --- /dev/null +++ b/project/manage.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +import os, sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv)