This commit is contained in:
martin.bohacek 2012-06-05 13:29:33 +02:00
commit eee791e9b2
190 changed files with 3268 additions and 2317 deletions

View File

@ -93,7 +93,7 @@ answer newbie questions, and generally made Django that much better:
Simon Blanchard
David Blewett <david@dawninglight.net>
Matt Boersma <matt@sprout.org>
boobsd@gmail.com
Artem Gnilov <boobsd@gmail.com>
Matías Bordese
Nate Bragg <jonathan.bragg@alum.rpi.edu>
Sean Brant
@ -211,7 +211,6 @@ answer newbie questions, and generally made Django that much better:
Dimitris Glezos <dimitris@glezos.com>
glin@seznam.cz
martin.glueck@gmail.com
Artyom Gnilov <boobsd@gmail.com>
Ben Godfrey <http://aftnn.org>
GomoX <gomo@datafull.com>
Guilherme Mesquita Gondim <semente@taurinus.org>
@ -347,6 +346,7 @@ answer newbie questions, and generally made Django that much better:
Frantisek Malina <vizualbod@vizualbod.com>
Mike Malone <mjmalone@gmail.com>
Martin Maney <http://www.chipy.org/Martin_Maney>
Michael Manfre <mmanfre@gmail.com>
masonsimon+django@gmail.com
Manuzhai
Petr Marhoun <petr.marhoun@gmail.com>
@ -401,6 +401,7 @@ answer newbie questions, and generally made Django that much better:
Christian Oudard <christian.oudard@gmail.com>
oggie rob <oz.robharvey@gmail.com>
oggy <ognjen.maric@gmail.com>
Jens Page
Jay Parlar <parlar@gmail.com>
Carlos Eduardo de Paula <carlosedp@gmail.com>
John Paulett <john@paulett.org>

View File

@ -3,7 +3,7 @@
var options = $.extend({}, $.fn.actions.defaults, opts);
var actionCheckboxes = $(this);
var list_editable_changed = false;
checker = function(checked) {
var checker = function(checked) {
if (checked) {
showQuestion();
} else {
@ -11,7 +11,7 @@
}
$(actionCheckboxes).attr("checked", checked)
.parent().parent().toggleClass(options.selectedClass, checked);
}
},
updateCounter = function() {
var sel = $(actionCheckboxes).filter(":checked").length;
$(options.counterContainer).html(interpolate(
@ -29,30 +29,30 @@
}
return value;
});
}
},
showQuestion = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).show();
$(options.allContainer).hide();
}
},
showClear = function() {
$(options.acrossClears).show();
$(options.acrossQuestions).hide();
$(options.actionContainer).toggleClass(options.selectedClass);
$(options.allContainer).show();
$(options.counterContainer).hide();
}
},
reset = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).hide();
$(options.allContainer).hide();
$(options.counterContainer).show();
}
},
clearAcross = function() {
reset();
$(options.acrossInput).val(0);
$(options.actionContainer).removeClass(options.selectedClass);
}
};
// Show counter by default
$(options.counterContainer).show();
// Check state of checkboxes and reinit state if needed
@ -81,9 +81,9 @@
});
lastChecked = null;
$(actionCheckboxes).click(function(event) {
if (!event) { var event = window.event; }
if (!event) { event = window.event; }
var target = event.target ? event.target : event.srcElement;
if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) {
if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey === true) {
var inrange = false;
$(lastChecked).attr("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
@ -124,7 +124,7 @@
}
}
});
}
};
/* Setup plugin defaults */
$.fn.actions.defaults = {
actionContainer: "div.actions",
@ -135,5 +135,5 @@
acrossClears: "div.actions span.clear",
allToggle: "#action-toggle",
selectedClass: "selected"
}
};
})(django.jQuery);

View File

@ -1,7 +1,6 @@
(function(a){a.fn.actions=function(g){var b=a.extend({},a.fn.actions.defaults,g),f=a(this),e=!1;checker=function(c){c?showQuestion():reset();a(f).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)};updateCounter=function(){var c=a(f).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).attr("checked",function(){c==f.length?(value=!0,showQuestion()):(value=
!1,clearAcross());return value})};showQuestion=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()};showClear=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()};reset=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()};clearAcross=function(){reset();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};
a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);updateCounter();1==a(b.acrossInput).val()&&showClear()});a(b.allToggle).show().click(function(){checker(a(this).attr("checked"));updateCounter()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);showClear()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",!1);clearAcross();checker(0);
updateCounter()});lastChecked=null;a(f).click(function(c){if(!c)c=window.event;var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&!0==c.shiftKey){var e=!1;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))e=e?!1:!0;e&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,
d.checked);lastChecked=d;updateCounter()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var b=!1;a("div.actions select option:selected").each(function(){a(this).val()&&(b=!0)});
if(b)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",
acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);
(function(a){a.fn.actions=function(m){var b=a.extend({},a.fn.actions.defaults,m),f=a(this),e=!1,j=function(c){c?h():i();a(f).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},g=function(){var c=a(f).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).attr("checked",function(){c==f.length?(value=!0,h()):(value=!1,k());return value})},h=function(){a(b.acrossClears).hide();
a(b.acrossQuestions).show();a(b.allContainer).hide()},l=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},i=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},k=function(){i();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);
g();1==a(b.acrossInput).val()&&l()});a(b.allToggle).show().click(function(){j(a(this).attr("checked"));g()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);l()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",!1);k();j(0);g()});lastChecked=null;a(f).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&!0===c.shiftKey){var e=!1;a(lastChecked).attr("checked",
d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))e=e?false:true;e&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;g()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});
a('form#changelist-form input[name="_save"]').click(function(){var b=!1;a("div.actions select option:selected").each(function(){a(this).val()&&(b=!0)});if(b)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};
a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);

View File

@ -31,11 +31,11 @@
}
};
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
var nextIndex = parseInt(totalForms.val());
var nextIndex = parseInt(totalForms.val(), 10);
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
// only show the add button if we are allowed to add more items,
// note that max_num = None translates to a blank string.
var showAddButton = maxForms.val() == '' || (maxForms.val()-totalForms.val()) > 0;
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
$(this).each(function(i) {
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
});
@ -52,13 +52,14 @@
$(this).filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
addButton = $(this).filter(":last").next().find("a");
}
addButton.click(function() {
addButton.click(function(e) {
e.preventDefault();
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
var template = $("#" + options.prefix + "-empty");
var row = template.clone(true);
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
@ -78,14 +79,15 @@
// Insert the new form when it has been fully edited
row.insertBefore($(template));
// Update number of total forms
$(totalForms).val(parseInt(totalForms.val()) + 1);
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
nextIndex += 1;
// Hide add button in case we've hit the max, except we want to add infinitely
if ((maxForms.val() != '') && (maxForms.val()-totalForms.val()) <= 0) {
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
addButton.parent().hide();
}
// The delete button of each row triggers a bunch of other things
row.find("a." + options.deleteCssClass).click(function() {
row.find("a." + options.deleteCssClass).click(function(e) {
e.preventDefault();
// Remove the parent form containing this button:
var row = $(this).parents("." + options.formCssClass);
row.remove();
@ -98,7 +100,7 @@
var forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
// Show add button again once we drop below max
if ((maxForms.val() == '') || (maxForms.val()-forms.length) > 0) {
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
addButton.parent().show();
}
// Also, update names and ids for all remaining form controls
@ -110,17 +112,15 @@
updateElementIndex(this, options.prefix, i);
});
}
return false;
});
// If a post-add callback was supplied, call it with the added form:
if (options.added) {
options.added(row);
}
return false;
});
}
return this;
}
};
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: "form", // The form prefix for your django formset
@ -132,5 +132,5 @@
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
added: null, // Function called each time a new form is added
removed: null // Function called each time a form is deleted
}
};
})(django.jQuery);

View File

@ -1,5 +1,5 @@
(function(b){b.fn.formset=function(c){var a=b.extend({},b.fn.formset.defaults,c),j=function(a,e,d){var i=RegExp("("+e+"-(\\d+|__prefix__))"),e=e+"-"+d;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(i,e));if(a.id)a.id=a.id.replace(i,e);if(a.name)a.name=a.name.replace(i,e)},c=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),g=parseInt(c.val()),f=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),c=""==f.val()||0<f.val()-c.val();b(this).each(function(){b(this).not("."+
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&c){var h;"TR"==b(this).attr("tagName")?(c=this.eq(0).children().length,b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),h=b(this).parent().find("tr:last a")):(b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),h=b(this).filter(":last").next().find("a"));h.click(function(){var c=b("#id_"+a.prefix+
"-TOTAL_FORMS"),e=b("#"+a.prefix+"-empty"),d=e.clone(!0);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+g);d.is("tr")?d.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):d.is("ul")||d.is("ol")?d.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):d.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+
"</a></span>");d.find("*").each(function(){j(this,a.prefix,c.val())});d.insertBefore(b(e));b(c).val(parseInt(c.val())+1);g+=1;""!=f.val()&&0>=f.val()-c.val()&&h.parent().hide();d.find("a."+a.deleteCssClass).click(function(){var c=b(this).parents("."+a.formCssClass);c.remove();g-=1;a.removed&&a.removed(c);c=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(c.length);(""==f.val()||0<f.val()-c.length)&&h.parent().show();for(var d=0,e=c.length;d<e;d++)j(b(c).get(d),a.prefix,d),b(c.get(d)).find("*").each(function(){j(this,
a.prefix,d)});return!1});a.added&&a.added(d);return!1})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
(function(b){b.fn.formset=function(c){var a=b.extend({},b.fn.formset.defaults,c),j=function(a,f,d){var e=RegExp("("+f+"-(\\d+|__prefix__))"),f=f+"-"+d;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(e,f));a.id&&(a.id=a.id.replace(e,f));a.name&&(a.name=a.name.replace(e,f))},c=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),h=parseInt(c.val(),10),g=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),c=""===g.val()||0<g.val()-c.val();b(this).each(function(){b(this).not("."+
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&c){var i;"TR"==b(this).attr("tagName")?(c=this.eq(0).children().length,b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),i=b(this).parent().find("tr:last a")):(b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),i=b(this).filter(":last").next().find("a"));i.click(function(c){c.preventDefault();
var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),c=b("#"+a.prefix+"-empty"),d=c.clone(true);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+h);d.is("tr")?d.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):d.is("ul")||d.is("ol")?d.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):d.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
a.deleteText+"</a></span>");d.find("*").each(function(){j(this,a.prefix,f.val())});d.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);h=h+1;g.val()!==""&&g.val()-f.val()<=0&&i.parent().hide();d.find("a."+a.deleteCssClass).click(function(e){e.preventDefault();e=b(this).parents("."+a.formCssClass);e.remove();h=h-1;a.removed&&a.removed(e);e=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(e.length);(g.val()===""||g.val()-e.length>0)&&i.parent().show();for(var c=0,d=e.length;c<d;c++){j(b(e).get(c),
a.prefix,c);b(e.get(c)).find("*").each(function(){j(this,a.prefix,c)})}});a.added&&a.added(d)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);

View File

@ -15,8 +15,12 @@
{% if app_list %}
{% for app in app_list %}
<div class="module">
<table summary="{% blocktrans with name=app.name %}Models available in the {{ name }} application.{% endblocktrans %}">
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with name=app.name %}{{ name }}{% endblocktrans %}</a></caption>
<table>
<caption>
<a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">
{% blocktrans with name=app.name %}{{ name }}{% endblocktrans %}
</a>
</caption>
{% for model in app.models %}
<tr>
{% if model.admin_url %}

View File

@ -1,4 +1,5 @@
import operator
from functools import reduce
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
from django.core.paginator import InvalidPage

View File

@ -80,7 +80,7 @@ class Command(BaseCommand):
if default_username and username == '':
username = default_username
if not RE_VALID_USERNAME.match(username):
sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
self.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.")
username = None
continue
try:
@ -88,7 +88,7 @@ class Command(BaseCommand):
except User.DoesNotExist:
break
else:
sys.stderr.write("Error: That username is already taken.\n")
self.stderr.write("Error: That username is already taken.")
username = None
# Get an email
@ -98,7 +98,7 @@ class Command(BaseCommand):
try:
is_valid_email(email)
except exceptions.ValidationError:
sys.stderr.write("Error: That e-mail address is invalid.\n")
self.stderr.write("Error: That e-mail address is invalid.")
email = None
else:
break
@ -109,19 +109,19 @@ class Command(BaseCommand):
password = getpass.getpass()
password2 = getpass.getpass('Password (again): ')
if password != password2:
sys.stderr.write("Error: Your passwords didn't match.\n")
self.stderr.write("Error: Your passwords didn't match.")
password = None
continue
if password.strip() == '':
sys.stderr.write("Error: Blank passwords aren't allowed.\n")
self.stderr.write("Error: Blank passwords aren't allowed.")
password = None
continue
break
except KeyboardInterrupt:
sys.stderr.write("\nOperation cancelled.\n")
self.stderr.write("\nOperation cancelled.")
sys.exit(1)
User.objects.db_manager(database).create_superuser(username, email, password)
if verbosity >= 1:
self.stdout.write("Superuser created successfully.\n")
self.stdout.write("Superuser created successfully.")

View File

@ -2,6 +2,7 @@ from StringIO import StringIO
from django.contrib.auth import models, management
from django.contrib.auth.management.commands import changepassword
from django.core.management.base import CommandError
from django.test import TestCase
@ -56,16 +57,10 @@ class ChangepasswordManagementCommandTestCase(TestCase):
def test_that_max_tries_exits_1(self):
"""
A CommandError should be thrown by handle() if the user enters in
mismatched passwords three times. This should be caught by execute() and
converted to a SystemExit
mismatched passwords three times.
"""
command = changepassword.Command()
command._get_pass = lambda *args: args or 'foo'
self.assertRaises(
SystemExit,
command.execute,
"joe",
stdout=self.stdout,
stderr=self.stderr
)
with self.assertRaises(CommandError):
command.execute("joe", stdout=self.stdout, stderr=self.stderr)

View File

@ -0,0 +1,11 @@
[
{
"pk": 1,
"model": "sites.site",
"fields": {
"domain": "example.com",
"name": "example.com"
}
}
]

View File

@ -18,9 +18,10 @@ from django.test.utils import override_settings
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
SITE_ID=1,
)
class FlatpageCSRFTests(TestCase):
fixtures = ['sample_flatpages']
fixtures = ['sample_flatpages', 'example_site']
urls = 'django.contrib.flatpages.tests.urls'
def setUp(self):

View File

@ -5,7 +5,10 @@ from django.test import TestCase
from django.test.utils import override_settings
from django.utils import translation
@override_settings(SITE_ID=1)
class FlatpageAdminFormTests(TestCase):
fixtures = ['example_site']
def setUp(self):
self.form_data = {
'title': "A test page",
@ -89,5 +92,5 @@ class FlatpageAdminFormTests(TestCase):
self.assertEqual(
f.errors,
{'sites': [u'This field is required.']})
{'sites': [translation.ugettext(u'This field is required.')]})

View File

@ -19,9 +19,10 @@ from django.test.utils import override_settings
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
SITE_ID=1,
)
class FlatpageMiddlewareTests(TestCase):
fixtures = ['sample_flatpages']
fixtures = ['sample_flatpages', 'example_site']
urls = 'django.contrib.flatpages.tests.urls'
def test_view_flatpage(self):
@ -75,7 +76,7 @@ class FlatpageMiddlewareTests(TestCase):
enable_comments=False,
registration_required=False,
)
fp.sites.add(1)
fp.sites.add(settings.SITE_ID)
response = self.client.get('/some.very_special~chars-here/')
self.assertEqual(response.status_code, 200)
@ -96,9 +97,10 @@ class FlatpageMiddlewareTests(TestCase):
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
SITE_ID=1,
)
class FlatpageMiddlewareAppendSlashTests(TestCase):
fixtures = ['sample_flatpages']
fixtures = ['sample_flatpages', 'example_site']
urls = 'django.contrib.flatpages.tests.urls'
def test_redirect_view_flatpage(self):
@ -130,7 +132,7 @@ class FlatpageMiddlewareAppendSlashTests(TestCase):
enable_comments=False,
registration_required=False,
)
fp.sites.add(1)
fp.sites.add(settings.SITE_ID)
response = self.client.get('/some.very_special~chars-here')
self.assertRedirects(response, '/some.very_special~chars-here/', status_code=301)
@ -144,7 +146,7 @@ class FlatpageMiddlewareAppendSlashTests(TestCase):
enable_comments=False,
registration_required=False,
)
fp.sites.add(1)
fp.sites.add(settings.SITE_ID)
response = self.client.get('/')
self.assertEqual(response.status_code, 200)

View File

@ -18,6 +18,7 @@ from django.test.utils import override_settings
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
SITE_ID=1,
)
class FlatpageTemplateTagTests(TestCase):
fixtures = ['sample_flatpages']

View File

@ -19,9 +19,10 @@ from django.test.utils import override_settings
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
SITE_ID=1,
)
class FlatpageViewTests(TestCase):
fixtures = ['sample_flatpages']
fixtures = ['sample_flatpages', 'example_site']
urls = 'django.contrib.flatpages.tests.urls'
def test_view_flatpage(self):
@ -85,9 +86,10 @@ class FlatpageViewTests(TestCase):
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
SITE_ID=1,
)
class FlatpageViewAppendSlashTests(TestCase):
fixtures = ['sample_flatpages']
fixtures = ['sample_flatpages', 'example_site']
urls = 'django.contrib.flatpages.tests.urls'
def test_redirect_view_flatpage(self):
@ -119,7 +121,7 @@ class FlatpageViewAppendSlashTests(TestCase):
enable_comments=False,
registration_required=False,
)
fp.sites.add(1)
fp.sites.add(settings.SITE_ID)
response = self.client.get('/flatpage_root/some.very_special~chars-here')
self.assertRedirects(response, '/flatpage_root/some.very_special~chars-here/', status_code=301)

View File

@ -156,9 +156,6 @@ class PreviewTests(TestCase):
class FormHmacTests(unittest.TestCase):
"""
Same as SecurityHashTests, but with form_hmac
"""
def test_textfield_hash(self):
"""
@ -166,8 +163,8 @@ class FormHmacTests(unittest.TestCase):
leading/trailing whitespace so as to be friendly to broken browsers that
submit it (usually in textareas).
"""
f1 = HashTestForm({'name': 'joe', 'bio': 'Nothing notable.'})
f2 = HashTestForm({'name': ' joe', 'bio': 'Nothing notable. '})
f1 = HashTestForm({'name': u'joe', 'bio': u'Nothing notable.'})
f2 = HashTestForm({'name': u' joe', 'bio': u'Nothing notable. '})
hash1 = utils.form_hmac(f1)
hash2 = utils.form_hmac(f2)
self.assertEqual(hash1, hash2)
@ -269,10 +266,10 @@ class WizardTests(TestCase):
Form should advance if the hash is present and good, as calculated using
current method.
"""
data = {"0-field": "test",
"1-field": "test2",
"hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
"wizard_step": "1"}
data = {"0-field": u"test",
"1-field": u"test2",
"hash_0": u"cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca",
"wizard_step": u"1"}
response = self.client.post('/wizard1/', data)
self.assertEqual(2, response.context['step0'])
@ -294,18 +291,18 @@ class WizardTests(TestCase):
reached[0] = True
wizard = WizardWithProcessStep([WizardPageOneForm])
data = {"0-field": "test",
"1-field": "test2",
"hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
"wizard_step": "1"}
data = {"0-field": u"test",
"1-field": u"test2",
"hash_0": u"cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca",
"wizard_step": u"1"}
wizard(DummyRequest(POST=data))
self.assertTrue(reached[0])
data = {"0-field": "test",
"1-field": "test2",
"hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
"hash_1": "d5b434e3934cc92fee4bd2964c4ebc06f81d362d",
"wizard_step": "2"}
data = {"0-field": u"test",
"1-field": u"test2",
"hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca",
"hash_1": u"1e6f6315da42e62f33a30640ec7e007ad3fbf1a1",
"wizard_step": u"2"}
self.assertRaises(http.Http404, wizard, DummyRequest(POST=data))
def test_14498(self):
@ -324,10 +321,10 @@ class WizardTests(TestCase):
wizard = WizardWithProcessStep([WizardPageOneForm,
WizardPageTwoForm,
WizardPageThreeForm])
data = {"0-field": "test",
"1-field": "test2",
"hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
"wizard_step": "1"}
data = {"0-field": u"test",
"1-field": u"test2",
"hash_0": u"cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca",
"wizard_step": u"1"}
wizard(DummyRequest(POST=data))
self.assertTrue(reached[0])
@ -348,10 +345,10 @@ class WizardTests(TestCase):
wizard = Wizard([WizardPageOneForm,
WizardPageTwoForm])
data = {"0-field": "test",
"1-field": "test2",
"hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
"wizard_step": "1"}
data = {"0-field": u"test",
"1-field": u"test2",
"hash_0": u"cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca",
"wizard_step": u"1"}
wizard(DummyRequest(POST=data))
self.assertTrue(reached[0])
@ -374,10 +371,10 @@ class WizardTests(TestCase):
wizard = WizardWithProcessStep([WizardPageOneForm,
WizardPageTwoForm,
WizardPageThreeForm])
data = {"0-field": "test",
"1-field": "test2",
"hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
"wizard_step": "1"}
data = {"0-field": u"test",
"1-field": u"test2",
"hash_0": u"cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca",
"wizard_step": u"1"}
wizard(DummyRequest(POST=data))
self.assertTrue(reached[0])

View File

@ -120,7 +120,7 @@ class NamedWizardTests(object):
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(
reverse(self.wizard_urlname,
kwargs={'step': response.context['wizard']['steps'].current}),
@ -147,7 +147,7 @@ class NamedWizardTests(object):
self.assertEqual(response.status_code, 200)
all_data = response.context['form_list']
self.assertEqual(all_data[1]['file1'].read(), open(__file__).read())
self.assertEqual(all_data[1]['file1'].read(), open(__file__, 'rb').read())
del all_data[1]['file1']
self.assertEqual(all_data, [
{'name': u'Pony', 'thirsty': True, 'user': self.testuser},
@ -168,7 +168,7 @@ class NamedWizardTests(object):
self.assertEqual(response.status_code, 200)
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(
reverse(self.wizard_urlname,
kwargs={'step': response.context['wizard']['steps'].current}),
@ -180,7 +180,9 @@ class NamedWizardTests(object):
response = self.client.get(step2_url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
self.assertEqual(response.context['wizard']['form'].files['form2-file1'].read(), open(__file__).read())
self.assertEqual(
response.context['wizard']['form'].files['form2-file1'].read(),
open(__file__, 'rb').read())
response = self.client.post(
reverse(self.wizard_urlname,
@ -197,7 +199,7 @@ class NamedWizardTests(object):
self.assertEqual(response.status_code, 200)
all_data = response.context['all_cleaned_data']
self.assertEqual(all_data['file1'].read(), open(__file__).read())
self.assertEqual(all_data['file1'].read(), open(__file__, 'rb').read())
del all_data['file1']
self.assertEqual(
all_data,
@ -221,7 +223,7 @@ class NamedWizardTests(object):
self.assertEqual(response.status_code, 200)
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(
reverse(self.wizard_urlname,
kwargs={'step': response.context['wizard']['steps'].current}),

View File

@ -80,7 +80,7 @@ class WizardTests(object):
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(self.wizard_url, post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['wizard']['steps'].current, 'form3')
@ -93,7 +93,7 @@ class WizardTests(object):
self.assertEqual(response.status_code, 200)
all_data = response.context['form_list']
self.assertEqual(all_data[1]['file1'].read(), open(__file__).read())
self.assertEqual(all_data[1]['file1'].read(), open(__file__, 'rb').read())
del all_data[1]['file1']
self.assertEqual(all_data, [
{'name': u'Pony', 'thirsty': True, 'user': self.testuser},
@ -110,7 +110,7 @@ class WizardTests(object):
self.assertEqual(response.status_code, 200)
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(self.wizard_url, post_data)
self.assertEqual(response.status_code, 200)
@ -121,7 +121,7 @@ class WizardTests(object):
self.assertEqual(response.status_code, 200)
all_data = response.context['all_cleaned_data']
self.assertEqual(all_data['file1'].read(), open(__file__).read())
self.assertEqual(all_data['file1'].read(), open(__file__, 'rb').read())
del all_data['file1']
self.assertEqual(all_data, {
'name': u'Pony', 'thirsty': True, 'user': self.testuser,
@ -138,7 +138,7 @@ class WizardTests(object):
self.assertEqual(response.status_code, 200)
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(self.wizard_url, post_data)
self.assertEqual(response.status_code, 200)
@ -165,7 +165,7 @@ class WizardTests(object):
self.assertEqual(response.context['wizard']['steps'].current, 'form2')
post_data = self.wizard_step_data[1]
post_data['form2-file1'] = open(__file__)
post_data['form2-file1'] = open(__file__, 'rb')
response = self.client.post(self.wizard_url, post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['wizard']['steps'].current, 'form3')

View File

@ -1,7 +1,5 @@
try:
import cPickle as pickle
except ImportError:
import pickle
# Do not try cPickle here (see #18340)
import pickle
from django.utils.crypto import salted_hmac

View File

@ -36,6 +36,7 @@ class GeoModelAdmin(ModelAdmin):
wms_url = 'http://vmap0.tiles.osgeo.org/wms/vmap0'
wms_layer = 'basic'
wms_name = 'OpenLayers WMS'
wms_options = {'format': 'image/jpeg'}
debug = False
widget = OpenLayersWidget
@ -76,6 +77,12 @@ class GeoModelAdmin(ModelAdmin):
class OLMap(self.widget):
template = self.map_template
geom_type = db_field.geom_type
wms_options = ''
if self.wms_options:
wms_options = ["%s: '%s'" % pair for pair in self.wms_options.items()]
wms_options = ', %s' % ', '.join(wms_options)
params = {'default_lon' : self.default_lon,
'default_lat' : self.default_lat,
'default_zoom' : self.default_zoom,
@ -106,6 +113,7 @@ class GeoModelAdmin(ModelAdmin):
'wms_url' : self.wms_url,
'wms_layer' : self.wms_layer,
'wms_name' : self.wms_name,
'wms_options' : wms_options,
'debug' : self.debug,
}
return OLMap

View File

@ -1,5 +1,5 @@
from ctypes import c_void_p
from types import NoneType
from django.contrib.gis.gdal.error import GDALException
class GDALBase(object):
@ -26,7 +26,7 @@ class GDALBase(object):
# compatible type or None (NULL).
if isinstance(ptr, (int, long)):
self._ptr = self.ptr_type(ptr)
elif isinstance(ptr, (self.ptr_type, NoneType)):
elif ptr is None or isinstance(ptr, self.ptr_type):
self._ptr = ptr
else:
raise TypeError('Incompatible pointer type')

View File

@ -2,10 +2,12 @@
Module for executing all of the GDAL tests. None
of these tests require the use of the database.
"""
from __future__ import absolute_import
from django.utils.unittest import TestSuite, TextTestRunner
# Importing the GDAL test modules.
import test_driver, test_ds, test_envelope, test_geom, test_srs
from . import test_driver, test_ds, test_envelope, test_geom, test_srs
test_suites = [test_driver.suite(),
test_ds.suite(),

View File

@ -11,8 +11,10 @@
Grab GeoIP.dat.gz and GeoLiteCity.dat.gz, and unzip them in the directory
corresponding to settings.GEOIP_PATH.
"""
from __future__ import absolute_import
try:
from django.contrib.gis.geoip.base import GeoIP, GeoIPException
from .base import GeoIP, GeoIPException
HAS_GEOIP = True
except:
HAS_GEOIP = False

View File

@ -1,5 +1,5 @@
from ctypes import c_void_p
from types import NoneType
from django.contrib.gis.geos.error import GEOSException
# Trying to import GDAL libraries, if available. Have to place in
@ -41,7 +41,7 @@ class GEOSBase(object):
def _set_ptr(self, ptr):
# Only allow the pointer to be set with pointers of the
# compatible type or None (NULL).
if isinstance(ptr, (self.ptr_type, NoneType)):
if ptr is None or isinstance(ptr, self.ptr_type):
self._ptr = ptr
else:
raise TypeError('Incompatible pointer type')

View File

@ -8,6 +8,9 @@ See also http://www.aryehleib.com/MutableLists.html
Author: Aryeh Leib Taurog.
"""
from django.utils.functional import total_ordering
@total_ordering
class ListMixin(object):
"""
A base class which provides complete list interface.
@ -143,20 +146,28 @@ class ListMixin(object):
self.extend(cache)
return self
def __cmp__(self, other):
'cmp'
def __eq__(self, other):
for i in range(len(self)):
try:
c = self[i] == other[i]
except IndexError:
# must be other is shorter
return False
if not c:
return False
return True
def __lt__(self, other):
slen = len(self)
for i in range(slen):
try:
c = cmp(self[i], other[i])
c = self[i] < other[i]
except IndexError:
# must be other is shorter
return 1
else:
# elements not equal
if c: return c
return cmp(slen, len(other))
return False
if c:
return c
return slen < len(other)
### Public list interface Methods ###
## Non-mutating ##

View File

@ -1,8 +1,10 @@
"""
GEOS Testing module.
"""
from __future__ import absolute_import
from django.utils.unittest import TestSuite, TextTestRunner
import test_geos, test_io, test_geos_mutation, test_mutable_list
from . import test_geos, test_io, test_geos_mutation, test_mutable_list
test_suites = [
test_geos.suite(),

View File

@ -1,18 +1,23 @@
import ctypes
import random
import unittest
from django.contrib.gis.geos import *
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
LineString, MultiLineString, fromfile, fromstr, geos_version_info)
from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
from django.contrib.gis.geos.libgeos import GEOS_PREPARE
from django.contrib.gis.geometry.test_data import TestDataMixin
from django.utils import unittest
class GEOSTest(unittest.TestCase, TestDataMixin):
@property
def null_srid(self):
"""
Returns the proper null SRID depending on the GEOS version.
See the comments in `test15_srid` for more details.
See the comments in `test_srid` for more details.
"""
info = geos_version_info()
if info['version'] == '3.0.0' and info['release_candidate']:
@ -20,7 +25,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
else:
return None
def test00_base(self):
def test_base(self):
"Tests out the GEOSBase class."
# Testing out GEOSBase class, which provides a `ptr` property
# that abstracts out access to underlying C pointers.
@ -62,19 +67,19 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertRaises(TypeError, fg1._set_ptr, bad_ptr)
self.assertRaises(TypeError, fg2._set_ptr, bad_ptr)
def test01a_wkt(self):
def test_wkt(self):
"Testing WKT output."
for g in self.geometries.wkt_out:
geom = fromstr(g.wkt)
self.assertEqual(g.ewkt, geom.wkt)
def test01b_hex(self):
def test_hex(self):
"Testing HEX output."
for g in self.geometries.hex_wkt:
geom = fromstr(g.wkt)
self.assertEqual(g.hex, geom.hex)
def test01b_hexewkb(self):
def test_hexewkb(self):
"Testing (HEX)EWKB output."
from binascii import a2b_hex
@ -124,14 +129,14 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
# Redundant sanity check.
self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid)
def test01c_kml(self):
def test_kml(self):
"Testing KML output."
for tg in self.geometries.wkt_out:
geom = fromstr(tg.wkt)
kml = getattr(tg, 'kml', False)
if kml: self.assertEqual(kml, geom.kml)
def test01d_errors(self):
def test_errors(self):
"Testing the Error handlers."
# string-based
print("\nBEGIN - expecting GEOS_ERROR; safe to ignore.\n")
@ -154,7 +159,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
# None
self.assertRaises(TypeError, GEOSGeometry, None)
def test01e_wkb(self):
def test_wkb(self):
"Testing WKB output."
from binascii import b2a_hex
for g in self.geometries.hex_wkt:
@ -162,7 +167,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
wkb = geom.wkb
self.assertEqual(b2a_hex(wkb).upper(), g.hex)
def test01f_create_hex(self):
def test_create_hex(self):
"Testing creation from HEX."
for g in self.geometries.hex_wkt:
geom_h = GEOSGeometry(g.hex)
@ -170,7 +175,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
geom_t = fromstr(g.wkt)
self.assertEqual(geom_t.wkt, geom_h.wkt)
def test01g_create_wkb(self):
def test_create_wkb(self):
"Testing creation from WKB."
from binascii import a2b_hex
for g in self.geometries.hex_wkt:
@ -180,7 +185,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
geom_t = fromstr(g.wkt)
self.assertEqual(geom_t.wkt, geom_h.wkt)
def test01h_ewkt(self):
def test_ewkt(self):
"Testing EWKT."
srids = (-1, 32140)
for srid in srids:
@ -191,9 +196,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(srid, poly.shell.srid)
self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export
def test01i_json(self):
@unittest.skipUnless(gdal.HAS_GDAL and gdal.GEOJSON, "gdal >= 1.5 is required")
def test_json(self):
"Testing GeoJSON input/output (via GDAL)."
if not gdal or not gdal.GEOJSON: return
for g in self.geometries.json_geoms:
geom = GEOSGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
@ -201,7 +206,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(g.json, geom.geojson)
self.assertEqual(GEOSGeometry(g.wkt), GEOSGeometry(geom.json))
def test01k_fromfile(self):
def test_fromfile(self):
"Testing the fromfile() factory."
from io import BytesIO
ref_pnt = GEOSGeometry('POINT(5 23)')
@ -218,7 +223,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
pnt = fromfile(fh)
self.assertEqual(ref_pnt, pnt)
def test01k_eq(self):
def test_eq(self):
"Testing equivalence."
p = fromstr('POINT(5 23)')
self.assertEqual(p, p.wkt)
@ -233,7 +238,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertNotEqual(g, {'foo' : 'bar'})
self.assertNotEqual(g, False)
def test02a_points(self):
def test_points(self):
"Testing Point objects."
prev = fromstr('POINT(0 0)')
for p in self.geometries.points:
@ -288,7 +293,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
prev = pnt # setting the previous geometry
def test02b_multipoints(self):
def test_multipoints(self):
"Testing MultiPoint objects."
for mp in self.geometries.multipoints:
mpnt = fromstr(mp.wkt)
@ -307,7 +312,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(p.empty, False)
self.assertEqual(p.valid, True)
def test03a_linestring(self):
def test_linestring(self):
"Testing LineString objects."
prev = fromstr('POINT(0 0)')
for l in self.geometries.linestrings:
@ -333,7 +338,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(ls.wkt, LineString(*tuple(Point(tup) for tup in ls.tuple)).wkt) # Point individual arguments
if numpy: self.assertEqual(ls, LineString(numpy.array(ls.tuple))) # as numpy array
def test03b_multilinestring(self):
def test_multilinestring(self):
"Testing MultiLineString objects."
prev = fromstr('POINT(0 0)')
for l in self.geometries.multilinestrings:
@ -357,7 +362,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(ml.wkt, MultiLineString(*tuple(s.clone() for s in ml)).wkt)
self.assertEqual(ml, MultiLineString(*tuple(LineString(s.tuple) for s in ml)))
def test04_linearring(self):
def test_linearring(self):
"Testing LinearRing objects."
for rr in self.geometries.linearrings:
lr = fromstr(rr.wkt)
@ -373,14 +378,15 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(lr, LinearRing([list(tup) for tup in lr.tuple]))
if numpy: self.assertEqual(lr, LinearRing(numpy.array(lr.tuple)))
def test05a_polygons(self):
"Testing Polygon objects."
# Testing `from_bbox` class method
def test_polygons_from_bbox(self):
"Testing `from_bbox` class method."
bbox = (-180, -90, 180, 90)
p = Polygon.from_bbox( bbox )
p = Polygon.from_bbox(bbox)
self.assertEqual(bbox, p.extent)
def test_polygons(self):
"Testing Polygon objects."
prev = fromstr('POINT(0 0)')
for p in self.geometries.polygons:
# Creating the Polygon, testing its properties.
@ -437,7 +443,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(poly.wkt, Polygon(*tuple(r for r in poly)).wkt)
self.assertEqual(poly.wkt, Polygon(*tuple(LinearRing(r.tuple) for r in poly)).wkt)
def test05b_multipolygons(self):
def test_multipolygons(self):
"Testing MultiPolygon objects."
print("\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n")
prev = fromstr('POINT (0 0)')
@ -460,7 +466,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
print("\nEND - expecting GEOS_NOTICE; safe to ignore.\n")
def test06a_memory_hijinks(self):
def test_memory_hijinks(self):
"Testing Geometry __del__() on rings and polygons."
#### Memory issues with rings and polygons
@ -483,7 +489,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
# Access to these rings is OK since they are clones.
s1, s2 = str(ring1), str(ring2)
def test08_coord_seq(self):
def test_coord_seq(self):
"Testing Coordinate Sequence objects."
for p in self.geometries.polygons:
if p.ext_ring_cs:
@ -510,7 +516,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
cs[i] = tset
self.assertEqual(tset[j], cs[i][j])
def test09_relate_pattern(self):
def test_relate_pattern(self):
"Testing relate() and relate_pattern()."
g = fromstr('POINT (0 0)')
self.assertRaises(GEOSException, g.relate_pattern, 0, 'invalid pattern, yo')
@ -520,7 +526,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(rg.result, a.relate_pattern(b, rg.pattern))
self.assertEqual(rg.pattern, a.relate(b))
def test10_intersection(self):
def test_intersection(self):
"Testing intersects() and intersection()."
for i in xrange(len(self.geometries.topology_geoms)):
a = fromstr(self.geometries.topology_geoms[i].wkt_a)
@ -533,7 +539,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
a &= b # testing __iand__
self.assertEqual(i1, a)
def test11_union(self):
def test_union(self):
"Testing union()."
for i in xrange(len(self.geometries.topology_geoms)):
a = fromstr(self.geometries.topology_geoms[i].wkt_a)
@ -545,7 +551,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
a |= b # testing __ior__
self.assertEqual(u1, a)
def test12_difference(self):
def test_difference(self):
"Testing difference()."
for i in xrange(len(self.geometries.topology_geoms)):
a = fromstr(self.geometries.topology_geoms[i].wkt_a)
@ -557,7 +563,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
a -= b # testing __isub__
self.assertEqual(d1, a)
def test13_symdifference(self):
def test_symdifference(self):
"Testing sym_difference()."
for i in xrange(len(self.geometries.topology_geoms)):
a = fromstr(self.geometries.topology_geoms[i].wkt_a)
@ -569,7 +575,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
a ^= b # testing __ixor__
self.assertEqual(d1, a)
def test14_buffer(self):
def test_buffer(self):
"Testing buffer()."
for bg in self.geometries.buffer_geoms:
g = fromstr(bg.wkt)
@ -597,7 +603,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertAlmostEqual(exp_ring[k][0], buf_ring[k][0], 9)
self.assertAlmostEqual(exp_ring[k][1], buf_ring[k][1], 9)
def test15_srid(self):
def test_srid(self):
"Testing the SRID property and keyword."
# Testing SRID keyword on Point
pnt = Point(5, 23, srid=4326)
@ -635,7 +641,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
p3 = fromstr(p1.hex, srid=-1) # -1 is intended.
self.assertEqual(-1, p3.srid)
def test16_mutable_geometries(self):
def test_mutable_geometries(self):
"Testing the mutability of Polygons and Geometry Collections."
### Testing the mutability of Polygons ###
for p in self.geometries.polygons:
@ -699,7 +705,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
#self.assertEqual((3.14, 2.71), mpoly[0].shell[0])
#del mpoly
def test17_threed(self):
def test_threed(self):
"Testing three-dimensional geometries."
# Testing a 3D Point
pnt = Point(2, 3, 8)
@ -715,7 +721,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
ls[0] = (1.,2.,3.)
self.assertEqual((1.,2.,3.), ls[0])
def test18_distance(self):
def test_distance(self):
"Testing the distance() function."
# Distance to self should be 0.
pnt = Point(0, 0)
@ -733,7 +739,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
ls2 = LineString((5, 2), (6, 1), (7, 0))
self.assertEqual(3, ls1.distance(ls2))
def test19_length(self):
def test_length(self):
"Testing the length property."
# Points have 0 length.
pnt = Point(0, 0)
@ -751,7 +757,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
mpoly = MultiPolygon(poly.clone(), poly)
self.assertEqual(8.0, mpoly.length)
def test20a_emptyCollections(self):
def test_emptyCollections(self):
"Testing empty geometries and collections."
gc1 = GeometryCollection([])
gc2 = fromstr('GEOMETRYCOLLECTION EMPTY')
@ -789,7 +795,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
else:
self.assertRaises(GEOSIndexError, g.__getitem__, 0)
def test20b_collections_of_collections(self):
def test_collections_of_collections(self):
"Testing GeometryCollection handling of other collections."
# Creating a GeometryCollection WKT string composed of other
# collections and polygons.
@ -808,9 +814,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
# And, they should be equal.
self.assertEqual(gc1, gc2)
def test21_test_gdal(self):
@unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
def test_gdal(self):
"Testing `ogr` and `srs` properties."
if not gdal.HAS_GDAL: return
g1 = fromstr('POINT(5 23)')
self.assertEqual(True, isinstance(g1.ogr, gdal.OGRGeometry))
self.assertEqual(g1.srs, None)
@ -821,7 +827,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(g2.hex, g2.ogr.hex)
self.assertEqual('WGS 84', g2.srs.name)
def test22_copy(self):
def test_copy(self):
"Testing use with the Python `copy` module."
import copy
poly = GEOSGeometry('POLYGON((0 0, 0 23, 23 23, 23 0, 0 0), (5 5, 5 10, 10 10, 10 5, 5 5))')
@ -830,9 +836,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertNotEqual(poly._ptr, cpy1._ptr)
self.assertNotEqual(poly._ptr, cpy2._ptr)
def test23_transform(self):
@unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
def test_transform(self):
"Testing `transform` method."
if not gdal.HAS_GDAL: return
orig = GEOSGeometry('POINT (-104.609 38.255)', 4326)
trans = GEOSGeometry('POINT (992385.4472045 481455.4944650)', 2774)
@ -855,7 +861,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertAlmostEqual(trans.x, p.x, prec)
self.assertAlmostEqual(trans.y, p.y, prec)
def test23_transform_noop(self):
def test_transform_noop(self):
""" Testing `transform` method (SRID match) """
# transform() should no-op if source & dest SRIDs match,
# regardless of whether GDAL is available.
@ -890,7 +896,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
finally:
gdal.HAS_GDAL = old_has_gdal
def test23_transform_nosrid(self):
def test_transform_nosrid(self):
""" Testing `transform` method (no SRID or negative SRID) """
g = GEOSGeometry('POINT (-104.609 38.255)', srid=None)
@ -905,7 +911,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
g = GEOSGeometry('POINT (-104.609 38.255)', srid=-1)
self.assertRaises(GEOSException, g.transform, 2774, clone=True)
def test23_transform_nogdal(self):
def test_transform_nogdal(self):
""" Testing `transform` method (GDAL not available) """
old_has_gdal = gdal.HAS_GDAL
try:
@ -919,7 +925,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
finally:
gdal.HAS_GDAL = old_has_gdal
def test24_extent(self):
def test_extent(self):
"Testing `extent` method."
# The xmin, ymin, xmax, ymax of the MultiPoint should be returned.
mp = MultiPoint(Point(5, 23), Point(0, 0), Point(10, 50))
@ -935,7 +941,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
xmax, ymax = max(x), max(y)
self.assertEqual((xmin, ymin, xmax, ymax), poly.extent)
def test25_pickle(self):
def test_pickle(self):
"Testing pickling and unpickling support."
# Using both pickle and cPickle -- just 'cause.
import pickle, cPickle
@ -958,9 +964,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(geom, tmpg)
if not no_srid: self.assertEqual(geom.srid, tmpg.srid)
def test26_prepared(self):
@unittest.skipUnless(GEOS_PREPARE, "geos >= 3.1.0 is required")
def test_prepared(self):
"Testing PreparedGeometry support."
if not GEOS_PREPARE: return
# Creating a simple multipolygon and getting a prepared version.
mpoly = GEOSGeometry('MULTIPOLYGON(((0 0,0 5,5 5,5 0,0 0)),((5 5,5 10,10 10,10 5,5 5)))')
prep = mpoly.prepared
@ -974,7 +980,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(mpoly.intersects(pnt), prep.intersects(pnt))
self.assertEqual(c, prep.covers(pnt))
def test26_line_merge(self):
def test_line_merge(self):
"Testing line merge support"
ref_geoms = (fromstr('LINESTRING(1 1, 1 1, 3 3)'),
fromstr('MULTILINESTRING((1 1, 3 3), (3 3, 4 2))'),
@ -985,10 +991,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
for geom, merged in zip(ref_geoms, ref_merged):
self.assertEqual(merged, geom.merged)
def test27_valid_reason(self):
@unittest.skipUnless(GEOS_PREPARE, "geos >= 3.1.0 is required")
def test_valid_reason(self):
"Testing IsValidReason support"
# Skipping tests if GEOS < v3.1.
if not GEOS_PREPARE: return
g = GEOSGeometry("POINT(0 0)")
self.assertTrue(g.valid)
@ -1005,7 +1010,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
print("\nEND - expecting GEOS_NOTICE; safe to ignore.\n")
def test28_geos_version(self):
def test_geos_version(self):
"Testing the GEOS version regular expression."
from django.contrib.gis.geos.libgeos import version_regex
versions = [ ('3.0.0rc4-CAPI-1.3.3', '3.0.0'),

View File

@ -1,5 +1,7 @@
from django.utils.safestring import mark_safe
from django.contrib.gis.geos import fromstr, Point, LineString, LinearRing, Polygon
from django.utils.functional import total_ordering
from django.utils.safestring import mark_safe
class GEvent(object):
"""
@ -166,6 +168,7 @@ class GPolyline(GOverlayBase):
return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity)
@total_ordering
class GIcon(object):
"""
Creates a GIcon object to pass into a Gmarker object.
@ -231,8 +234,11 @@ class GIcon(object):
self.iconanchor = iconanchor
self.infowindowanchor = infowindowanchor
def __cmp__(self, other):
return cmp(self.varname, other.varname)
def __eq__(self, other):
return self.varname == other.varname
def __lt__(self, other):
return self.varname < other.varname
def __hash__(self):
# XOR with hash of GIcon type so that hash('varname') won't

View File

@ -38,6 +38,8 @@ and Geoff Biggs' PhD work on dimensioned units for robotics.
__all__ = ['A', 'Area', 'D', 'Distance']
from decimal import Decimal
from django.utils.functional import total_ordering
class MeasureBase(object):
def default_units(self, kwargs):
"""
@ -84,6 +86,7 @@ class MeasureBase(object):
else:
raise Exception('Could not find a unit keyword associated with "%s"' % unit_str)
@total_ordering
class Distance(MeasureBase):
UNITS = {
'chain' : 20.1168,
@ -178,9 +181,15 @@ class Distance(MeasureBase):
def __str__(self):
return '%s %s' % (getattr(self, self._default_unit), self._default_unit)
def __cmp__(self, other):
def __eq__(self, other):
if isinstance(other, Distance):
return cmp(self.m, other.m)
return self.m == other.m
else:
return NotImplemented
def __lt__(self, other):
if isinstance(other, Distance):
return self.m < other.m
else:
return NotImplemented
@ -244,6 +253,7 @@ class Distance(MeasureBase):
def __nonzero__(self):
return bool(self.m)
@total_ordering
class Area(MeasureBase):
# Getting the square units values and the alias dictionary.
UNITS = dict([('sq_%s' % k, v ** 2) for k, v in Distance.UNITS.items()])
@ -267,9 +277,15 @@ class Area(MeasureBase):
def __str__(self):
return '%s %s' % (getattr(self, self._default_unit), self._default_unit)
def __cmp__(self, other):
def __eq__(self, other):
if isinstance(other, Area):
return cmp(self.sq_m, other.sq_m)
return self.sq_m == other.sq_m
else:
return NotImplemented
def __lt__(self, other):
if isinstance(other, Area):
return self.sq_m < other.sq_m
else:
return NotImplemented

View File

@ -111,7 +111,7 @@ OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.Sp
// The admin map for this geometry field.
{{ module }}.map = new OpenLayers.Map('{{ id }}_map', options);
// Base Layer
{{ module }}.layers.base = {% block base_layer %}new OpenLayers.Layer.WMS( "{{ wms_name }}", "{{ wms_url }}", {layers: '{{ wms_layer }}'} );{% endblock %}
{{ module }}.layers.base = {% block base_layer %}new OpenLayers.Layer.WMS("{{ wms_name }}", "{{ wms_url }}", {layers: '{{ wms_layer }}'{{ wms_options|safe }}});{% endblock %}
{{ module }}.map.addLayer({{ module }}.layers.base);
{% block extra_layers %}{% endblock %}
{% if is_linestring %}OpenLayers.Feature.Vector.style["default"]["strokeWidth"] = 3; // Default too thin for linestrings. {% endif %}

View File

@ -2,6 +2,7 @@ from __future__ import absolute_import
from django.test import TestCase
from django.contrib.gis import admin
from django.contrib.gis.geos import Point
from .models import City
@ -14,3 +15,21 @@ class GeoAdminTest(TestCase):
admin_js = geoadmin.media.render_js()
self.assertTrue(any([geoadmin.openlayers_url in js for js in admin_js]))
def test_olmap_OSM_rendering(self):
geoadmin = admin.site._registry[City]
result = geoadmin.get_map_widget(City._meta.get_field('point'))(
).render('point', Point(-79.460734, 40.18476))
self.assertIn(
"""geodjango_point.layers.base = new OpenLayers.Layer.OSM("OpenStreetMap (Mapnik)");""",
result)
def test_olmap_WMS_rendering(self):
admin.site.unregister(City)
admin.site.register(City, admin.GeoModelAdmin)
geoadmin = admin.site._registry[City]
result = geoadmin.get_map_widget(City._meta.get_field('point'))(
).render('point', Point(-79.460734, 40.18476))
self.assertIn(
"""geodjango_point.layers.base = new OpenLayers.Layer.WMS("OpenLayers WMS", "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: \'basic\', format: 'image/jpeg'});""",
result)

View File

@ -17,6 +17,7 @@ class CountyFeat(models.Model):
class City(models.Model):
name = models.CharField(max_length=25)
name_txt = models.TextField(default='')
population = models.IntegerField()
density = models.DecimalField(max_digits=7, decimal_places=1)
dt = models.DateField()

View File

@ -4,11 +4,11 @@ import os
from copy import copy
from decimal import Decimal
from django.utils.unittest import TestCase
from django.contrib.gis.gdal import DataSource
from django.contrib.gis.tests.utils import mysql
from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError, InvalidDecimal, MissingForeignKey
from django.contrib.gis.utils.layermapping import (LayerMapping, LayerMapError,
InvalidDecimal, MissingForeignKey)
from django.test import TestCase
from .models import (
City, County, CountyFeat, Interstate, ICity1, ICity2, Invalid, State,
@ -28,7 +28,7 @@ STATES = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado']
class LayerMapTest(TestCase):
def test01_init(self):
def test_init(self):
"Testing LayerMapping initialization."
# Model field that does not exist.
@ -46,22 +46,14 @@ class LayerMapTest(TestCase):
# Incrementing through the bad mapping dictionaries and
# ensuring that a LayerMapError is raised.
for bad_map in (bad1, bad2, bad3):
try:
with self.assertRaises(LayerMapError):
lm = LayerMapping(City, city_shp, bad_map)
except LayerMapError:
pass
else:
self.fail('Expected a LayerMapError.')
# A LookupError should be thrown for bogus encodings.
try:
with self.assertRaises(LookupError):
lm = LayerMapping(City, city_shp, city_mapping, encoding='foobar')
except LookupError:
pass
else:
self.fail('Expected a LookupError')
def test02_simple_layermap(self):
def test_simple_layermap(self):
"Test LayerMapping import of a simple point shapefile."
# Setting up for the LayerMapping.
lm = LayerMapping(City, city_shp, city_mapping)
@ -85,18 +77,14 @@ class LayerMapTest(TestCase):
self.assertAlmostEqual(pnt1.x, pnt2.x, 5)
self.assertAlmostEqual(pnt1.y, pnt2.y, 5)
def test03_layermap_strict(self):
def test_layermap_strict(self):
"Testing the `strict` keyword, and import of a LineString shapefile."
# When the `strict` keyword is set an error encountered will force
# the importation to stop.
try:
with self.assertRaises(InvalidDecimal):
lm = LayerMapping(Interstate, inter_shp, inter_mapping)
lm.save(silent=True, strict=True)
except InvalidDecimal:
# No transactions for geoms on MySQL; delete added features.
if mysql: Interstate.objects.all().delete()
else:
self.fail('Should have failed on strict import with invalid decimal values.')
Interstate.objects.all().delete()
# This LayerMapping should work b/c `strict` is not set.
lm = LayerMapping(Interstate, inter_shp, inter_mapping)
@ -137,7 +125,7 @@ class LayerMapTest(TestCase):
qs = CountyFeat.objects.filter(name=name)
self.assertEqual(n, qs.count())
def test04_layermap_unique_multigeometry_fk(self):
def test_layermap_unique_multigeometry_fk(self):
"Testing the `unique`, and `transform`, geometry collection conversion, and ForeignKey mappings."
# All the following should work.
try:
@ -176,8 +164,9 @@ class LayerMapTest(TestCase):
self.assertRaises(MissingForeignKey, lm.save, silent=True, strict=True)
# Now creating the state models so the ForeignKey mapping may work.
co, hi, tx = State(name='Colorado'), State(name='Hawaii'), State(name='Texas')
co.save(), hi.save(), tx.save()
State.objects.bulk_create([
State(name='Colorado'), State(name='Hawaii'), State(name='Texas')
])
# If a mapping is specified as a collection, all OGR fields that
# are not collections will be converted into them. For example,
@ -203,16 +192,19 @@ class LayerMapTest(TestCase):
# The county helper is called to ensure integrity of County models.
self.county_helper()
def test05_test_fid_range_step(self):
def test_test_fid_range_step(self):
"Tests the `fid_range` keyword and the `step` keyword of .save()."
# Function for clearing out all the counties before testing.
def clear_counties(): County.objects.all().delete()
State.objects.bulk_create([
State(name='Colorado'), State(name='Hawaii'), State(name='Texas')
])
# Initializing the LayerMapping object to use in these tests.
lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name')
# Bad feature id ranges should raise a type error.
clear_counties()
bad_ranges = (5.0, 'foo', co_shp)
for bad in bad_ranges:
self.assertRaises(TypeError, lm.save, fid_range=bad)
@ -241,8 +233,10 @@ class LayerMapTest(TestCase):
self.assertEqual(2, qs.count())
hi, co = tuple(qs)
hi_idx, co_idx = tuple(map(NAMES.index, ('Honolulu', 'Pueblo')))
self.assertEqual('Pueblo', co.name); self.assertEqual(NUMS[co_idx], len(co.mpoly))
self.assertEqual('Honolulu', hi.name); self.assertEqual(NUMS[hi_idx], len(hi.mpoly))
self.assertEqual('Pueblo', co.name)
self.assertEqual(NUMS[co_idx], len(co.mpoly))
self.assertEqual('Honolulu', hi.name)
self.assertEqual(NUMS[hi_idx], len(hi.mpoly))
# Testing the `step` keyword -- should get the same counties
# regardless of we use a step that divides equally, that is odd,
@ -252,7 +246,7 @@ class LayerMapTest(TestCase):
lm.save(step=st, strict=True)
self.county_helper(county_feat=False)
def test06_model_inheritance(self):
def test_model_inheritance(self):
"Tests LayerMapping on inherited models. See #12093."
icity_mapping = {'name' : 'Name',
'population' : 'Population',
@ -272,9 +266,18 @@ class LayerMapTest(TestCase):
self.assertEqual(6, ICity1.objects.count())
self.assertEqual(3, ICity2.objects.count())
def test07_invalid_layer(self):
def test_invalid_layer(self):
"Tests LayerMapping on invalid geometries. See #15378."
invalid_mapping = {'point': 'POINT'}
lm = LayerMapping(Invalid, invalid_shp, invalid_mapping,
source_srs=4326)
lm.save(silent=True)
def test_textfield(self):
"Tests that String content fits also in a TextField"
mapping = copy(city_mapping)
mapping['name_txt'] = 'Name'
lm = LayerMapping(City, city_shp, mapping)
lm.save(silent=True, strict=True)
self.assertEqual(City.objects.count(), 3)
self.assertEqual(City.objects.all().order_by('name_txt')[0].name_txt, "Houston")

View File

@ -332,7 +332,7 @@ class LayerMapping(object):
val = unicode(ogr_field.value, self.encoding)
else:
val = ogr_field.value
if len(val) > model_field.max_length:
if model_field.max_length and len(val) > model_field.max_length:
raise InvalidString('%s model field maximum string length is %s, given %s characters.' %
(model_field.name, model_field.max_length, len(val)))
elif isinstance(ogr_field, OFTReal) and isinstance(model_field, models.DecimalField):

View File

@ -190,7 +190,7 @@ class HumanizeTests(TestCase):
orig_humanize_datetime = humanize.datetime
orig_timesince_datetime = timesince.datetime
humanize.datetime = MockDateTime
timesince.datetime = new.module("mock_datetime")
timesince.datetime = new.module(b"mock_datetime")
timesince.datetime.datetime = MockDateTime
try:

View File

@ -2,7 +2,7 @@
from django.utils.translation import ugettext_lazy as _
PROVINCE_CHOICES = (
('01', _('Arava')),
('01', _('Araba')),
('02', _('Albacete')),
('03', _('Alacant')),
('04', _('Almeria')),

View File

@ -1,5 +1,5 @@
from django import http
from django.conf import settings
from django.conf import settings, global_settings
from django.contrib.messages import constants, utils, get_level, set_level
from django.contrib.messages.api import MessageFailure
from django.contrib.messages.storage import default_storage, base
@ -57,6 +57,7 @@ class BaseTest(TestCase):
def setUp(self):
self.settings_override = override_settings_tags(
TEMPLATE_DIRS = (),
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS,
MESSAGE_TAGS = '',
MESSAGE_STORAGE = '%s.%s' % (self.storage_class.__module__,
self.storage_class.__name__),

View File

@ -1,6 +1,3 @@
import os
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.test import TestCase
@ -13,16 +10,9 @@ class SitemapTestsBase(TestCase):
def setUp(self):
self.base_url = '%s://%s' % (self.protocol, self.domain)
self.old_USE_L10N = settings.USE_L10N
self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
settings.TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates'),
)
self.old_Site_meta_installed = Site._meta.installed
# Create a user that will double as sitemap content
User.objects.create_user('testuser', 'test@example.com', 's3krit')
def tearDown(self):
settings.USE_L10N = self.old_USE_L10N
settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
Site._meta.installed = self.old_Site_meta_installed

View File

@ -1,15 +1,19 @@
import os
from datetime import date
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sitemaps import Sitemap, GenericSitemap
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
from django.test.utils import override_settings
from django.utils.unittest import skipUnless
from django.utils.formats import localize
from django.utils.translation import activate, deactivate
from .base import SitemapTestsBase
class HTTPSitemapTests(SitemapTestsBase):
def test_simple_sitemap_index(self):
@ -21,6 +25,9 @@ class HTTPSitemapTests(SitemapTestsBase):
</sitemapindex>
""" % self.base_url)
@override_settings(
TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'templates'),)
)
def test_simple_sitemap_custom_index(self):
"A simple sitemap index can be rendered with a custom template"
response = self.client.get('/simple/custom-index.xml')
@ -49,6 +56,9 @@ class HTTPSitemapTests(SitemapTestsBase):
</urlset>
""" % (self.base_url, date.today()))
@override_settings(
TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'templates'),)
)
def test_simple_custom_sitemap(self):
"A simple sitemap can be rendered with a custom template"
response = self.client.get('/simple/custom-sitemap.xml')
@ -60,10 +70,9 @@ class HTTPSitemapTests(SitemapTestsBase):
""" % (self.base_url, date.today()))
@skipUnless(settings.USE_I18N, "Internationalization is not enabled")
@override_settings(USE_L10N=True)
def test_localized_priority(self):
"The priority value should not be localized (Refs #14164)"
# Localization should be active
settings.USE_L10N = True
activate('fr')
self.assertEqual(u'0,3', localize(0.3))

View File

@ -4,7 +4,7 @@ from optparse import make_option
from django.core.files.storage import FileSystemStorage
from django.core.management.base import CommandError, NoArgsCommand
from django.utils.encoding import smart_str, smart_unicode
from django.utils.encoding import smart_unicode
from django.utils.datastructures import SortedDict
from django.contrib.staticfiles import finders, storage
@ -178,15 +178,12 @@ Type 'yes' to continue, or 'no' to cancel: """
', %s post-processed'
% post_processed_count or ''),
}
self.stdout.write(smart_str(summary))
self.stdout.write(summary)
def log(self, msg, level=2):
"""
Small log helper
"""
msg = smart_str(msg)
if not msg.endswith("\n"):
msg += "\n"
if self.verbosity >= level:
self.stdout.write(msg)

View File

@ -23,9 +23,7 @@ class Command(LabelCommand):
result = [result]
output = u'\n '.join(
(smart_unicode(os.path.realpath(path)) for path in result))
self.stdout.write(
smart_str(u"Found '%s' here:\n %s\n" % (path, output)))
self.stdout.write(u"Found '%s' here:\n %s" % (path, output))
else:
if verbosity >= 1:
self.stderr.write(
smart_str("No matching file found for '%s'.\n" % path))
self.stderr.write("No matching file found for '%s'." % path)

View File

@ -64,6 +64,17 @@ class CachedFilesMixin(object):
compiled = re.compile(pattern)
self._patterns.setdefault(extension, []).append(compiled)
def file_hash(self, name, content=None):
"""
Retuns a hash of the file with the given name and optional content.
"""
if content is None:
return None
md5 = hashlib.md5()
for chunk in content.chunks():
md5.update(chunk)
return md5.hexdigest()[:12]
def hashed_name(self, name, content=None):
parsed_name = urlsplit(unquote(name))
clean_name = parsed_name.path.strip()
@ -78,13 +89,11 @@ class CachedFilesMixin(object):
return name
path, filename = os.path.split(clean_name)
root, ext = os.path.splitext(filename)
# Get the MD5 hash of the file
md5 = hashlib.md5()
for chunk in content.chunks():
md5.update(chunk)
md5sum = md5.hexdigest()[:12]
hashed_name = os.path.join(path, u"%s.%s%s" %
(root, md5sum, ext))
file_hash = self.file_hash(clean_name, content)
if file_hash is not None:
file_hash = u".%s" % file_hash
hashed_name = os.path.join(path, u"%s%s%s" %
(root, file_hash, ext))
unparsed_name = list(parsed_name)
unparsed_name[2] = hashed_name
# Special casing for a @font-face hack, like url(myfont.eot?#iefix")

View File

@ -22,7 +22,7 @@ def csrf(request):
# In order to be able to provide debugging info in the
# case of misconfiguration, we use a sentinel value
# instead of returning an empty dict.
return 'NOTPROVIDED'
return b'NOTPROVIDED'
else:
return token
_get_val = lazy(_get_val, str)

View File

@ -1,6 +1,7 @@
"""
Global Django exception and warning classes.
"""
from functools import reduce
class DjangoRuntimeWarning(RuntimeWarning):
pass

View File

@ -12,7 +12,8 @@ class File(FileProxyMixin):
if name is None:
name = getattr(file, 'name', None)
self.name = name
self.mode = getattr(file, 'mode', None)
if hasattr(file, 'mode'):
self.mode = file.mode
def __str__(self):
return smart_str(self.name or '')
@ -125,7 +126,7 @@ class ContentFile(File):
A File-like object that takes just raw content, rather than an actual file.
"""
def __init__(self, content, name=None):
content = content or ''
content = content or b''
super(ContentFile, self).__init__(BytesIO(content), name=name)
self.size = len(content)

View File

@ -30,8 +30,8 @@ class UploadedFile(File):
self.charset = charset
def __repr__(self):
return "<%s: %s (%s)>" % (
self.__class__.__name__, smart_str(self.name), self.content_type)
return smart_str("<%s: %s (%s)>" % (
self.__class__.__name__, self.name, self.content_type))
def _get_name(self):
return self._name

View File

@ -78,14 +78,14 @@ class LimitedStream(object):
def __init__(self, stream, limit, buf_size=64 * 1024 * 1024):
self.stream = stream
self.remaining = limit
self.buffer = ''
self.buffer = b''
self.buf_size = buf_size
def _read_limited(self, size=None):
if size is None or size > self.remaining:
size = self.remaining
if size == 0:
return ''
return b''
result = self.stream.read(size)
self.remaining -= len(result)
return result
@ -93,17 +93,17 @@ class LimitedStream(object):
def read(self, size=None):
if size is None:
result = self.buffer + self._read_limited()
self.buffer = ''
self.buffer = b''
elif size < len(self.buffer):
result = self.buffer[:size]
self.buffer = self.buffer[size:]
else: # size >= len(self.buffer)
result = self.buffer + self._read_limited(size - len(self.buffer))
self.buffer = ''
self.buffer = b''
return result
def readline(self, size=None):
while '\n' not in self.buffer and \
while b'\n' not in self.buffer and \
(size is None or len(self.buffer) < size):
if size:
# since size is not None here, len(self.buffer) < size

View File

@ -45,6 +45,29 @@ def handle_default_options(options):
sys.path.insert(0, options.pythonpath)
class OutputWrapper(object):
"""
Wrapper around stdout/stderr
"""
def __init__(self, out, style_func=None, ending='\n'):
self._out = out
self.style_func = None
if hasattr(out, 'isatty') and out.isatty():
self.style_func = style_func
self.ending = ending
def __getattr__(self, name):
return getattr(self._out, name)
def write(self, msg, style_func=None, ending=None):
ending = ending is None and self.ending or ending
if ending and not msg.endswith(ending):
msg += ending
style_func = [f for f in (style_func, self.style_func, lambda x:x)
if f is not None][0]
self._out.write(smart_str(style_func(msg)))
class BaseCommand(object):
"""
The base class from which all management commands ultimately
@ -74,8 +97,9 @@ class BaseCommand(object):
output and, if the command is intended to produce a block of
SQL statements, will be wrapped in ``BEGIN`` and ``COMMIT``.
4. If ``handle()`` raised a ``CommandError``, ``execute()`` will
instead print an error message to ``stderr``.
4. If ``handle()`` or ``execute()`` raised any exception (e.g.
``CommandError``), ``run_from_argv()`` will instead print an error
message to ``stderr``.
Thus, the ``handle()`` method is typically the starting point for
subclasses; many built-in commands and command types either place
@ -187,46 +211,42 @@ class BaseCommand(object):
def run_from_argv(self, argv):
"""
Set up any environment changes requested (e.g., Python path
and Django settings), then run this command.
and Django settings), then run this command. If the
command raises a ``CommandError``, intercept it and print it sensibly
to stderr.
"""
parser = self.create_parser(argv[0], argv[1])
options, args = parser.parse_args(argv[2:])
handle_default_options(options)
self.execute(*args, **options.__dict__)
try:
self.execute(*args, **options.__dict__)
except Exception as e:
if options.traceback:
self.stderr.write(traceback.format_exc())
self.stderr.write('%s: %s' % (e.__class__.__name__, e))
sys.exit(1)
def execute(self, *args, **options):
"""
Try to execute this command, performing model validation if
needed (as controlled by the attribute
``self.requires_model_validation``, except if force-skipped). If the
command raises a ``CommandError``, intercept it and print it sensibly
to stderr.
``self.requires_model_validation``, except if force-skipped).
"""
show_traceback = options.get('traceback', False)
# Switch to English, because django-admin.py creates database content
# like permissions, and those shouldn't contain any translations.
# But only do this if we can assume we have a working settings file,
# because django.utils.translation requires settings.
saved_lang = None
self.stdout = OutputWrapper(options.get('stdout', sys.stdout))
self.stderr = OutputWrapper(options.get('stderr', sys.stderr), self.style.ERROR)
if self.can_import_settings:
try:
from django.utils import translation
saved_lang = translation.get_language()
translation.activate('en-us')
except ImportError as e:
# If settings should be available, but aren't,
# raise the error and quit.
if show_traceback:
traceback.print_exc()
else:
sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
sys.exit(1)
from django.utils import translation
saved_lang = translation.get_language()
translation.activate('en-us')
try:
self.stdout = options.get('stdout', sys.stdout)
self.stderr = options.get('stderr', sys.stderr)
if self.requires_model_validation and not options.get('skip_validation'):
self.validate()
output = self.handle(*args, **options)
@ -237,16 +257,10 @@ class BaseCommand(object):
from django.db import connections, DEFAULT_DB_ALIAS
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
if connection.ops.start_transaction_sql():
self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()) + '\n')
self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()))
self.stdout.write(output)
if self.output_transaction:
self.stdout.write('\n' + self.style.SQL_KEYWORD("COMMIT;") + '\n')
except CommandError as e:
if show_traceback:
traceback.print_exc()
else:
self.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
sys.exit(1)
self.stdout.write('\n' + self.style.SQL_KEYWORD("COMMIT;"))
finally:
if saved_lang is not None:
translation.activate(saved_lang)
@ -266,7 +280,7 @@ class BaseCommand(object):
error_text = s.read()
raise CommandError("One or more models did not validate:\n%s" % error_text)
if display_num_errors:
self.stdout.write("%s error%s found\n" % (num_errors, num_errors != 1 and 's' or ''))
self.stdout.write("%s error%s found" % (num_errors, num_errors != 1 and 's' or ''))
def handle(self, *args, **options):
"""

View File

@ -5,7 +5,7 @@ from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
def has_bom(fn):
with open(fn, 'r') as f:
with open(fn, 'rb') as f:
sample = f.read(4)
return sample[:3] == '\xef\xbb\xbf' or \
sample.startswith(codecs.BOM_UTF16_LE) or \

View File

@ -1,7 +1,7 @@
from optparse import make_option
from django.core.cache.backends.db import BaseDatabaseCache
from django.core.management.base import LabelCommand
from django.core.management.base import LabelCommand, CommandError
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
from django.db.utils import DatabaseError
@ -55,11 +55,10 @@ class Command(LabelCommand):
try:
curs.execute("\n".join(full_statement))
except DatabaseError as e:
self.stderr.write(
self.style.ERROR("Cache table '%s' could not be created.\nThe error was: %s.\n" %
(tablename, e)))
transaction.rollback_unless_managed(using=db)
else:
for statement in index_output:
curs.execute(statement)
transaction.commit_unless_managed(using=db)
raise CommandError(
"Cache table '%s' could not be created.\nThe error was: %s." %
(tablename, e))
for statement in index_output:
curs.execute(statement)
transaction.commit_unless_managed(using=db)

View File

@ -97,20 +97,24 @@ class Command(BaseCommand):
except KeyError:
raise CommandError("Unknown serialization format: %s" % format)
# Now collate the objects to be serialized.
objects = []
for model in sort_dependencies(app_list.items()):
if model in excluded_models:
continue
if not model._meta.proxy and router.allow_syncdb(using, model):
if use_base_manager:
objects.extend(model._base_manager.using(using).all())
else:
objects.extend(model._default_manager.using(using).all())
def get_objects():
# Collate the objects to be serialized.
for model in sort_dependencies(app_list.items()):
if model in excluded_models:
continue
if not model._meta.proxy and router.allow_syncdb(using, model):
if use_base_manager:
objects = model._base_manager
else:
objects = model._default_manager
for obj in objects.using(using).\
order_by(model._meta.pk.name).iterator():
yield obj
try:
return serializers.serialize(format, objects, indent=indent,
use_natural_keys=use_natural_keys)
self.stdout.ending = None
serializers.serialize(format, get_objects(), indent=indent,
use_natural_keys=use_natural_keys, stream=self.stdout)
except Exception as e:
if show_traceback:
raise

View File

@ -26,6 +26,8 @@ class Command(NoArgsCommand):
def handle_inspection(self, options):
connection = connections[options.get('database')]
# 'table_name_filter' is a stealth option
table_name_filter = options.get('table_name_filter')
table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
@ -43,6 +45,9 @@ class Command(NoArgsCommand):
yield ''
known_models = []
for table_name in connection.introspection.table_names(cursor):
if table_name_filter is not None and callable(table_name_filter):
if not table_name_filter(table_name):
continue
yield 'class %s(models.Model):' % table2model(table_name)
known_models.append(table2model(table_name))
try:

View File

@ -34,11 +34,11 @@ class Command(BaseCommand):
using = options.get('database')
connection = connections[using]
self.style = no_style()
if not len(fixture_labels):
self.stderr.write(
self.style.ERROR("No database fixture specified. Please provide the path of at least one fixture in the command line.\n")
"No database fixture specified. Please provide the path of at "
"least one fixture in the command line."
)
return
@ -124,11 +124,11 @@ class Command(BaseCommand):
if formats:
if verbosity >= 2:
self.stdout.write("Loading '%s' fixtures...\n" % fixture_name)
self.stdout.write("Loading '%s' fixtures..." % fixture_name)
else:
self.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" %
(fixture_name, format)))
"Problem installing fixture '%s': %s is not a known serialization format." %
(fixture_name, format))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
@ -141,7 +141,7 @@ class Command(BaseCommand):
for fixture_dir in fixture_dirs:
if verbosity >= 2:
self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir))
self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir))
label_found = False
for combo in product([using, None], formats, compression_formats):
@ -154,7 +154,7 @@ class Command(BaseCommand):
)
if verbosity >= 3:
self.stdout.write("Trying %s for %s fixture '%s'...\n" % \
self.stdout.write("Trying %s for %s fixture '%s'..." % \
(humanize(fixture_dir), file_name, fixture_name))
full_path = os.path.join(fixture_dir, file_name)
open_method = compression_types[compression_format]
@ -162,13 +162,13 @@ class Command(BaseCommand):
fixture = open_method(full_path, 'r')
except IOError:
if verbosity >= 2:
self.stdout.write("No %s fixture '%s' in %s.\n" % \
self.stdout.write("No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir)))
else:
try:
if label_found:
self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" %
(fixture_name, humanize(fixture_dir))))
self.stderr.write("Multiple fixtures named '%s' in %s. Aborting." %
(fixture_name, humanize(fixture_dir)))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
@ -178,7 +178,7 @@ class Command(BaseCommand):
objects_in_fixture = 0
loaded_objects_in_fixture = 0
if verbosity >= 2:
self.stdout.write("Installing %s fixture '%s' from %s.\n" % \
self.stdout.write("Installing %s fixture '%s' from %s." % \
(format, fixture_name, humanize(fixture_dir)))
objects = serializers.deserialize(format, fixture, using=using)
@ -209,8 +209,8 @@ class Command(BaseCommand):
# error was encountered during fixture loading.
if objects_in_fixture == 0:
self.stderr.write(
self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" %
(fixture_name)))
"No fixture data found for '%s'. (File format may be invalid.)" %
(fixture_name))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
@ -231,16 +231,16 @@ class Command(BaseCommand):
traceback.print_exc()
else:
self.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" %
"Problem installing fixture '%s': %s" %
(full_path, ''.join(traceback.format_exception(sys.exc_type,
sys.exc_value, sys.exc_traceback)))))
sys.exc_value, sys.exc_traceback))))
return
# If we found even one object in a fixture, we need to reset the
# database sequences.
if loaded_object_count > 0:
sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
sequence_sql = connection.ops.sequence_reset_sql(no_style(), models)
if sequence_sql:
if verbosity >= 2:
self.stdout.write("Resetting sequences\n")
@ -253,10 +253,10 @@ class Command(BaseCommand):
if verbosity >= 1:
if fixture_object_count == loaded_object_count:
self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % (
self.stdout.write("Installed %d object(s) from %d fixture(s)" % (
loaded_object_count, fixture_count))
else:
self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)\n" % (
self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)" % (
loaded_object_count, fixture_object_count, fixture_count))
# Close the DB connection. This is required as a workaround for an

View File

@ -120,12 +120,12 @@ class Command(BaseCommand):
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')
sys.stderr.write("Error: %s" % error_text)
# Need to use an OS exit because sys.exit doesn't work in a thread
os._exit(1)
except KeyboardInterrupt:
if shutdown_message:
self.stdout.write("%s\n" % shutdown_message)
self.stdout.write(shutdown_message)
sys.exit(0)

View File

@ -16,7 +16,6 @@ from os import path
import django
from django.template import Template, Context
from django.utils import archive
from django.utils.encoding import smart_str
from django.utils._os import rmtree_errorhandler
from django.core.management.base import BaseCommand, CommandError
from django.core.management.commands.makemessages import handle_extensions
@ -166,11 +165,10 @@ class TemplateCommand(BaseCommand):
shutil.copymode(old_path, new_path)
self.make_writeable(new_path)
except OSError:
notice = self.style.NOTICE(
self.stderr.write(
"Notice: Couldn't set permission bits on %s. You're "
"probably using an uncommon filesystem setup. No "
"problem.\n" % new_path)
sys.stderr.write(smart_str(notice))
"problem." % new_path, self.style.NOTICE)
if self.paths_to_remove:
if self.verbosity >= 2:

View File

@ -39,6 +39,7 @@ class Serializer(object):
self.use_natural_keys = options.pop("use_natural_keys", False)
self.start_serialization()
self.first = True
for obj in queryset:
self.start_object(obj)
# Use the concrete parent class' _meta instead of the object's _meta
@ -57,6 +58,8 @@ class Serializer(object):
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_m2m_field(obj, field)
self.end_object(obj)
if self.first:
self.first = False
self.end_serialization()
return self.getvalue()

View File

@ -21,13 +21,38 @@ class Serializer(PythonSerializer):
"""
internal_use_only = False
def end_serialization(self):
def start_serialization(self):
if json.__version__.split('.') >= ['2', '1', '3']:
# Use JS strings to represent Python Decimal instances (ticket #16850)
self.options.update({'use_decimal': False})
json.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options)
self._current = None
self.json_kwargs = self.options.copy()
self.json_kwargs.pop('stream', None)
self.json_kwargs.pop('fields', None)
self.stream.write("[")
def end_serialization(self):
if self.options.get("indent"):
self.stream.write("\n")
self.stream.write("]")
if self.options.get("indent"):
self.stream.write("\n")
def end_object(self, obj):
# self._current has the field data
indent = self.options.get("indent")
if not self.first:
self.stream.write(",")
if not indent:
self.stream.write(" ")
if indent:
self.stream.write("\n")
json.dump(self.get_dump_object(obj), self.stream,
cls=DjangoJSONEncoder, **self.json_kwargs)
self._current = None
def getvalue(self):
# overwrite PythonSerializer.getvalue() with base Serializer.getvalue()
if callable(getattr(self.stream, 'getvalue', None)):
return self.stream.getvalue()

View File

@ -27,13 +27,16 @@ class Serializer(base.Serializer):
self._current = {}
def end_object(self, obj):
self.objects.append({
"model" : smart_unicode(obj._meta),
"pk" : smart_unicode(obj._get_pk_val(), strings_only=True),
"fields" : self._current
})
self.objects.append(self.get_dump_object(obj))
self._current = None
def get_dump_object(self, obj):
return {
"pk": smart_unicode(obj._get_pk_val(), strings_only=True),
"model": smart_unicode(obj._meta),
"fields": self._current
}
def handle_field(self, obj, field):
value = field._get_val_from_obj(obj)
# Protected types (i.e., primitives like None, numbers, dates,

View File

@ -146,25 +146,23 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
env['PATH_INFO'] = urllib.unquote(path)
env['QUERY_STRING'] = query
env['REMOTE_ADDR'] = self.client_address[0]
env['CONTENT_TYPE'] = self.headers.get('content-type', 'text/plain')
if self.headers.typeheader is None:
env['CONTENT_TYPE'] = self.headers.type
else:
env['CONTENT_TYPE'] = self.headers.typeheader
length = self.headers.getheader('content-length')
length = self.headers.get('content-length')
if length:
env['CONTENT_LENGTH'] = length
for h in self.headers.headers:
k,v = h.split(':',1)
k=k.replace('-','_').upper(); v=v.strip()
if k in env:
continue # skip content length, type,etc.
if 'HTTP_'+k in env:
env['HTTP_'+k] += ','+v # comma-separate multiple headers
for key, value in self.headers.items():
key = key.replace('-','_').upper()
value = value.strip()
if key in env:
# Skip content length, type, etc.
continue
if 'HTTP_' + key in env:
# Comma-separate multiple headers
env['HTTP_' + key] += ',' + value
else:
env['HTTP_'+k] = v
env['HTTP_' + key] = value
return env
def log_message(self, format, *args):

View File

@ -306,10 +306,8 @@ class RegexURLResolver(LocaleRegexProvider):
tried.append([pattern])
else:
if sub_match:
sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
sub_match_dict.update(self.default_kwargs)
for k, v in sub_match.kwargs.iteritems():
sub_match_dict[smart_str(k)] = v
sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
sub_match_dict.update(sub_match.kwargs)
return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [self.namespace] + sub_match.namespaces)
tried.append([pattern])
raise Resolver404({'tried': tried, 'path': new_path})

View File

@ -399,6 +399,9 @@ class BaseDatabaseFeatures(object):
# in the SQL standard.
supports_tablespaces = False
# Does the backend reset sequences between tests?
supports_sequence_reset = True
# Features that need to be confirmed at runtime
# Cache whether the confirmation has been performed.
_confirmed = False
@ -414,10 +417,11 @@ class BaseDatabaseFeatures(object):
def confirm(self):
"Perform manual checks of any database features that might vary between installs"
self._confirmed = True
self.supports_transactions = self._supports_transactions()
self.supports_stddev = self._supports_stddev()
self.can_introspect_foreign_keys = self._can_introspect_foreign_keys()
if not self._confirmed:
self._confirmed = True
self.supports_transactions = self._supports_transactions()
self.supports_stddev = self._supports_stddev()
self.can_introspect_foreign_keys = self._can_introspect_foreign_keys()
def _supports_transactions(self):
"Confirm support for transactions"
@ -439,8 +443,9 @@ class BaseDatabaseFeatures(object):
try:
self.connection.ops.check_aggregate_support(StdDevPop())
return True
except NotImplementedError:
self.supports_stddev = False
return False
def _can_introspect_foreign_keys(self):
"Confirm support for introspected foreign keys"

View File

@ -418,11 +418,20 @@ class DatabaseWrapper(BaseDatabaseWrapper):
@cached_property
def mysql_version(self):
if not self.server_version:
new_connection = False
if not self._valid_connection():
# Ensure we have a connection with the DB by using a temporary
# cursor
new_connection = True
self.cursor().close()
m = server_version_re.match(self.connection.get_server_info())
server_info = self.connection.get_server_info()
if new_connection:
# Make sure we close the connection
self.connection.close()
self.connection = None
m = server_version_re.match(server_info)
if not m:
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
raise Exception('Unable to determine MySQL version from version string %r' % server_info)
self.server_version = tuple([int(x) for x in m.groups()])
return self.server_version

View File

@ -83,6 +83,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
ignores_nulls_in_unique_constraints = False
has_bulk_insert = True
supports_tablespaces = True
supports_sequence_reset = False
class DatabaseOperations(BaseDatabaseOperations):
compiler_module = "django.db.backends.oracle.compiler"

View File

@ -51,15 +51,15 @@ def adapt_datetime_with_timezone_support(value):
default_timezone = timezone.get_default_timezone()
value = timezone.make_aware(value, default_timezone)
value = value.astimezone(timezone.utc).replace(tzinfo=None)
return value.isoformat(" ")
return value.isoformat(b" ")
Database.register_converter("bool", lambda s: str(s) == '1')
Database.register_converter("time", parse_time)
Database.register_converter("date", parse_date)
Database.register_converter("datetime", parse_datetime_with_timezone_support)
Database.register_converter("timestamp", parse_datetime_with_timezone_support)
Database.register_converter("TIMESTAMP", parse_datetime_with_timezone_support)
Database.register_converter("decimal", util.typecast_decimal)
Database.register_converter(b"bool", lambda s: str(s) == '1')
Database.register_converter(b"time", parse_time)
Database.register_converter(b"date", parse_date)
Database.register_converter(b"datetime", parse_datetime_with_timezone_support)
Database.register_converter(b"timestamp", parse_datetime_with_timezone_support)
Database.register_converter(b"TIMESTAMP", parse_datetime_with_timezone_support)
Database.register_converter(b"decimal", util.typecast_decimal)
Database.register_adapter(datetime.datetime, adapt_datetime_with_timezone_support)
Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal)
if Database.version_info >= (2, 4, 1):

View File

@ -57,11 +57,11 @@ class ModelBase(type):
new_class.add_to_class('_meta', Options(meta, **kwargs))
if not abstract:
new_class.add_to_class('DoesNotExist', subclass_exception('DoesNotExist',
new_class.add_to_class('DoesNotExist', subclass_exception(b'DoesNotExist',
tuple(x.DoesNotExist
for x in parents if hasattr(x, '_meta') and not x._meta.abstract)
or (ObjectDoesNotExist,), module))
new_class.add_to_class('MultipleObjectsReturned', subclass_exception('MultipleObjectsReturned',
new_class.add_to_class('MultipleObjectsReturned', subclass_exception(b'MultipleObjectsReturned',
tuple(x.MultipleObjectsReturned
for x in parents if hasattr(x, '_meta') and not x._meta.abstract)
or (MultipleObjectsReturned,), module))
@ -404,7 +404,6 @@ class Model(object):
# and as a result, the super call will cause an infinite recursion.
# See #10547 and #12121.
defers = []
pk_val = None
if self._deferred:
from django.db.models.query_utils import deferred_class_factory
factory = deferred_class_factory
@ -412,12 +411,7 @@ class Model(object):
if isinstance(self.__class__.__dict__.get(field.attname),
DeferredAttribute):
defers.append(field.attname)
if pk_val is None:
# The pk_val and model values are the same for all
# DeferredAttribute classes, so we only need to do this
# once.
obj = self.__class__.__dict__[field.attname]
model = obj.model_ref()
model = self._meta.proxy_for_model
else:
factory = simple_class_factory
return (model_unpickle, (model, defers, factory), data)

View File

@ -1,3 +1,4 @@
import collections
import copy
import datetime
import decimal
@ -16,7 +17,7 @@ from django.utils.functional import curry, total_ordering
from django.utils.text import capfirst
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode, force_unicode, smart_str
from django.utils.encoding import smart_unicode, force_unicode
from django.utils.ipv6 import clean_ipv6_address
class NOT_PROVIDED:
@ -436,7 +437,7 @@ class Field(object):
return bound_field_class(self, fieldmapping, original)
def _get_choices(self):
if hasattr(self._choices, 'next'):
if isinstance(self._choices, collections.Iterator):
choices, self._choices = tee(self._choices)
return choices
else:
@ -529,7 +530,7 @@ class AutoField(Field):
try:
return int(value)
except (TypeError, ValueError):
msg = self.error_messages['invalid'] % str(value)
msg = self.error_messages['invalid'] % value
raise exceptions.ValidationError(msg)
def validate(self, value, model_instance):
@ -581,7 +582,7 @@ class BooleanField(Field):
return True
if value in ('f', 'False', '0'):
return False
msg = self.error_messages['invalid'] % str(value)
msg = self.error_messages['invalid'] % value
raise exceptions.ValidationError(msg)
def get_prep_lookup(self, lookup_type, value):
@ -677,7 +678,7 @@ class DateField(Field):
return value
if isinstance(value, datetime.datetime):
if settings.USE_TZ and timezone.is_aware(value):
# Convert aware datetimes to the current time zone
# Convert aware datetimes to the default time zone
# before casting them to dates (#17742).
default_timezone = timezone.get_default_timezone()
value = timezone.make_naive(value, default_timezone)
@ -685,8 +686,6 @@ class DateField(Field):
if isinstance(value, datetime.date):
return value
value = smart_str(value)
try:
parsed = parse_date(value)
if parsed is not None:
@ -778,8 +777,6 @@ class DateTimeField(DateField):
value = timezone.make_aware(value, default_timezone)
return value
value = smart_str(value)
try:
parsed = parse_datetime(value)
if parsed is not None:
@ -861,7 +858,7 @@ class DecimalField(Field):
try:
return decimal.Decimal(value)
except decimal.InvalidOperation:
msg = self.error_messages['invalid'] % str(value)
msg = self.error_messages['invalid'] % value
raise exceptions.ValidationError(msg)
def _format(self, value):
@ -966,7 +963,7 @@ class FloatField(Field):
try:
return float(value)
except (TypeError, ValueError):
msg = self.error_messages['invalid'] % str(value)
msg = self.error_messages['invalid'] % value
raise exceptions.ValidationError(msg)
def formfield(self, **kwargs):
@ -1001,7 +998,7 @@ class IntegerField(Field):
try:
return int(value)
except (TypeError, ValueError):
msg = self.error_messages['invalid'] % str(value)
msg = self.error_messages['invalid'] % value
raise exceptions.ValidationError(msg)
def formfield(self, **kwargs):
@ -1106,7 +1103,7 @@ class NullBooleanField(Field):
return True
if value in ('f', 'False', '0'):
return False
msg = self.error_messages['invalid'] % str(value)
msg = self.error_messages['invalid'] % value
raise exceptions.ValidationError(msg)
def get_prep_lookup(self, lookup_type, value):
@ -1227,8 +1224,6 @@ class TimeField(Field):
# database backend (e.g. Oracle), so we'll be accommodating.
return value.time()
value = smart_str(value)
try:
parsed = parse_time(value)
if parsed is not None:

View File

@ -3,6 +3,7 @@ import os
from django import forms
from django.db.models.fields import Field
from django.core.exceptions import ValidationError
from django.core.files.base import File
from django.core.files.storage import default_storage
from django.core.files.images import ImageFile
@ -204,6 +205,11 @@ class FileDescriptor(object):
instance.__dict__[self.field.name] = value
class FileField(Field):
default_error_messages = {
'max_length': _(u'Filename is %(extra)d characters too long.')
}
# The class to wrap instance attributes in. Accessing the file object off
# the instance will always return an instance of attr_class.
attr_class = FieldFile
@ -226,6 +232,21 @@ class FileField(Field):
kwargs['max_length'] = kwargs.get('max_length', 100)
super(FileField, self).__init__(verbose_name, name, **kwargs)
def validate(self, value, model_instance):
"""
Validates that the generated file name still fits within max_length.
"""
# The generated file name stored in the database is generally longer
# than the uploaded file name. Using the length of generated name in
# the error message would be confusing. However, in the common case
# (ie. upload_to='path/to/upload/dir'), the length of the generated
# name equals the length of the uploaded name plus a constant. Thus
# we can tell the user how much shorter the name should be (roughly).
length = len(self.generate_filename(model_instance, value.name))
if self.max_length and length > self.max_length:
error_values = {'extra': length - self.max_length}
raise ValidationError(self.error_messages['max_length'] % error_values)
def get_internal_type(self):
return "FileField"

View File

@ -237,13 +237,18 @@ class SingleRelatedObjectDescriptor(object):
return self.related.model._base_manager.using(db)
def get_prefetch_query_set(self, instances):
vals = set(instance._get_pk_val() for instance in instances)
params = {'%s__pk__in' % self.related.field.name: vals}
return (self.get_query_set(instance=instances[0]).filter(**params),
attrgetter(self.related.field.attname),
lambda obj: obj._get_pk_val(),
True,
self.cache_name)
rel_obj_attr = attrgetter(self.related.field.attname)
instance_attr = lambda obj: obj._get_pk_val()
instances_dict = dict((instance_attr(inst), inst) for inst in instances)
params = {'%s__pk__in' % self.related.field.name: instances_dict.keys()}
qs = self.get_query_set(instance=instances[0]).filter(**params)
# Since we're going to assign directly in the cache,
# we must manage the reverse relation cache manually.
rel_obj_cache_name = self.related.field.get_cache_name()
for rel_obj in qs:
instance = instances_dict[rel_obj_attr(rel_obj)]
setattr(rel_obj, rel_obj_cache_name, instance)
return qs, rel_obj_attr, instance_attr, True, self.cache_name
def __get__(self, instance, instance_type=None):
if instance is None:
@ -324,17 +329,23 @@ class ReverseSingleRelatedObjectDescriptor(object):
return QuerySet(self.field.rel.to).using(db)
def get_prefetch_query_set(self, instances):
vals = set(getattr(instance, self.field.attname) for instance in instances)
rel_obj_attr = attrgetter(self.field.rel.field_name)
instance_attr = attrgetter(self.field.attname)
instances_dict = dict((instance_attr(inst), inst) for inst in instances)
other_field = self.field.rel.get_related_field()
if other_field.rel:
params = {'%s__pk__in' % self.field.rel.field_name: vals}
params = {'%s__pk__in' % self.field.rel.field_name: instances_dict.keys()}
else:
params = {'%s__in' % self.field.rel.field_name: vals}
return (self.get_query_set(instance=instances[0]).filter(**params),
attrgetter(self.field.rel.field_name),
attrgetter(self.field.attname),
True,
self.cache_name)
params = {'%s__in' % self.field.rel.field_name: instances_dict.keys()}
qs = self.get_query_set(instance=instances[0]).filter(**params)
# Since we're going to assign directly in the cache,
# we must manage the reverse relation cache manually.
if not self.field.rel.multiple:
rel_obj_cache_name = self.field.related.get_cache_name()
for rel_obj in qs:
instance = instances_dict[rel_obj_attr(rel_obj)]
setattr(rel_obj, rel_obj_cache_name, instance)
return qs, rel_obj_attr, instance_attr, True, self.cache_name
def __get__(self, instance, instance_type=None):
if instance is None:
@ -467,18 +478,24 @@ class ForeignRelatedObjectsDescriptor(object):
return self.instance._prefetched_objects_cache[rel_field.related_query_name()]
except (AttributeError, KeyError):
db = self._db or router.db_for_read(self.model, instance=self.instance)
return super(RelatedManager, self).get_query_set().using(db).filter(**self.core_filters)
qs = super(RelatedManager, self).get_query_set().using(db).filter(**self.core_filters)
qs._known_related_object = (rel_field.name, self.instance)
return qs
def get_prefetch_query_set(self, instances):
rel_obj_attr = attrgetter(rel_field.get_attname())
instance_attr = attrgetter(attname)
instances_dict = dict((instance_attr(inst), inst) for inst in instances)
db = self._db or router.db_for_read(self.model, instance=instances[0])
query = {'%s__%s__in' % (rel_field.name, attname):
set(getattr(obj, attname) for obj in instances)}
query = {'%s__%s__in' % (rel_field.name, attname): instances_dict.keys()}
qs = super(RelatedManager, self).get_query_set().using(db).filter(**query)
return (qs,
attrgetter(rel_field.get_attname()),
attrgetter(attname),
False,
rel_field.related_query_name())
# Since we just bypassed this class' get_query_set(), we must manage
# the reverse relation manually.
for rel_obj in qs:
instance = instances_dict[rel_obj_attr(rel_obj)]
setattr(rel_obj, rel_field.name, instance)
cache_name = rel_field.related_query_name()
return qs, rel_obj_attr, instance_attr, False, cache_name
def add(self, *objs):
for obj in objs:

View File

@ -41,6 +41,7 @@ class QuerySet(object):
self._for_write = False
self._prefetch_related_lookups = []
self._prefetch_done = False
self._known_related_object = None # (attname, rel_obj)
########################
# PYTHON MAGIC METHODS #
@ -282,9 +283,10 @@ class QuerySet(object):
init_list.append(field.attname)
model_cls = deferred_class_factory(self.model, skip)
# Cache db and model outside the loop
# Cache db, model and known_related_object outside the loop
db = self.db
model = self.model
kro_attname, kro_instance = self._known_related_object or (None, None)
compiler = self.query.get_compiler(using=db)
if fill_cache:
klass_info = get_klass_info(model, max_depth=max_depth,
@ -294,12 +296,12 @@ class QuerySet(object):
obj, _ = get_cached_row(row, index_start, db, klass_info,
offset=len(aggregate_select))
else:
# Omit aggregates in object creation.
row_data = row[index_start:aggregate_start]
if skip:
row_data = row[index_start:aggregate_start]
obj = model_cls(**dict(zip(init_list, row_data)))
else:
# Omit aggregates in object creation.
obj = model(*row[index_start:aggregate_start])
obj = model(*row_data)
# Store the source database of the object
obj._state.db = db
@ -313,7 +315,11 @@ class QuerySet(object):
# Add the aggregates to the model
if aggregate_select:
for i, aggregate in enumerate(aggregate_select):
setattr(obj, aggregate, row[i+aggregate_start])
setattr(obj, aggregate, row[i + aggregate_start])
# Add the known related object to the model, if there is one
if kro_instance:
setattr(obj, kro_attname, kro_instance)
yield obj
@ -864,6 +870,7 @@ class QuerySet(object):
c = klass(model=self.model, query=query, using=self._db)
c._for_write = self._for_write
c._prefetch_related_lookups = self._prefetch_related_lookups[:]
c._known_related_object = self._known_related_object
c.__dict__.update(kwargs)
if setup and hasattr(c, '_setup_query'):
c._setup_query()
@ -1781,9 +1788,7 @@ def prefetch_one_level(instances, prefetcher, attname):
rel_obj_cache = {}
for rel_obj in all_related_objects:
rel_attr_val = rel_obj_attr(rel_obj)
if rel_attr_val not in rel_obj_cache:
rel_obj_cache[rel_attr_val] = []
rel_obj_cache[rel_attr_val].append(rel_obj)
rel_obj_cache.setdefault(rel_attr_val, []).append(rel_obj)
for obj in instances:
instance_attr_val = instance_attr(obj)

View File

@ -6,8 +6,6 @@ large and/or so that they can be used by other modules without getting into
circular import difficulties.
"""
import weakref
from django.db.backends import util
from django.utils import tree
@ -70,8 +68,6 @@ class DeferredAttribute(object):
"""
def __init__(self, field_name, model):
self.field_name = field_name
self.model_ref = weakref.ref(model)
self.loaded = False
def __get__(self, instance, owner):
"""
@ -79,27 +75,32 @@ class DeferredAttribute(object):
Returns the cached value.
"""
from django.db.models.fields import FieldDoesNotExist
non_deferred_model = instance._meta.proxy_for_model
opts = non_deferred_model._meta
assert instance is not None
cls = self.model_ref()
data = instance.__dict__
if data.get(self.field_name, self) is self:
# self.field_name is the attname of the field, but only() takes the
# actual name, so we need to translate it here.
try:
cls._meta.get_field_by_name(self.field_name)
name = self.field_name
f = opts.get_field_by_name(self.field_name)[0]
except FieldDoesNotExist:
name = [f.name for f in cls._meta.fields
if f.attname == self.field_name][0]
# We use only() instead of values() here because we want the
# various data coersion methods (to_python(), etc.) to be called
# here.
val = getattr(
cls._base_manager.filter(pk=instance.pk).only(name).using(
instance._state.db).get(),
self.field_name
)
f = [f for f in opts.fields
if f.attname == self.field_name][0]
name = f.name
# Lets see if the field is part of the parent chain. If so we
# might be able to reuse the already loaded value. Refs #18343.
val = self._check_parent_chain(instance, name)
if val is None:
# We use only() instead of values() here because we want the
# various data coersion methods (to_python(), etc.) to be
# called here.
val = getattr(
non_deferred_model._base_manager.only(name).using(
instance._state.db).get(pk=instance.pk),
self.field_name
)
data[self.field_name] = val
return data[self.field_name]
@ -110,6 +111,20 @@ class DeferredAttribute(object):
"""
instance.__dict__[self.field_name] = value
def _check_parent_chain(self, instance, name):
"""
Check if the field value can be fetched from a parent field already
loaded in the instance. This can be done if the to-be fetched
field is a primary key field.
"""
opts = instance._meta
f = opts.get_field_by_name(name)[0]
link_field = opts.get_ancestor_link(f.model)
if f.primary_key and f != link_field:
return getattr(instance, link_field.attname)
return None
def select_related_descend(field, restricted, requested, reverse=False):
"""
Returns True if this field should be used to descend deeper for

View File

@ -455,6 +455,9 @@ class SQLCompiler(object):
alias = self.query.get_initial_alias()
field, target, opts, joins, _, _ = self.query.setup_joins(pieces,
opts, alias, False)
# We will later on need to promote those joins that were added to the
# query afresh above.
joins_to_promote = [j for j in joins if self.query.alias_refcount[j] < 2]
alias = joins[-1]
col = target.column
if not field.rel:
@ -466,8 +469,9 @@ class SQLCompiler(object):
# Must use left outer joins for nullable fields and their relations.
# Ordering or distinct must not affect the returned set, and INNER
# JOINS for nullable fields could do this.
self.query.promote_alias_chain(joins,
self.query.alias_map[joins[0]].join_type == self.query.LOUTER)
if joins_to_promote:
self.query.promote_alias_chain(joins_to_promote,
self.query.alias_map[joins_to_promote[0]].join_type == self.query.LOUTER)
return field, col, alias, joins, opts
def _final_join_removal(self, col, alias):
@ -1015,6 +1019,12 @@ class SQLUpdateCompiler(SQLCompiler):
query.extra = {}
query.select = []
query.add_fields([query.model._meta.pk.name])
# Recheck the count - it is possible that fiddling with the select
# fields above removes tables from the query. Refs #18304.
count = query.count_active_tables()
if not self.query.related_updates and count == 1:
return
must_pre_select = count > 1 and not self.connection.features.update_can_self_select
# Now we adjust the current query: reset the where clause and get rid

View File

@ -4,6 +4,7 @@ Code to manage the creation and SQL rendering of 'where' constraints.
from __future__ import absolute_import
import collections
import datetime
from itertools import repeat
@ -49,7 +50,7 @@ class WhereNode(tree.Node):
return
obj, lookup_type, value = data
if hasattr(value, '__iter__') and hasattr(value, 'next'):
if isinstance(value, collections.Iterator):
# Consume any generators immediately, so that we can determine
# emptiness and transform any non-empty values correctly.
value = list(value)

View File

@ -156,11 +156,11 @@ class BoundMethodWeakref(object):
"""Whether we are still a valid reference"""
return self() is not None
def __cmp__( self, other ):
def __eq__(self, other):
"""Compare with another reference"""
if not isinstance (other,self.__class__):
return cmp( self.__class__, type(other) )
return cmp( self.key, other.key)
if not isinstance(other, self.__class__):
return self.__class__ == type(other)
return self.key == other.key
def __call__(self):
"""Return a strong reference to the bound method

View File

@ -446,7 +446,7 @@ class RegexField(CharField):
def _set_regex(self, regex):
if isinstance(regex, basestring):
regex = re.compile(regex)
regex = re.compile(regex, re.UNICODE)
self._regex = regex
if hasattr(self, '_regex_validator') and self._regex_validator in self.validators:
self.validators.remove(self._regex_validator)
@ -932,12 +932,16 @@ class FilePathField(ChoiceField):
self.choices.append((f, f.replace(path, "", 1)))
if self.allow_folders:
for f in dirs:
if f == '__pycache__':
continue
if self.match is None or self.match_re.search(f):
f = os.path.join(root, f)
self.choices.append((f, f.replace(path, "", 1)))
else:
try:
for f in sorted(os.listdir(self.path)):
if f == '__pycache__':
continue
full_file = os.path.join(self.path, f)
if (((self.allow_files and os.path.isfile(full_file)) or
(self.allow_folders and os.path.isdir(full_file))) and

View File

@ -388,10 +388,10 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
parent = (object,)
if hasattr(form, 'Meta'):
parent = (form.Meta, object)
Meta = type('Meta', parent, attrs)
Meta = type(b'Meta', parent, attrs)
# Give this new form class a reasonable name.
class_name = model.__name__ + 'Form'
class_name = model.__name__ + b'Form'
# Class attributes for the new form class.
form_class_attrs = {

View File

@ -18,7 +18,7 @@ _cookie_encodes_correctly = Cookie.SimpleCookie().value_encode(';') == (';', '"\
# See ticket #13007, http://bugs.python.org/issue2193 and http://trac.edgewall.org/ticket/2256
_tc = Cookie.SimpleCookie()
try:
_tc.load('foo:bar=1')
_tc.load(b'foo:bar=1')
_cookie_allows_colon_in_names = True
except Cookie.CookieError:
_cookie_allows_colon_in_names = False
@ -650,8 +650,8 @@ class HttpResponse(object):
def _get_content(self):
if self.has_header('Content-Encoding'):
return ''.join([str(e) for e in self._container])
return ''.join([smart_str(e, self._charset) for e in self._container])
return b''.join([str(e) for e in self._container])
return b''.join([smart_str(e, self._charset) for e in self._container])
def _set_content(self, value):
if hasattr(value, '__iter__'):

View File

@ -268,7 +268,7 @@ class LazyStream(object):
"""
self._producer = producer
self._empty = False
self._leftover = ''
self._leftover = b''
self.length = length
self.position = 0
self._remaining = length
@ -282,7 +282,7 @@ class LazyStream(object):
remaining = (size is not None and [size] or [self._remaining])[0]
# do the whole thing in one shot if no limit was provided.
if remaining is None:
yield ''.join(self)
yield b''.join(self)
return
# otherwise do some bookkeeping to return exactly enough
@ -298,7 +298,7 @@ class LazyStream(object):
remaining -= len(emitting)
yield emitting
out = ''.join(parts())
out = b''.join(parts())
return out
def next(self):
@ -311,7 +311,7 @@ class LazyStream(object):
"""
if self._leftover:
output = self._leftover
self._leftover = ''
self._leftover = b''
else:
output = next(self._producer)
self._unget_history = []
@ -341,7 +341,7 @@ class LazyStream(object):
return
self._update_unget_history(len(bytes))
self.position -= len(bytes)
self._leftover = ''.join([bytes, self._leftover])
self._leftover = b''.join([bytes, self._leftover])
def _update_unget_history(self, num_bytes):
"""
@ -459,7 +459,7 @@ class BoundaryIter(object):
if not chunks:
raise StopIteration()
chunk = ''.join(chunks)
chunk = b''.join(chunks)
boundary = self._find_boundary(chunk, len(chunk) < self._rollback)
if boundary:
@ -495,9 +495,9 @@ class BoundaryIter(object):
end = index
next = index + len(self._boundary)
# backup over CRLF
if data[max(0,end-1)] == '\n':
if data[max(0,end-1)] == b'\n':
end -= 1
if data[max(0,end-1)] == '\r':
if data[max(0,end-1)] == b'\r':
end -= 1
return end, next
@ -531,7 +531,7 @@ def parse_boundary_stream(stream, max_header_size):
# 'find' returns the top of these four bytes, so we'll
# need to munch them later to prevent them from polluting
# the payload.
header_end = chunk.find('\r\n\r\n')
header_end = chunk.find(b'\r\n\r\n')
def _parse_header(line):
main_value_pair, params = parse_header(line)
@ -557,7 +557,7 @@ def parse_boundary_stream(stream, max_header_size):
outdict = {}
# Eliminate blank lines
for line in header.split('\r\n'):
for line in header.split(b'\r\n'):
# This terminology ("main value" and "dictionary of
# parameters") is from the Python docs.
try:
@ -580,7 +580,7 @@ def parse_boundary_stream(stream, max_header_size):
class Parser(object):
def __init__(self, stream, boundary):
self._stream = stream
self._separator = '--' + boundary
self._separator = b'--' + boundary
def __iter__(self):
boundarystream = InterBoundaryIter(self._stream, self._separator)
@ -590,27 +590,27 @@ class Parser(object):
def parse_header(line):
""" Parse the header into a key-value. """
plist = _parse_header_params(';' + line)
plist = _parse_header_params(b';' + line)
key = plist.pop(0).lower()
pdict = {}
for p in plist:
i = p.find('=')
i = p.find(b'=')
if i >= 0:
name = p[:i].strip().lower()
value = p[i+1:].strip()
if len(value) >= 2 and value[0] == value[-1] == '"':
if len(value) >= 2 and value[0] == value[-1] == b'"':
value = value[1:-1]
value = value.replace('\\\\', '\\').replace('\\"', '"')
value = value.replace(b'\\\\', b'\\').replace(b'\\"', b'"')
pdict[name] = value
return key, pdict
def _parse_header_params(s):
plist = []
while s[:1] == ';':
while s[:1] == b';':
s = s[1:]
end = s.find(';')
while end > 0 and s.count('"', 0, end) % 2:
end = s.find(';', end + 1)
end = s.find(b';')
while end > 0 and s.count(b'"', 0, end) % 2:
end = s.find(b';', end + 1)
if end < 0:
end = len(s)
f = s[:end]

View File

@ -52,7 +52,7 @@ class Loader(BaseLoader):
def load_template_source(self, template_name, template_dirs=None):
for filepath in self.get_template_sources(template_name, template_dirs):
try:
with open(filepath) as fp:
with open(filepath, 'rb') as fp:
return (fp.read().decode(settings.FILE_CHARSET), filepath)
except IOError:
pass

View File

@ -34,7 +34,7 @@ class Loader(BaseLoader):
tried = []
for filepath in self.get_template_sources(template_name, template_dirs):
try:
with open(filepath) as fp:
with open(filepath, 'rb') as fp:
return (fp.read().decode(settings.FILE_CHARSET), filepath)
except IOError:
tried.append(filepath)

View File

@ -148,7 +148,7 @@ class BlockTranslateNode(Node):
context.pop()
try:
result = result % data
except KeyError:
except (KeyError, ValueError):
with translation.override(None):
result = self.render(context)
return result

View File

@ -155,7 +155,6 @@ def encode_file(boundary, key, file):
]
class RequestFactory(object):
"""
Class that lets you create mock Request objects for use in testing.
@ -227,7 +226,7 @@ class RequestFactory(object):
return urllib.unquote(parsed[2])
def get(self, path, data={}, **extra):
"Construct a GET request"
"Construct a GET request."
parsed = urlparse(path)
r = {
@ -270,49 +269,39 @@ class RequestFactory(object):
r.update(extra)
return self.request(**r)
def options(self, path, data={}, **extra):
"Constrict an OPTIONS request"
def options(self, path, data='', content_type='application/octet-stream',
**extra):
"Construct an OPTIONS request."
return self.generic('OPTIONS', path, data, content_type, **extra)
parsed = urlparse(path)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': urlencode(data, doseq=True) or parsed[4],
'REQUEST_METHOD': 'OPTIONS',
}
r.update(extra)
return self.request(**r)
def put(self, path, data={}, content_type=MULTIPART_CONTENT,
def put(self, path, data='', content_type='application/octet-stream',
**extra):
"Construct a PUT request."
return self.generic('PUT', path, data, content_type, **extra)
put_data = self._encode_data(data, content_type)
def delete(self, path, data='', content_type='application/octet-stream',
**extra):
"Construct a DELETE request."
return self.generic('DELETE', path, data, content_type, **extra)
def generic(self, method, path,
data='', content_type='application/octet-stream', **extra):
parsed = urlparse(path)
data = smart_str(data, settings.DEFAULT_CHARSET)
r = {
'CONTENT_LENGTH': len(put_data),
'CONTENT_TYPE': content_type,
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': parsed[4],
'REQUEST_METHOD': 'PUT',
'wsgi.input': FakePayload(put_data),
'REQUEST_METHOD': method,
}
if data:
r.update({
'CONTENT_LENGTH': len(data),
'CONTENT_TYPE': content_type,
'wsgi.input': FakePayload(data),
})
r.update(extra)
return self.request(**r)
def delete(self, path, data={}, **extra):
"Construct a DELETE request."
parsed = urlparse(path)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': urlencode(data, doseq=True) or parsed[4],
'REQUEST_METHOD': 'DELETE',
}
r.update(extra)
return self.request(**r)
class Client(RequestFactory):
"""
A class that can act as a client for testing purposes.
@ -445,30 +434,35 @@ class Client(RequestFactory):
response = self._handle_redirects(response, **extra)
return response
def options(self, path, data={}, follow=False, **extra):
def options(self, path, data='', content_type='application/octet-stream',
follow=False, **extra):
"""
Request a response from the server using OPTIONS.
"""
response = super(Client, self).options(path, data=data, **extra)
response = super(Client, self).options(path,
data=data, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response
def put(self, path, data={}, content_type=MULTIPART_CONTENT,
def put(self, path, data='', content_type='application/octet-stream',
follow=False, **extra):
"""
Send a resource to the server using PUT.
"""
response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
response = super(Client, self).put(path,
data=data, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response
def delete(self, path, data={}, follow=False, **extra):
def delete(self, path, data='', content_type='application/octet-stream',
follow=False, **extra):
"""
Send a DELETE request to the server.
"""
response = super(Client, self).delete(path, data=data, **extra)
response = super(Client, self).delete(path,
data=data, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response

View File

@ -23,7 +23,7 @@ import time
from django.conf import settings
from django.core.cache import get_cache
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
from django.utils.encoding import iri_to_uri, force_unicode
from django.utils.http import http_date
from django.utils.timezone import get_current_timezone_name
from django.utils.translation import get_language
@ -53,7 +53,7 @@ def patch_cache_control(response, **kwargs):
if t[1] is True:
return t[0]
else:
return t[0] + '=' + smart_str(t[1])
return '%s=%s' % (t[0], t[1])
if response.has_header('Cache-Control'):
cc = cc_delim_re.split(response['Cache-Control'])

View File

@ -8,6 +8,7 @@ import hashlib
import binascii
import operator
import time
from functools import reduce
# Use the system PRNG if possible
import random
@ -23,8 +24,8 @@ except NotImplementedError:
from django.conf import settings
_trans_5c = "".join([chr(x ^ 0x5C) for x in xrange(256)])
_trans_36 = "".join([chr(x ^ 0x36) for x in xrange(256)])
_trans_5c = b"".join([chr(x ^ 0x5C) for x in xrange(256)])
_trans_36 = b"".join([chr(x ^ 0x36) for x in xrange(256)])
def salted_hmac(key_salt, value, secret=None):
@ -148,11 +149,11 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
def F(i):
def U():
u = salt + struct.pack('>I', i)
u = salt + struct.pack(b'>I', i)
for j in xrange(int(iterations)):
u = _fast_hmac(password, u, digest).digest()
yield _bin_to_long(u)
return _long_to_bin(reduce(operator.xor, U()), hex_format_string)
T = [F(x) for x in range(1, l + 1)]
return ''.join(T[:-1]) + T[-1][:r]
return b''.join(T[:-1]) + T[-1][:r]

View File

@ -1,4 +1,3 @@
import types
import urllib
import locale
import datetime
@ -45,7 +44,7 @@ def is_protected_type(obj):
force_unicode(strings_only=True).
"""
return isinstance(obj, (
types.NoneType,
type(None),
int, long,
datetime.datetime, datetime.date, datetime.time,
float, Decimal)
@ -107,7 +106,7 @@ def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
If strings_only is True, don't convert (some) non-string-like objects.
"""
if strings_only and isinstance(s, (types.NoneType, int)):
if strings_only and (s is None or isinstance(s, int)):
return s
if isinstance(s, Promise):
return unicode(s).encode(encoding, errors)
@ -154,7 +153,7 @@ def iri_to_uri(iri):
# converted.
if iri is None:
return iri
return urllib.quote(smart_str(iri), safe="/#%[]=:;$&()+,!?*@'~")
return urllib.quote(smart_str(iri), safe=b"/#%[]=:;$&()+,!?*@'~")
def filepath_to_uri(path):
"""Convert an file system path to a URI portion that is suitable for
@ -173,7 +172,7 @@ def filepath_to_uri(path):
return path
# I know about `os.sep` and `os.altsep` but I want to leave
# some flexibility for hardcoding separators.
return urllib.quote(smart_str(path).replace("\\", "/"), safe="/~!*()'")
return urllib.quote(smart_str(path).replace("\\", "/"), safe=b"/~!*()'")
# The encoding of the default system locale but falls back to the
# given fallback encoding if the encoding is unsupported by python or could

View File

@ -58,6 +58,7 @@ def lazy(func, *resultclasses):
function is evaluated on every access.
"""
@total_ordering
class __proxy__(Promise):
"""
Encapsulate a function call and act as a proxy for methods that are
@ -124,17 +125,23 @@ def lazy(func, *resultclasses):
def __str_cast(self):
return str(func(*self.__args, **self.__kw))
def __cmp__(self, rhs):
def __cast(self):
if self._delegate_str:
s = str(func(*self.__args, **self.__kw))
return self.__str_cast()
elif self._delegate_unicode:
s = unicode(func(*self.__args, **self.__kw))
return self.__unicode_cast()
else:
s = func(*self.__args, **self.__kw)
if isinstance(rhs, Promise):
return -cmp(rhs, s)
else:
return cmp(s, rhs)
return func(*self.__args, **self.__kw)
def __eq__(self, other):
if isinstance(other, Promise):
other = other.__cast()
return self.__cast() == other
def __lt__(self, other):
if isinstance(other, Promise):
other = other.__cast()
return self.__cast() < other
def __mod__(self, rhs):
if self._delegate_str:

View File

@ -116,7 +116,7 @@ def smart_urlquote(url):
# contains a % not followed by two hexadecimal digits. See #9655.
if '%' not in url or unquoted_percents_re.search(url):
# See http://bugs.python.org/issue2637
url = urllib.quote(smart_str(url), safe='!*\'();:@&=+$,/?#[]~')
url = urllib.quote(smart_str(url), safe=b'!*\'();:@&=+$,/?#[]~')
return force_unicode(url)

View File

@ -155,9 +155,20 @@ class SafeExceptionReporterFilter(ExceptionReporterFilter):
Replaces the values of variables marked as sensitive with
stars (*********).
"""
func_name = tb_frame.f_code.co_name
func = tb_frame.f_globals.get(func_name)
sensitive_variables = getattr(func, 'sensitive_variables', [])
# Loop through the frame's callers to see if the sensitive_variables
# decorator was used.
current_frame = tb_frame.f_back
sensitive_variables = None
while current_frame is not None:
if (current_frame.f_code.co_name == 'sensitive_variables_wrapper'
and 'sensitive_variables_wrapper' in current_frame.f_locals):
# The sensitive_variables decorator was used, so we take note
# of the sensitive variables' names.
wrapper = current_frame.f_locals['sensitive_variables_wrapper']
sensitive_variables = getattr(wrapper, 'sensitive_variables', None)
break
current_frame = current_frame.f_back
cleansed = []
if self.is_active(request) and sensitive_variables:
if sensitive_variables == '__ALL__':
@ -333,7 +344,7 @@ class ExceptionReporter(object):
source = source.splitlines()
if source is None:
try:
with open(filename) as fp:
with open(filename, 'rb') as fp:
source = fp.readlines()
except (OSError, IOError):
pass

View File

@ -26,13 +26,13 @@ def sensitive_variables(*variables):
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
def sensitive_variables_wrapper(*args, **kwargs):
if variables:
wrapper.sensitive_variables = variables
sensitive_variables_wrapper.sensitive_variables = variables
else:
wrapper.sensitive_variables = '__ALL__'
sensitive_variables_wrapper.sensitive_variables = '__ALL__'
return func(*args, **kwargs)
return wrapper
return sensitive_variables_wrapper
return decorator
@ -61,11 +61,11 @@ def sensitive_post_parameters(*parameters):
"""
def decorator(view):
@functools.wraps(view)
def wrapper(request, *args, **kwargs):
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
if parameters:
request.sensitive_post_parameters = parameters
else:
request.sensitive_post_parameters = '__ALL__'
return view(request, *args, **kwargs)
return wrapper
return sensitive_post_parameters_wrapper
return decorator

View File

@ -79,14 +79,22 @@ class View(object):
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
extra={
'status_code': 405,
'request': self.request
}
)
return http.HttpResponseNotAllowed(allowed_methods)
return http.HttpResponseNotAllowed(self._allowed_methods())
def options(self, request, *args, **kwargs):
response = http.HttpResponse()
response['Allow'] = ', '.join(self._allowed_methods())
response['Content-Length'] = 0
return response
def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]
class TemplateResponseMixin(object):

View File

@ -23,7 +23,9 @@ class YearMixin(object):
return self.year_format
def get_year(self):
"Return the year for which this view should display data"
"""
Return the year for which this view should display data.
"""
year = self.year
if year is None:
try:
@ -35,6 +37,32 @@ class YearMixin(object):
raise Http404(_(u"No year specified"))
return year
def get_next_year(self, date):
"""
Get the next valid year.
"""
return _get_next_prev(self, date, is_previous=False, period='year')
def get_previous_year(self, date):
"""
Get the previous valid year.
"""
return _get_next_prev(self, date, is_previous=True, period='year')
def _get_next_year(self, date):
"""
Return the start date of the next interval.
The interval is defined by start date <= item date < next start date.
"""
return date.replace(year=date.year + 1, month=1, day=1)
def _get_current_year(self, date):
"""
Return the start date of the current interval.
"""
return date.replace(month=1, day=1)
class MonthMixin(object):
month_format = '%b'
@ -48,7 +76,9 @@ class MonthMixin(object):
return self.month_format
def get_month(self):
"Return the month for which this view should display data"
"""
Return the month for which this view should display data.
"""
month = self.month
if month is None:
try:
@ -64,20 +94,30 @@ class MonthMixin(object):
"""
Get the next valid month.
"""
# next must be the first day of the next month.
if date.month == 12:
next = date.replace(year=date.year + 1, month=1, day=1)
else:
next = date.replace(month=date.month + 1, day=1)
return _get_next_prev(self, next, is_previous=False, period='month')
return _get_next_prev(self, date, is_previous=False, period='month')
def get_previous_month(self, date):
"""
Get the previous valid month.
"""
# prev must be the last day of the previous month.
prev = date.replace(day=1) - datetime.timedelta(days=1)
return _get_next_prev(self, prev, is_previous=True, period='month')
return _get_next_prev(self, date, is_previous=True, period='month')
def _get_next_month(self, date):
"""
Return the start date of the next interval.
The interval is defined by start date <= item date < next start date.
"""
if date.month == 12:
return date.replace(year=date.year + 1, month=1, day=1)
else:
return date.replace(month=date.month + 1, day=1)
def _get_current_month(self, date):
"""
Return the start date of the previous interval.
"""
return date.replace(day=1)
class DayMixin(object):
@ -92,7 +132,9 @@ class DayMixin(object):
return self.day_format
def get_day(self):
"Return the day for which this view should display data"
"""
Return the day for which this view should display data.
"""
day = self.day
if day is None:
try:
@ -108,15 +150,27 @@ class DayMixin(object):
"""
Get the next valid day.
"""
next = date + datetime.timedelta(days=1)
return _get_next_prev(self, next, is_previous=False, period='day')
return _get_next_prev(self, date, is_previous=False, period='day')
def get_previous_day(self, date):
"""
Get the previous valid day.
"""
prev = date - datetime.timedelta(days=1)
return _get_next_prev(self, prev, is_previous=True, period='day')
return _get_next_prev(self, date, is_previous=True, period='day')
def _get_next_day(self, date):
"""
Return the start date of the next interval.
The interval is defined by start date <= item date < next start date.
"""
return date + datetime.timedelta(days=1)
def _get_current_day(self, date):
"""
Return the start date of the current interval.
"""
return date
class WeekMixin(object):
@ -131,7 +185,9 @@ class WeekMixin(object):
return self.week_format
def get_week(self):
"Return the week for which this view should display data"
"""
Return the week for which this view should display data
"""
week = self.week
if week is None:
try:
@ -147,19 +203,34 @@ class WeekMixin(object):
"""
Get the next valid week.
"""
# next must be the first day of the next week.
next = date + datetime.timedelta(days=7 - self._get_weekday(date))
return _get_next_prev(self, next, is_previous=False, period='week')
return _get_next_prev(self, date, is_previous=False, period='week')
def get_previous_week(self, date):
"""
Get the previous valid week.
"""
# prev must be the last day of the previous week.
prev = date - datetime.timedelta(days=self._get_weekday(date) + 1)
return _get_next_prev(self, prev, is_previous=True, period='week')
return _get_next_prev(self, date, is_previous=True, period='week')
def _get_next_week(self, date):
"""
Return the start date of the next interval.
The interval is defined by start date <= item date < next start date.
"""
return date + datetime.timedelta(days=7 - self._get_weekday(date))
def _get_current_week(self, date):
"""
Return the start date of the current interval.
"""
return date - datetime.timedelta(self._get_weekday(date))
def _get_weekday(self, date):
"""
Return the weekday for a given date.
The first day according to the week format is 0 and the last day is 6.
"""
week_format = self.get_week_format()
if week_format == '%W': # week starts on Monday
return date.weekday()
@ -168,6 +239,7 @@ class WeekMixin(object):
else:
raise ValueError("unknown week format: %s" % week_format)
class DateMixin(object):
"""
Mixin class for views manipulating date-based data.
@ -255,7 +327,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
"""
raise NotImplementedError('A DateView must provide an implementation of get_dated_items()')
def get_dated_queryset(self, **lookup):
def get_dated_queryset(self, ordering=None, **lookup):
"""
Get a queryset properly filtered according to `allow_future` and any
extra lookup kwargs.
@ -266,14 +338,17 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
allow_empty = self.get_allow_empty()
paginate_by = self.get_paginate_by(qs)
if ordering is not None:
qs = qs.order_by(ordering)
if not allow_future:
now = timezone.now() if self.uses_datetime_field else datetime.date.today()
now = timezone.now() if self.uses_datetime_field else timezone_today()
qs = qs.filter(**{'%s__lte' % date_field: now})
if not allow_empty:
# When pagination is enabled, it's better to do a cheap query
# than to load the unpaginated queryset in memory.
is_empty = not bool(qs) if paginate_by is None else not qs.exists()
is_empty = len(qs) == 0 if paginate_by is None else not qs.exists()
if is_empty:
raise Http404(_(u"No %(verbose_name_plural)s available") % {
'verbose_name_plural': force_unicode(qs.model._meta.verbose_name_plural)
@ -310,15 +385,13 @@ class BaseArchiveIndexView(BaseDateListView):
"""
Return (date_list, items, extra_context) for this request.
"""
qs = self.get_dated_queryset()
qs = self.get_dated_queryset(ordering='-%s' % self.get_date_field())
date_list = self.get_date_list(qs, 'year')
if date_list:
object_list = qs.order_by('-' + self.get_date_field())
else:
object_list = qs.none()
if not date_list:
qs = qs.none()
return (date_list, object_list, {})
return (date_list, qs, {})
class ArchiveIndexView(MultipleObjectTemplateResponseMixin, BaseArchiveIndexView):
@ -344,23 +417,25 @@ class BaseYearArchiveView(YearMixin, BaseDateListView):
date = _date_from_string(year, self.get_year_format())
since = self._make_date_lookup_arg(date)
until = self._make_date_lookup_arg(datetime.date(date.year + 1, 1, 1))
until = self._make_date_lookup_arg(self._get_next_year(date))
lookup_kwargs = {
'%s__gte' % date_field: since,
'%s__lt' % date_field: until,
}
qs = self.get_dated_queryset(**lookup_kwargs)
qs = self.get_dated_queryset(ordering='-%s' % date_field, **lookup_kwargs)
date_list = self.get_date_list(qs, 'month')
if self.get_make_object_list():
object_list = qs.order_by('-' + date_field)
else:
if not self.get_make_object_list():
# We need this to be a queryset since parent classes introspect it
# to find information about the model.
object_list = qs.none()
qs = qs.none()
return (date_list, object_list, {'year': year})
return (date_list, qs, {
'year': date,
'next_year': self.get_next_year(date),
'previous_year': self.get_previous_year(date),
})
def get_make_object_list(self):
"""
@ -392,12 +467,8 @@ class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView):
date = _date_from_string(year, self.get_year_format(),
month, self.get_month_format())
# Construct a date-range lookup.
since = self._make_date_lookup_arg(date)
if date.month == 12:
until = self._make_date_lookup_arg(datetime.date(date.year + 1, 1, 1))
else:
until = self._make_date_lookup_arg(datetime.date(date.year, date.month + 1, 1))
until = self._make_date_lookup_arg(self._get_next_month(date))
lookup_kwargs = {
'%s__gte' % date_field: since,
'%s__lt' % date_field: until,
@ -442,9 +513,8 @@ class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView):
week_start, '%w',
week, week_format)
# Construct a date-range lookup.
since = self._make_date_lookup_arg(date)
until = self._make_date_lookup_arg(date + datetime.timedelta(days=7))
until = self._make_date_lookup_arg(self._get_next_week(date))
lookup_kwargs = {
'%s__gte' % date_field: since,
'%s__lt' % date_field: until,
@ -585,22 +655,22 @@ def _date_from_string(year, year_format, month='', month_format='', day='', day_
})
def _get_next_prev(generic_view, naive_result, is_previous, period):
def _get_next_prev(generic_view, date, is_previous, period):
"""
Helper: Get the next or the previous valid date. The idea is to allow
links on month/day views to never be 404s by never providing a date
that'll be invalid for the given view.
This is a bit complicated since it handles both next and previous months
and days (for MonthArchiveView and DayArchiveView); hence the coupling to generic_view.
This is a bit complicated since it handles different intervals of time,
hence the coupling to generic_view.
However in essence the logic comes down to:
* If allow_empty and allow_future are both true, this is easy: just
return the naive result (just the next/previous day or month,
return the naive result (just the next/previous day/week/month,
reguardless of object existence.)
* If allow_empty is true, allow_future is false, and the naive month
* If allow_empty is true, allow_future is false, and the naive result
isn't in the future, then return it; otherwise return None.
* If allow_empty is false and allow_future is true, return the next
@ -616,9 +686,23 @@ def _get_next_prev(generic_view, naive_result, is_previous, period):
allow_empty = generic_view.get_allow_empty()
allow_future = generic_view.get_allow_future()
# If allow_empty is True the naive value will be valid
get_current = getattr(generic_view, '_get_current_%s' % period)
get_next = getattr(generic_view, '_get_next_%s' % period)
# Bounds of the current interval
start, end = get_current(date), get_next(date)
# If allow_empty is True, the naive result will be valid
if allow_empty:
result = naive_result
if is_previous:
result = get_current(start - datetime.timedelta(days=1))
else:
result = end
if allow_future or result <= timezone_today():
return result
else:
return None
# Otherwise, we'll need to go to the database to look for an object
# whose date_field is at least (greater than/less than) the given
@ -627,12 +711,22 @@ def _get_next_prev(generic_view, naive_result, is_previous, period):
# Construct a lookup and an ordering depending on whether we're doing
# a previous date or a next date lookup.
if is_previous:
lookup = {'%s__lte' % date_field: generic_view._make_date_lookup_arg(naive_result)}
lookup = {'%s__lt' % date_field: generic_view._make_date_lookup_arg(start)}
ordering = '-%s' % date_field
else:
lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(naive_result)}
lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(end)}
ordering = date_field
# Filter out objects in the future if appropriate.
if not allow_future:
# Fortunately, to match the implementation of allow_future,
# we need __lte, which doesn't conflict with __lt above.
if generic_view.uses_datetime_field:
now = timezone.now()
else:
now = timezone_today()
lookup['%s__lte' % date_field] = now
qs = generic_view.get_queryset().filter(**lookup).order_by(ordering)
# Snag the first object from the queryset; if it doesn't exist that
@ -640,26 +734,23 @@ def _get_next_prev(generic_view, naive_result, is_previous, period):
try:
result = getattr(qs[0], date_field)
except IndexError:
result = None
return None
# Convert datetimes to a dates
if result and generic_view.uses_datetime_field:
if settings.USE_TZ:
result = timezone.localtime(result)
result = result.date()
# Convert datetimes to dates in the current time zone.
if generic_view.uses_datetime_field:
if settings.USE_TZ:
result = timezone.localtime(result)
result = result.date()
if result:
if period == 'month':
# first day of the month
result = result.replace(day=1)
elif period == 'week':
# monday of the week
result = result - datetime.timedelta(days=generic_view._get_weekday(result))
elif period != 'day':
raise ValueError('invalid period: %s' % period)
# Return the first day of the period.
return get_current(result)
# Check against future dates.
if result and (allow_future or result < datetime.date.today()):
return result
def timezone_today():
"""
Return the current date in the current time zone.
"""
if settings.USE_TZ:
return timezone.localtime(timezone.now()).date()
else:
return None
return datetime.date.today()

View File

@ -1,6 +1,5 @@
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import Http404
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
@ -81,7 +80,7 @@ class SingleObjectMixin(ContextMixin):
if self.context_object_name:
return self.context_object_name
elif hasattr(obj, '_meta'):
return smart_str(obj._meta.object_name.lower())
return obj._meta.object_name.lower()
else:
return None
@ -108,7 +107,7 @@ class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if get_template is overridden.
a list. May not be called if render_to_response is overridden.
"""
try:
names = super(SingleObjectTemplateResponseMixin, self).get_template_names()

View File

@ -1,7 +1,6 @@
from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
@ -16,7 +15,7 @@ class MultipleObjectMixin(ContextMixin):
def get_queryset(self):
"""
Get the list of items for this view. This must be an interable, and may
Get the list of items for this view. This must be an iterable, and may
be a queryset (in which qs-specific behavior will be enabled).
"""
if self.queryset is not None:
@ -77,7 +76,7 @@ class MultipleObjectMixin(ContextMixin):
if self.context_object_name:
return self.context_object_name
elif hasattr(object_list, 'model'):
return smart_str('%s_list' % object_list.model._meta.object_name.lower())
return '%s_list' % object_list.model._meta.object_name.lower()
else:
return None
@ -113,9 +112,19 @@ class BaseListView(MultipleObjectMixin, View):
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty and len(self.object_list) == 0:
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
% {'class_name': self.__class__.__name__})
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if (self.get_paginate_by(self.object_list) is not None
and hasattr(self.object_list, 'exists')):
is_empty = not self.object_list.exists()
else:
is_empty = len(self.object_list) == 0
if is_empty:
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
% {'class_name': self.__class__.__name__})
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
@ -126,7 +135,7 @@ class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if get_template is overridden.
a list. May not be called if render_to_response is overridden.
"""
try:
names = super(MultipleObjectTemplateResponseMixin, self).get_template_names()

View File

@ -61,7 +61,7 @@ look like this:
poll.opened = False
poll.save()
self.stdout.write('Successfully closed poll "%s"\n' % poll_id)
self.stdout.write('Successfully closed poll "%s"' % poll_id)
.. note::
When you are using management commands and wish to provide console
@ -317,8 +317,12 @@ Exception class indicating a problem while executing a management
command.
If this exception is raised during the execution of a management
command, it will be caught and turned into a nicely-printed error
message to the appropriate output stream (i.e., stderr); as a
result, raising this exception (with a sensible description of the
command from a command line console, it will be caught and turned into a
nicely-printed error message to the appropriate output stream (i.e., stderr);
as a result, raising this exception (with a sensible description of the
error) is the preferred way to indicate that something has gone
wrong in the execution of a command.
If a management command is called from code through
:ref:`call_command <call-command>`, it's up to you to catch the exception
when needed.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -13,9 +13,19 @@ It'll consist of two parts:
* An admin site that lets you add, change and delete polls.
We'll assume you have :doc:`Django installed </intro/install>` already. You can
tell Django is installed by running the Python interactive interpreter and
typing ``import django``. If that command runs successfully, with no errors,
Django is installed.
tell Django is installed and which version by running the following command:
.. code-block:: bash
python -c "import django; print(django.get_version())"
You should see either the version of your Django installation or an error
telling "No module named django". Check also that the version number matches
the version of this tutorial. If they don't match, you can refer to the
tutorial for your version of Django or update Django to the newest version.
See :doc:`How to install Django </topics/install>` for advice on how to remove
older versions of Django and install a newer one.
.. admonition:: Where to get help:
@ -339,9 +349,10 @@ The first step in writing a database Web app in Django is to define your models
the :ref:`DRY Principle <dry>`. The goal is to define your data model in one
place and automatically derive things from it.
In our simple poll app, we'll create two models: polls and choices. A poll has
a question and a publication date. A choice has two fields: the text of the
choice and a vote tally. Each choice is associated with a poll.
In our simple poll app, we'll create two models: ``Poll`` and ``Choice``.
A ``Poll`` has a question and a publication date. A ``Choice`` has two fields:
the text of the choice and a vote tally. Each ``Choice`` is associated with a
``Poll``.
These concepts are represented by simple Python classes. Edit the
:file:`polls/models.py` file so it looks like this::
@ -354,7 +365,7 @@ These concepts are represented by simple Python classes. Edit the
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField()
The code is straightforward. Each model is represented by a class that
@ -384,8 +395,8 @@ Some :class:`~django.db.models.Field` classes have required elements.
schema, but in validation, as we'll soon see.
Finally, note a relationship is defined, using
:class:`~django.db.models.ForeignKey`. That tells Django each Choice is related
to a single Poll. Django supports all the common database relationships:
:class:`~django.db.models.ForeignKey`. That tells Django each ``Choice`` is related
to a single ``Poll``. Django supports all the common database relationships:
many-to-ones, many-to-manys and one-to-ones.
.. _`Python path`: http://docs.python.org/tutorial/modules.html#the-module-search-path
@ -397,7 +408,7 @@ That small bit of model code gives Django a lot of information. With it, Django
is able to:
* Create a database schema (``CREATE TABLE`` statements) for this app.
* Create a Python database-access API for accessing Poll and Choice objects.
* Create a Python database-access API for accessing ``Poll`` and ``Choice`` objects.
But first we need to tell our project that the ``polls`` app is installed.
@ -446,7 +457,7 @@ statements for the polls app):
CREATE TABLE "polls_choice" (
"id" serial NOT NULL PRIMARY KEY,
"poll_id" integer NOT NULL REFERENCES "polls_poll" ("id") DEFERRABLE INITIALLY DEFERRED,
"choice" varchar(200) NOT NULL,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL
);
COMMIT;
@ -597,7 +608,7 @@ of this object. Let's fix that by editing the polls model (in the
class Choice(models.Model):
# ...
def __unicode__(self):
return self.choice
return self.choice_text
It's important to add :meth:`~django.db.models.Model.__unicode__` methods to
your models, not only for your own sanity when dealing with the interactive
@ -678,7 +689,7 @@ Save these changes and start a new Python interactive shell by running
True
# Give the Poll a couple of Choices. The create call constructs a new
# choice object, does the INSERT statement, adds the choice to the set
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a poll's choices) which can be accessed via the API.
@ -689,11 +700,11 @@ Save these changes and start a new Python interactive shell by running
[]
# Create three choices.
>>> p.choice_set.create(choice='Not much', votes=0)
>>> p.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice='The sky', votes=0)
>>> p.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice='Just hacking again', votes=0)
>>> c = p.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Poll objects.
>>> c.poll
@ -713,7 +724,7 @@ Save these changes and start a new Python interactive shell by running
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
# Let's delete one of the choices. Use delete() for that.
>>> c = p.choice_set.filter(choice__startswith='Just hacking')
>>> c = p.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
For more information on model relations, see :doc:`Accessing related objects

View File

@ -276,11 +276,11 @@ in that window and click "Save," Django will save the poll to the database and
dynamically add it as the selected choice on the "Add choice" form you're
looking at.
But, really, this is an inefficient way of adding Choice objects to the system.
But, really, this is an inefficient way of adding ``Choice`` objects to the system.
It'd be better if you could add a bunch of Choices directly when you create the
Poll object. Let's make that happen.
``Poll`` object. Let's make that happen.
Remove the ``register()`` call for the Choice model. Then, edit the ``Poll``
Remove the ``register()`` call for the ``Choice`` model. Then, edit the ``Poll``
registration code to read::
class ChoiceInline(admin.StackedInline):
@ -296,7 +296,7 @@ registration code to read::
admin.site.register(Poll, PollAdmin)
This tells Django: "Choice objects are edited on the Poll admin page. By
This tells Django: "``Choice`` objects are edited on the ``Poll`` admin page. By
default, provide enough fields for 3 choices."
Load the "Add poll" page to see how that looks, you may need to restart your development server:
@ -309,7 +309,7 @@ by ``extra`` -- and each time you come back to the "Change" page for an
already-created object, you get another three extra slots.
One small problem, though. It takes a lot of screen space to display all the
fields for entering related Choice objects. For that reason, Django offers a
fields for entering related ``Choice`` objects. For that reason, Django offers a
tabular way of displaying inline related objects; you just need to change
the ``ChoiceInline`` declaration to read::
@ -397,7 +397,7 @@ search terms, Django will search the ``question`` field. You can use as many
fields as you'd like -- although because it uses a ``LIKE`` query behind the
scenes, keep it reasonable, to keep your database happy.
Finally, because Poll objects have dates, it'd be convenient to be able to
Finally, because ``Poll`` objects have dates, it'd be convenient to be able to
drill down by date. Add this line::
date_hierarchy = 'pub_date'

View File

@ -400,7 +400,7 @@ like:
<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
<li>{{ choice.choice }}</li>
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
@ -412,7 +412,7 @@ list-index lookup.
Method-calling happens in the :ttag:`{% for %}<for>` loop:
``poll.choice_set.all`` is interpreted as the Python code
``poll.choice_set.all()``, which returns an iterable of Choice objects and is
``poll.choice_set.all()``, which returns an iterable of ``Choice`` objects and is
suitable for use in the :ttag:`{% for %}<for>` tag.
See the :doc:`template guide </topics/templates>` for more about templates.

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