From 61ef97eb9492edf051c6c8acf79e01446a0f3a32 Mon Sep 17 00:00:00 2001 From: Allen Short Date: Mon, 24 Mar 2014 14:00:27 -0700 Subject: [PATCH] Upgrade to Django 1.6. This reverts commit 62d75b2ab9d91a3ffffdf67e5ca70e261143ef1d. --- apps/addons/forms.py | 3 -- apps/addons/models.py | 6 +-- apps/addons/query.py | 29 ++++++++--- apps/addons/tests/test_models.py | 19 +++++--- apps/amo/forms.py | 2 +- apps/amo/models.py | 4 +- apps/amo/templates/amo/robots.html | 2 +- apps/amo/tests/test_helpers.py | 2 +- apps/amo/tests/test_readonly.py | 3 -- apps/amo/tests/test_send_mail.py | 3 +- apps/amo/utils.py | 4 +- apps/api/tests/test_urls.py | 15 ++++++ apps/api/tests/test_views.py | 3 +- apps/browse/views.py | 12 ++--- apps/devhub/forms.py | 7 ++- apps/devhub/models.py | 2 +- apps/devhub/tests/test_forms.py | 1 + apps/discovery/tests/test_views.py | 3 +- apps/editors/helpers.py | 2 +- apps/editors/models.py | 8 +-- apps/editors/tests/test_views_themes.py | 8 +-- apps/files/tests/test_views.py | 2 +- apps/search/tests/test_views.py | 3 +- apps/stats/db.py | 5 +- apps/stats/views.py | 4 +- apps/translations/fields.py | 6 ++- apps/translations/query.py | 23 +++++---- apps/users/models.py | 6 ++- apps/users/templates/users/edit.html | 2 +- apps/users/templates/users/login_help.html | 2 +- apps/users/templates/users/mobile/login.html | 2 +- apps/users/tests/test_forms.py | 23 +++------ apps/users/tests/test_models.py | 9 +++- apps/users/tests/test_views.py | 4 +- apps/users/urls.py | 6 +-- apps/users/views.py | 8 +-- apps/zadmin/tests/test_views.py | 2 +- lib/misc/safe_signals.py | 51 -------------------- lib/settings_base.py | 3 ++ manage.py | 16 ++---- mkt/api/tests/test_oauth.py | 7 ++- mkt/developers/forms.py | 6 +-- mkt/developers/tasks.py | 1 + mkt/developers/tests/test_commands.py | 4 +- mkt/developers/tests/test_forms.py | 4 +- mkt/developers/tests/test_views_api.py | 2 +- mkt/developers/tests/test_views_edit.py | 2 +- mkt/developers/views.py | 4 +- mkt/search/tests/test_api.py | 3 +- mkt/submit/forms.py | 6 +-- mkt/submit/tests/test_forms.py | 1 - mkt/submit/tests/test_views.py | 3 +- mkt/submit/views.py | 2 - mkt/webapps/models.py | 4 +- mkt/zadmin/tests/test_views.py | 2 +- requirements/dev.txt | 2 +- requirements/prod.txt | 16 +++--- services/pfs.py | 2 - services/theme_update.py | 3 -- services/update.py | 3 +- services/utils.py | 3 -- services/verify.py | 2 - settings_test.py | 4 +- sites/altdev/settings_base.py | 8 --- sites/dev/settings_base.py | 8 --- sites/identitystage/settings_base.py | 8 --- sites/landfill/settings_base.py | 11 ----- sites/prod/settings_base.py | 8 --- sites/stage/settings_base.py | 8 --- 69 files changed, 186 insertions(+), 266 deletions(-) create mode 100644 apps/api/tests/test_urls.py delete mode 100644 lib/misc/safe_signals.py diff --git a/apps/addons/forms.py b/apps/addons/forms.py index c70da334965..69dbb96e777 100644 --- a/apps/addons/forms.py +++ b/apps/addons/forms.py @@ -296,9 +296,6 @@ def __init__(self, *args, **kw): cats = dict(self.addon.app_categories).get(app, []) self.initial.append({'categories': [c.id for c in cats]}) - # Reconstruct the forms according to the initial data. - self._construct_forms() - for app, form in zip(apps, self.forms): key = app.id if app else None form.request = self.request diff --git a/apps/addons/models.py b/apps/addons/models.py index 54a40fa3a03..47583d90ece 100644 --- a/apps/addons/models.py +++ b/apps/addons/models.py @@ -890,7 +890,7 @@ def compatible_version(self, app_id, app_version=None, platform=None, version_id = 0 log.debug(u'Caching compat version %s => %s' % (cache_key, version_id)) - cache.set(cache_key, version_id, 0) + cache.set(cache_key, version_id, None) return version @@ -1356,7 +1356,7 @@ def tags_partitioned_by_developer(self): """Returns a tuple of developer tags and user tags for this addon.""" tags = self.tags.not_blacklisted() if self.is_persona: - return models.query.EmptyQuerySet(), tags + return [], tags user_tags = tags.exclude(addon_tags__user__in=self.listed_authors) dev_tags = tags.exclude(id__in=[t.id for t in user_tags]) return dev_tags, user_tags @@ -1713,7 +1713,7 @@ def check_ownership(self, request, require_owner, require_author, class AddonDeviceType(amo.models.ModelBase): - addon = models.ForeignKey(Addon) + addon = models.ForeignKey(Addon, db_constraint=False) device_type = models.PositiveIntegerField( default=amo.DEVICE_DESKTOP, choices=do_dictsort(amo.DEVICE_TYPES), db_index=True) diff --git a/apps/addons/query.py b/apps/addons/query.py index 7a2c941886e..d6179687893 100644 --- a/apps/addons/query.py +++ b/apps/addons/query.py @@ -77,11 +77,12 @@ def get_from_clause(self): qn2 = self.connection.ops.quote_name index_map = self.query.index_map first = True + from_params = [] for alias in self.query.tables: if not self.query.alias_refcount[alias]: continue try: - name, alias, join_type, lhs, lhs_col, col, nullable = self.query.alias_map[alias] + name, alias, join_type, lhs, join_cols, _, join_field = self.query.alias_map[alias] except KeyError: # Extra tables can end up in self.tables, but not in the # alias_map if they aren't in a join. That's OK. We skip them. @@ -93,13 +94,25 @@ def get_from_clause(self): else: use_index = '' if join_type and not first: - # If you really need a LEFT OUTER JOIN, file a bug. - join_type = 'INNER JOIN' - result.append('%s %s%s %s ON (%s.%s = %s.%s)' - % (join_type, qn(name), alias_str, use_index, qn(lhs), - qn2(lhs_col), qn(alias), qn2(col))) + extra_cond = join_field.get_extra_restriction( + self.query.where_class, alias, lhs) + if extra_cond: + extra_sql, extra_params = extra_cond.as_sql( + qn, self.connection) + extra_sql = 'AND (%s)' % extra_sql + from_params.extend(extra_params) + else: + extra_sql = "" + result.append('%s %s%s %s ON (' + % (join_type, qn(name), alias_str, use_index)) + for index, (lhs_col, rhs_col) in enumerate(join_cols): + if index != 0: + result.append(' AND ') + result.append('%s.%s = %s.%s' % + (qn(lhs), qn2(lhs_col), qn(alias), qn2(rhs_col))) + result.append('%s)' % extra_sql) else: - connector = not first and ', ' or '' + connector = connector = '' if first else ', ' result.append('%s%s%s %s' % (connector, qn(name), alias_str, use_index)) ### jbalogh out. ### first = False @@ -112,4 +125,4 @@ def get_from_clause(self): connector = not first and ', ' or '' result.append('%s%s' % (connector, qn(alias))) first = False - return result, [] + return result, from_params diff --git a/apps/addons/tests/test_models.py b/apps/addons/tests/test_models.py index 9a0e4f4becb..de87546095a 100644 --- a/apps/addons/tests/test_models.py +++ b/apps/addons/tests/test_models.py @@ -2200,29 +2200,32 @@ def setUp(self): def test_extract(self): File.objects.create(platform=self.platform_mob, version=self.version, - filename=self.xpi_path('langpack-localepicker')) - assert self.addon.get_localepicker() + filename=self.xpi_path('langpack-localepicker'), + status=amo.STATUS_PUBLIC) + assert self.addon.reload().get_localepicker() assert 'title=Select a language' in self.addon.get_localepicker() def test_extract_no_file(self): File.objects.create(platform=self.platform_mob, version=self.version, - filename=self.xpi_path('langpack')) - eq_(self.addon.get_localepicker(), '') + filename=self.xpi_path('langpack'), status=amo.STATUS_PUBLIC) + eq_(self.addon.reload().get_localepicker(), '') def test_extract_no_files(self): eq_(self.addon.get_localepicker(), '') def test_extract_not_language_pack(self): File.objects.create(platform=self.platform_mob, version=self.version, - filename=self.xpi_path('langpack-localepicker')) - assert self.addon.get_localepicker() + filename=self.xpi_path('langpack-localepicker'), + status=amo.STATUS_PUBLIC) + assert self.addon.reload().get_localepicker() self.addon.update(type=amo.ADDON_EXTENSION) eq_(self.addon.get_localepicker(), '') def test_extract_not_platform_mobile(self): File.objects.create(platform=self.platform_all, version=self.version, - filename=self.xpi_path('langpack-localepicker')) - eq_(self.addon.get_localepicker(), '') + filename=self.xpi_path('langpack-localepicker'), + status=amo.STATUS_PUBLIC) + eq_(self.addon.reload().get_localepicker(), '') class TestMarketplace(amo.tests.TestCase): diff --git a/apps/amo/forms.py b/apps/amo/forms.py index 3c4772116da..515ade24fc5 100644 --- a/apps/amo/forms.py +++ b/apps/amo/forms.py @@ -37,7 +37,7 @@ def _get_changed_data(self): """ Model = self._meta.model if self._changed_data is None: - changed = copy(super(AMOModelForm, self)._get_changed_data()) + changed = copy(forms.ModelForm.changed_data.__get__(self)) fieldnames = [f.name for f in Model._meta.fields] fields = [(name, Model._meta.get_field(name)) for name in changed if name in fieldnames] diff --git a/apps/amo/models.py b/apps/amo/models.py index 6f4e78c03e7..10ab38a82ae 100644 --- a/apps/amo/models.py +++ b/apps/amo/models.py @@ -163,8 +163,8 @@ class ManagerBase(caching.base.CachingManager, UncachedManagerBase): function. """ - def get_query_set(self): - qs = super(ManagerBase, self).get_query_set() + def get_queryset(self): + qs = super(ManagerBase, self).get_queryset() if getattr(_locals, 'skip_cache', False): qs = qs.no_cache() return self._with_translations(qs) diff --git a/apps/amo/templates/amo/robots.html b/apps/amo/templates/amo/robots.html index b76f35378a6..284417ec973 100644 --- a/apps/amo/templates/amo/robots.html +++ b/apps/amo/templates/amo/robots.html @@ -16,7 +16,7 @@ {% for a in apps -%} Disallow: /{{ l }}/{{ a.short }}{{ url('search.search', add_prefix=False) }} -Disallow: /{{ l }}/{{ a.short }}{{ url('users.pwreset', add_prefix=False) }} +Disallow: /{{ l }}/{{ a.short }}{{ url('password_reset_form', add_prefix=False) }} {% endfor %} {% endfor %} diff --git a/apps/amo/tests/test_helpers.py b/apps/amo/tests/test_helpers.py index 7a7c7826214..868422fa8cc 100644 --- a/apps/amo/tests/test_helpers.py +++ b/apps/amo/tests/test_helpers.py @@ -461,7 +461,7 @@ def test_absolutify(): def test_timesince(): month_ago = datetime.now() - timedelta(days=30) - eq_(helpers.timesince(month_ago), u'1 month ago') + eq_(helpers.timesince(month_ago), u'1 month ago') eq_(helpers.timesince(None), u'') diff --git a/apps/amo/tests/test_readonly.py b/apps/amo/tests/test_readonly.py index c388a88a561..2fd8303e1f3 100644 --- a/apps/amo/tests/test_readonly.py +++ b/apps/amo/tests/test_readonly.py @@ -3,7 +3,6 @@ from django.utils import importlib import MySQLdb as mysql -from lib.misc import safe_signals from nose.tools import assert_raises, eq_ from pyquery import PyQuery as pq @@ -30,7 +29,6 @@ class ReadOnlyModeTest(amo.tests.TestCase): extra = ('amo.middleware.ReadOnlyMiddleware',) def setUp(self): - safe_signals.Signal.send = safe_signals.unsafe_send models.signals.pre_save.connect(self.db_error) models.signals.pre_delete.connect(self.db_error) self.old_settings = dict((k, quickcopy(getattr(settings, k))) @@ -52,7 +50,6 @@ def tearDown(self): pass models.signals.pre_save.disconnect(self.db_error) models.signals.pre_delete.disconnect(self.db_error) - safe_signals.Signal.send = safe_signals.safe_send def db_error(self, *args, **kwargs): raise mysql.OperationalError("You can't do this in read-only mode.") diff --git a/apps/amo/tests/test_send_mail.py b/apps/amo/tests/test_send_mail.py index bda55001f32..7138f981c23 100644 --- a/apps/amo/tests/test_send_mail.py +++ b/apps/amo/tests/test_send_mail.py @@ -26,6 +26,7 @@ def setUp(self): self._email_blacklist = list(getattr(settings, 'EMAIL_BLACKLIST', [])) def tearDown(self): + translation.activate('en_US') settings.EMAIL_BLACKLIST = self._email_blacklist def test_send_string(self): @@ -202,7 +203,7 @@ def test_send_html_mail_jinja(self): def test_send_attachment(self): path = os.path.join(ATTACHMENTS_DIR, 'bacon.txt') - attachments = [(os.path.basename(path), storage.open(path), + attachments = [(os.path.basename(path), storage.open(path).read(), mimetypes.guess_type(path)[0])] send_mail('test subject', 'test body', from_email='a@example.com', recipient_list=['b@example.com'], attachments=attachments) diff --git a/apps/amo/utils.py b/apps/amo/utils.py index 068262d3f3d..d01cb8265d2 100644 --- a/apps/amo/utils.py +++ b/apps/amo/utils.py @@ -671,12 +671,12 @@ def cache_ns_key(namespace, increment=False): except ValueError: log.info('Cache increment failed for key: %s. Resetting.' % ns_key) ns_val = epoch(datetime.datetime.now()) - cache.set(ns_key, ns_val, 0) + cache.set(ns_key, ns_val, None) else: ns_val = cache.get(ns_key) if ns_val is None: ns_val = epoch(datetime.datetime.now()) - cache.set(ns_key, ns_val, 0) + cache.set(ns_key, ns_val, None) return '%s:%s' % (ns_val, ns_key) diff --git a/apps/api/tests/test_urls.py b/apps/api/tests/test_urls.py new file mode 100644 index 00000000000..9d5a28aa887 --- /dev/null +++ b/apps/api/tests/test_urls.py @@ -0,0 +1,15 @@ +from mock import Mock +from nose.tools import eq_ + +from amo.tests import TestCase + +from ..urls import SwitchToDRF + + +class TestDRFSwitch(TestCase): + + def test_piston_view(self): + view = SwitchToDRF('LanguageView') + eq_(view(Mock(), 1).__module__, 'django.http.response') + self.create_switch('drf', db=True) + eq_(view(Mock()).__module__, 'rest_framework.response') diff --git a/apps/api/tests/test_views.py b/apps/api/tests/test_views.py index 85e1c82307b..85c2e9b2d34 100644 --- a/apps/api/tests/test_views.py +++ b/apps/api/tests/test_views.py @@ -1282,7 +1282,8 @@ def test_search_no_localepicker(self): def setup_localepicker(self, platform): self.addon.update(type=amo.ADDON_LPAPP, status=amo.STATUS_PUBLIC) version = self.addon.versions.all()[0] - File.objects.create(version=version, platform_id=platform) + File.objects.create(version=version, platform_id=platform, + status=amo.STATUS_PUBLIC) def test_search_wrong_platform(self): self.setup_localepicker(amo.PLATFORM_MAC.id) diff --git a/apps/browse/views.py b/apps/browse/views.py index 6676be8b3be..2fecf9780f6 100644 --- a/apps/browse/views.py +++ b/apps/browse/views.py @@ -294,17 +294,13 @@ class PersonasFilter(BaseFilter): def _filter(self, field): qs = Addon.objects if field == 'created': - return (qs.order_by('-created') - .with_index(addons='created_type_idx')) + return qs.order_by('-created') elif field == 'popular': - return (qs.order_by('-persona__popularity') - .with_index(personas='personas_popularity_idx')) + return qs.order_by('-persona__popularity') elif field == 'rating': - return (qs.order_by('-bayesian_rating') - .with_index(addons='rating_type_idx')) + return qs.order_by('-bayesian_rating') else: - return (qs.order_by('-persona__movers') - .with_index(personas='personas_movers_idx')) + return qs.order_by('-persona__movers') def personas_listing(request, category_slug=None): diff --git a/apps/devhub/forms.py b/apps/devhub/forms.py index a2116140c0d..3f37e16fb72 100644 --- a/apps/devhub/forms.py +++ b/apps/devhub/forms.py @@ -441,7 +441,11 @@ def __init__(self, *args, **kw): self.initial = ([{} for _ in qs] + [{'application': a.id} for a in apps]) self.extra = len(amo.APP_GUIDS) - len(self.forms) - self._construct_forms() + # After these changes, the forms need to be rebuilt. `forms` + # is a cached property, so we delete the existing cache and + # ask for a new one to be built. + del self.forms + self.forms def clean(self): if any(self.errors): @@ -898,7 +902,6 @@ class PackagerCompatBaseFormSet(BaseFormSet): def __init__(self, *args, **kw): super(PackagerCompatBaseFormSet, self).__init__(*args, **kw) self.initial = [{'application': a} for a in amo.APP_USAGE] - self._construct_forms() def clean(self): if any(self.errors): diff --git a/apps/devhub/models.py b/apps/devhub/models.py index b25d18da32a..3e653e415b9 100644 --- a/apps/devhub/models.py +++ b/apps/devhub/models.py @@ -113,7 +113,7 @@ class AppLog(amo.models.ModelBase): """ This table is for indexing the activity log by app. """ - addon = models.ForeignKey(Webapp) + addon = models.ForeignKey(Webapp, db_constraint=False) activity_log = models.ForeignKey('ActivityLog') class Meta: diff --git a/apps/devhub/tests/test_forms.py b/apps/devhub/tests/test_forms.py index cd70fbe92d0..19fb1dcde5a 100644 --- a/apps/devhub/tests/test_forms.py +++ b/apps/devhub/tests/test_forms.py @@ -498,6 +498,7 @@ def test_localize_name_description(self): def test_reupload(self, save_persona_image_mock, create_persona_preview_images_mock, make_checksum_mock): + make_checksum_mock.return_value = 'checksumbeforeyouwrecksome' data = self.get_dict(header_hash='y0l0', footer_hash='abab') self.form = EditThemeForm(data, request=self.request, instance=self.instance) diff --git a/apps/discovery/tests/test_views.py b/apps/discovery/tests/test_views.py index 773080833ca..2d07ba82951 100644 --- a/apps/discovery/tests/test_views.py +++ b/apps/discovery/tests/test_views.py @@ -527,7 +527,8 @@ def test_eula_trickle(self): class TestMonthlyPick(amo.tests.TestCase): - fixtures = ['base/apps', 'base/addon_3615', 'discovery/discoverymodules'] + fixtures = ['base/users', 'base/apps', 'base/addon_3615', + 'discovery/discoverymodules'] def setUp(self): self.url = reverse('discovery.pane.promos', args=['Darwin', '10.0']) diff --git a/apps/editors/helpers.py b/apps/editors/helpers.py index b27d6edd54a..d01de125b1b 100644 --- a/apps/editors/helpers.py +++ b/apps/editors/helpers.py @@ -641,7 +641,7 @@ class ReviewAddon(ReviewBase): def __init__(self, *args, **kwargs): super(ReviewAddon, self).__init__(*args, **kwargs) - self.is_upgrade = (self.addon.status is amo.STATUS_LITE_AND_NOMINATED + self.is_upgrade = (self.addon.status == amo.STATUS_LITE_AND_NOMINATED and self.review_type == 'nominated') def set_data(self, data): diff --git a/apps/editors/models.py b/apps/editors/models.py index 976fa8499d1..39d301bc16a 100644 --- a/apps/editors/models.py +++ b/apps/editors/models.py @@ -453,7 +453,7 @@ def get_total(cls, user): if val is None: val = 0 - cache.set(key, val, 0) + cache.set(key, val, None) return val @classmethod @@ -469,7 +469,7 @@ def get_recent(cls, user, limit=5, addon_type=None): val.filter(addon__type=addon_type) val = list(val[:limit]) - cache.set(key, val, 0) + cache.set(key, val, None) return val @classmethod @@ -492,7 +492,7 @@ def get_breakdown(cls, user): """ with amo.models.skip_cache(): val = list(ReviewerScore.objects.raw(sql, [user.id])) - cache.set(key, val, 0) + cache.set(key, val, None) return val @classmethod @@ -605,7 +605,7 @@ def get_leaderboards(cls, user, days=7, types=None, addon_type=None): 'leader_near': leader_near, 'user_rank': user_rank, } - cache.set(key, val, 0) + cache.set(key, val, None) return val @classmethod diff --git a/apps/editors/tests/test_views_themes.py b/apps/editors/tests/test_views_themes.py index ed7e2f3e484..9b55ac210f3 100644 --- a/apps/editors/tests/test_views_themes.py +++ b/apps/editors/tests/test_views_themes.py @@ -178,10 +178,10 @@ def test_commit(self, copy_mock, create_preview_mock, eq_(themes[4].addon.reload().current_version.version, str(float(old_version) + 1)) else: - eq_(themes[0].addon.status, amo.STATUS_REVIEW_PENDING) - eq_(themes[1].addon.status, amo.STATUS_REVIEW_PENDING) - eq_(themes[2].addon.status, amo.STATUS_REJECTED) - eq_(themes[3].addon.status, amo.STATUS_REJECTED) + eq_(themes[0].addon.reload().status, amo.STATUS_REVIEW_PENDING) + eq_(themes[1].addon.reload().status, amo.STATUS_REVIEW_PENDING) + eq_(themes[2].addon.reload().status, amo.STATUS_REJECTED) + eq_(themes[3].addon.reload().status, amo.STATUS_REJECTED) eq_(themes[4].addon.reload().status, amo.STATUS_PUBLIC) eq_(ActivityLog.objects.count(), 4 if self.rereview else 5) diff --git a/apps/files/tests/test_views.py b/apps/files/tests/test_views.py index da7f8461215..b7c43eaa9a5 100644 --- a/apps/files/tests/test_views.py +++ b/apps/files/tests/test_views.py @@ -381,7 +381,7 @@ def test_directory(self): def test_unicode(self): self.file_viewer.src = unicode_filenames self.file_viewer.extract() - res = self.client.get(self.file_url(iri_to_uri(u'\u1109\u1161\u11a9'))) + res = self.client.get(self.file_url(u'\u1109\u1161\u11a9')) eq_(res.status_code, 200) def test_serve_no_token(self): diff --git a/apps/search/tests/test_views.py b/apps/search/tests/test_views.py index 5546f350079..fd0359348ca 100644 --- a/apps/search/tests/test_views.py +++ b/apps/search/tests/test_views.py @@ -128,7 +128,7 @@ def check_heading(self): class TestESSearch(SearchBase): - fixtures = ['base/apps', 'base/category', 'tags/tags'] + fixtures = ['base/apps', 'base/category'] @classmethod def setUpClass(cls): @@ -1077,6 +1077,7 @@ def search_addons(self, url, params, addons=[], types=amo.ADDON_TYPES, eq_(len(data), len(addons)) for got, expected in zip(data, addons): + expected.reload() eq_(int(got['id']), expected.id) eq_(got['name'], unicode(expected.name)) expected_url = expected.get_url_path() diff --git a/apps/stats/db.py b/apps/stats/db.py index 82cd9dc98a4..445c16f72ce 100644 --- a/apps/stats/db.py +++ b/apps/stats/db.py @@ -1,10 +1,7 @@ from django.db import models import phpserialize as php -try: - import simplejson as json -except ImportError: - import json +import json class StatsDictField(models.TextField): diff --git a/apps/stats/views.py b/apps/stats/views.py index 8b1de0b7930..8355a667310 100644 --- a/apps/stats/views.py +++ b/apps/stats/views.py @@ -1,6 +1,7 @@ import cStringIO import csv import itertools +import json import logging import time from datetime import date, timedelta @@ -13,7 +14,6 @@ from django.db import connection from django.db.models import Avg, Count, Q, Sum from django.shortcuts import get_object_or_404, render -from django.utils import simplejson from django.utils.cache import add_never_cache_headers, patch_cache_control from django.utils.datastructures import SortedDict @@ -712,5 +712,5 @@ def render_json(request, addon, stats): # Django's encoder supports date and datetime. fudge_headers(response, stats) - simplejson.dump(stats, response, cls=DjangoJSONEncoder) + json.dump(stats, response, cls=DjangoJSONEncoder) return response diff --git a/apps/translations/fields.py b/apps/translations/fields.py index 10ea34506c8..475ec8323d4 100644 --- a/apps/translations/fields.py +++ b/apps/translations/fields.py @@ -242,7 +242,11 @@ def _has_changed(self, initial, data): class LocaleValidationError(forms.ValidationError): def __init__(self, messages, code=None, params=None): - self.messages = messages + self.msgs = messages + + @property + def messages(self): + return self.msgs class TransField(_TransField, forms.CharField): diff --git a/apps/translations/query.py b/apps/translations/query.py index 3d0126f5863..50dd33dba96 100644 --- a/apps/translations/query.py +++ b/apps/translations/query.py @@ -2,7 +2,6 @@ from django.conf import settings from django.db import models -from django.db.models.sql import compiler from django.utils import translation as translation_utils @@ -27,20 +26,23 @@ def order_by_translation(qs, fieldname): model = qs.model field = model._meta.get_field(fieldname) - # (lhs, rhs, lhs_col, rhs_col) => lhs.lhs_col = rhs.rhs_col + # connection is a tuple (lhs, table, join_cols) connection = (model._meta.db_table, field.rel.to._meta.db_table, - field.column, field.rel.field_name) + field.rel.field_name) # Doing the manual joins is flying under Django's radar, so we need to make # sure the initial alias (the main table) is set up. if not qs.query.tables: qs.query.get_initial_alias() - # Force two LEFT JOINs against the translation table. We'll hook up the + # Force two new (reuse is an empty set) LEFT OUTER JOINs against the + # translation table, without reusing any aliases. We'll hook up the # language fallbacks later. qs.query = qs.query.clone(TranslationQuery) - t1 = qs.query.join(connection, always_create=True, promote=True) - t2 = qs.query.join(connection, always_create=True, promote=True) + t1 = qs.query.join(connection, join_field=field, + outer_if_first=True, reuse=set()) + t2 = qs.query.join(connection, join_field=field, + outer_if_first=True, reuse=set()) qs.query.translation_aliases = {field: (t1, t2)} f1, f2 = '%s.`localized_string`' % t1, '%s.`localized_string`' % t2 @@ -108,8 +110,11 @@ def join_with_locale(self, alias, fallback=None): qn = self.quote_name_unless_alias qn2 = self.connection.ops.quote_name mapping = self.query.alias_map[alias] - name, alias, join_type, lhs, lhs_col, col, nullable = mapping - alias_str = (alias != name and ' %s' % alias or '') + # name, alias, join_type, lhs, lhs_col, col, nullable = mapping + name, alias, join_type, lhs, join_cols, _, join_field = mapping + lhs_col = join_field.column + rhs_col = join_cols + alias_str = '' if alias == name else (' %s' % alias) if isinstance(fallback, models.Field): fallback_str = '%s.%s' % (qn(self.query.model._meta.db_table), @@ -119,5 +124,5 @@ def join_with_locale(self, alias, fallback=None): return ('%s %s%s ON (%s.%s = %s.%s AND %s.%s = %s)' % (join_type, qn(name), alias_str, - qn(lhs), qn2(lhs_col), qn(alias), qn2(col), + qn(lhs), qn2(lhs_col), qn(alias), qn2(rhs_col), qn(alias), qn('locale'), fallback_str)) diff --git a/apps/users/models.py b/apps/users/models.py index 47e89ef664f..607b799b80a 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -313,7 +313,11 @@ def last_login(self): @amo.cached_property def reviews(self): """All reviews that are not dev replies.""" - return self._reviews_all.filter(reply_to=None) + qs = self._reviews_all.filter(reply_to=None) + # Force the query to occur immediately. Several + # reviews-related tests hang if this isn't done. + list(qs) + return qs def anonymize(self): log.info(u"User (%s: <%s>) is being anonymized." % (self, self.email)) diff --git a/apps/users/templates/users/edit.html b/apps/users/templates/users/edit.html index 7ab56ad0d0c..29006a29943 100644 --- a/apps/users/templates/users/edit.html +++ b/apps/users/templates/users/edit.html @@ -55,7 +55,7 @@
{{ _('Password') }}

- {% trans reset_url=url('users.pwreset') -%} + {% trans reset_url=url('password_reset_form') -%} Change your password. If you forgot your password, you can use the reset form. {%- endtrans %}

diff --git a/apps/users/templates/users/login_help.html b/apps/users/templates/users/login_help.html index d5b5c2ffe36..1ed303d5618 100644 --- a/apps/users/templates/users/login_help.html +++ b/apps/users/templates/users/login_help.html @@ -3,7 +3,7 @@

{{ _('Login Problems?') }}

diff --git a/apps/users/templates/users/mobile/login.html b/apps/users/templates/users/mobile/login.html index c8ef394ddca..bd684e60ff4 100644 --- a/apps/users/templates/users/mobile/login.html +++ b/apps/users/templates/users/mobile/login.html @@ -79,7 +79,7 @@

{{ _('Log In') }}

{{ _('Login Problems?') }}

{% endif %} diff --git a/apps/users/tests/test_forms.py b/apps/users/tests/test_forms.py index 139f30f0ac5..3b09217c664 100644 --- a/apps/users/tests/test_forms.py +++ b/apps/users/tests/test_forms.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator from django.core import mail -from django.utils.http import int_to_base36 +from django.utils.http import urlsafe_base64_encode from django.conf import settings from mock import Mock, patch @@ -26,21 +26,22 @@ class UserFormBase(amo.tests.TestCase): def setUp(self): self.user = User.objects.get(id='4043307') self.user_profile = self.user.get_profile() - self.uidb36 = int_to_base36(self.user.id) + self.uidb64 = urlsafe_base64_encode(str(self.user.id)) self.token = default_token_generator.make_token(self.user) class TestSetPasswordForm(UserFormBase): def _get_reset_url(self): - return "/en-US/firefox/users/pwreset/%s/%s" % (self.uidb36, self.token) + return "/en-US/firefox/users/pwreset/%s/%s" % (self.uidb64, self.token) def test_url_fail(self): r = self.client.get('/users/pwreset/junk/', follow=True) eq_(r.status_code, 404) r = self.client.get('/en-US/firefox/users/pwreset/%s/12-345' % - self.uidb36) + self.uidb64 +) self.assertContains(r, "Password reset unsuccessful") def test_set_fail(self): @@ -106,15 +107,7 @@ def test_request_success(self): eq_(len(mail.outbox), 1) assert mail.outbox[0].subject.find('Password reset') == 0 - assert mail.outbox[0].body.find('pwreset/%s' % self.uidb36) > 0 - - def test_amo_user_but_no_django_user(self): - # Password reset should work without a Django user. - self.user_profile.update(user=None, _signal=True) - self.user.delete() - self.client.post('/en-US/firefox/users/pwreset', - {'email': self.user.email}) - eq_(len(mail.outbox), 1) + assert mail.outbox[0].body.find('pwreset/%s' % self.uidb64) > 0 class TestUserDeleteForm(UserFormBase): @@ -280,7 +273,7 @@ def test_credential_fail(self): 'password': 'wrongpassword'}) self.assertFormError(r, 'form', '', ("Please enter a correct username " "and password. Note that both " - "fields are case-sensitive.")) + "fields may be case-sensitive.")) def test_credential_success(self): user = UserProfile.objects.get(email='jbalogh@mozilla.com') @@ -358,7 +351,7 @@ def test_disabled_account(self): 'password': 'foo'}, follow=True) self.assertNotContains(r, "Welcome, Jeff") self.assertContains(r, 'Please enter a correct username and password. ' - 'Note that both fields are case-sensitive.') + 'Note that both fields may be case-sensitive.') def test_successful_login_logging(self): t = datetime.now() diff --git a/apps/users/tests/test_models.py b/apps/users/tests/test_models.py index d7bf007d9d1..b2b11edae71 100644 --- a/apps/users/tests/test_models.py +++ b/apps/users/tests/test_models.py @@ -7,7 +7,7 @@ from django import forms from django.conf import settings from django.contrib.auth.hashers import (is_password_usable, - check_password, make_password) + check_password, make_password, load_hashers, identify_hasher) from django.contrib.auth.models import User from django.core import mail from django.utils import encoding, translation @@ -348,6 +348,13 @@ def test_sha512(self): self.assertTrue(is_password_usable(encoded)) self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) + self.assertEqual(identify_hasher(encoded).algorithm, "sha512") + # Blank passwords + blank_encoded = make_password('', 'seasalt', 'sha512') + self.assertTrue(blank_encoded.startswith('sha512$')) + self.assertTrue(is_password_usable(blank_encoded)) + self.assertTrue(check_password('', blank_encoded)) + self.assertFalse(check_password(' ', blank_encoded)) class TestBlacklistedUsername(amo.tests.TestCase): diff --git a/apps/users/tests/test_views.py b/apps/users/tests/test_views.py index a064c03a427..bb6ffded4ef 100644 --- a/apps/users/tests/test_views.py +++ b/apps/users/tests/test_views.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator from django.forms.models import model_to_dict -from django.utils.http import int_to_base36 +from django.utils.http import urlsafe_base64_encode from mock import ANY, Mock, patch from nose.tools import eq_ @@ -1066,7 +1066,7 @@ class TestReset(UserViewBase): def setUp(self): user = User.objects.get(email='editor@mozilla.com').get_profile() - self.token = [int_to_base36(user.id), + self.token = [urlsafe_base64_encode(str(user.id)), default_token_generator.make_token(user)] def test_reset_msg(self): diff --git a/apps/users/urls.py b/apps/users/urls.py index d9a4cb7d69f..f9df3e0ab8b 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -44,11 +44,11 @@ {'template_name': 'users/pwreset_request.html', 'email_template_name': 'users/email/pwreset.ltxt', 'password_reset_form': forms.PasswordResetForm, - }, name="users.pwreset"), + }, name='password_reset_form'), url(r'^pwresetsent$', auth_views.password_reset_done, {'template_name': 'users/pwreset_sent.html'}, - name="users.pwreset_sent"), - url(r'^pwreset/(?P\w{1,13})/(?P\w{1,13}-\w{1,20})$', + name="password_reset_done"), + url(r'^pwreset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})', views.password_reset_confirm, name="users.pwreset_confirm"), url(r'^pwresetcomplete$', auth_views.password_reset_complete, diff --git a/apps/users/views.py b/apps/users/views.py index 2a79b0757de..76f5ea55744 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -12,7 +12,7 @@ from django.template import Context, loader from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_exempt -from django.utils.http import base36_to_int, is_safe_url +from django.utils.http import urlsafe_base64_decode, is_safe_url import commonware.log import waffle @@ -749,15 +749,15 @@ def remove_locale(request, user): @never_cache -def password_reset_confirm(request, uidb36=None, token=None): +def password_reset_confirm(request, uidb64=None, token=None): """ Pulled from django contrib so that we can add user into the form so then we can show relevant messages about the user. """ - assert uidb36 is not None and token is not None + assert uidb64 is not None and token is not None user = None try: - uid_int = base36_to_int(uidb36) + uid_int = urlsafe_base64_decode(uidb64) user = UserProfile.objects.get(id=uid_int) except (ValueError, UserProfile.DoesNotExist): pass diff --git a/apps/zadmin/tests/test_views.py b/apps/zadmin/tests/test_views.py index f348e0324c0..898d266890b 100644 --- a/apps/zadmin/tests/test_views.py +++ b/apps/zadmin/tests/test_views.py @@ -1478,7 +1478,7 @@ def test_no_webapps(self): rows = doc('#result_list tbody tr') eq_(rows.length, 1) eq_(rows.find('a').attr('href'), - '3615/') + '/en-US/admin/models/addons/addon/3615/') class TestAddonManagement(amo.tests.TestCase): diff --git a/lib/misc/safe_signals.py b/lib/misc/safe_signals.py deleted file mode 100644 index 333886eb0a4..00000000000 --- a/lib/misc/safe_signals.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -A monkeypatch for ``django.dispatch`` to send signals safely. - -Usage:: - - >>> import safe_signals - >>> safe_signals.start_the_machine() - -``django.dispatch.Signal.send`` is replaced with a safer function that catches -and logs errors. It's like ``Signal.send_robust`` but with logging. - -""" -import logging - -from django.dispatch.dispatcher import Signal, _make_id -from django.conf import settings - - -log = logging.getLogger('signals') - - -def safe_send(self, sender, **named): - responses = [] - if not self.receivers: - return responses - - do_raise = getattr(settings, 'RAISE_ON_SIGNAL_ERROR', False) - - # Call each receiver with whatever arguments it can accept. - # Return a list of tuple pairs [(receiver, response), ... ]. - for receiver in self._live_receivers(_make_id(sender)): - try: - response = receiver(signal=self, sender=sender, **named) - except Exception, err: - if do_raise: - raise - log.error('Error calling signal', exc_info=True) - responses.append((receiver, err)) - else: - responses.append((receiver, response)) - return responses - - -safe_send.__doc__ = Signal.send_robust.__doc__ -unsafe_send = Signal.send - - -def start_the_machine(): - # Monkeypatch! - Signal.send = safe_send - Signal.send_robust = safe_send diff --git a/lib/settings_base.py b/lib/settings_base.py index 46f3df4a1cb..b224370f27f 100644 --- a/lib/settings_base.py +++ b/lib/settings_base.py @@ -79,6 +79,8 @@ ABUSE_EMAIL = 'marketplace-staff+abuse@mozilla.org' NOBODY_EMAIL = 'nobody@mozilla.org' +CONN_MAX_AGE = 60 + DATABASES = { 'default': { 'NAME': 'zamboni', @@ -404,6 +406,7 @@ def JINJA_CONFIG(): # Auth AUTHENTICATION_BACKENDS = ( 'users.backends.AmoUserBackend', + 'django_browserid.auth.BrowserIDBackend' ) AUTH_PROFILE_MODULE = 'users.UserProfile' diff --git a/manage.py b/manage.py index f2cb3196671..76d90920094 100755 --- a/manage.py +++ b/manage.py @@ -31,8 +31,7 @@ # No third-party imports until we've added all our sitedirs! -from django.core.management import (call_command, execute_manager, - setup_environ) +from django.core.management import call_command, execute_from_command_line # Figuring out what settings file to use. # 1. Look first for the command line setting. @@ -70,14 +69,6 @@ if not settings.DEBUG: warnings.simplefilter('ignore') -# The first thing execute_manager does is call `setup_environ`. Logging config -# needs to access settings, so we'll setup the environ early. -setup_environ(settings) - -# Hardcore monkeypatching action. -import jingo.monkey -jingo.monkey.patch() - import session_csrf session_csrf.monkeypatch() @@ -93,6 +84,9 @@ #imported before anything else imports waffle. import amo +# Hardcore monkeypatching action. +import jingo.monkey +jingo.monkey.patch() def new(self, arg): try: @@ -137,4 +131,4 @@ def new(self, arg): call_command('update_product_details') product_details.__init__() # reload the product details - execute_manager(settings) + execute_from_command_line() diff --git a/mkt/api/tests/test_oauth.py b/mkt/api/tests/test_oauth.py index 2c540d25bed..92d1549ab0a 100644 --- a/mkt/api/tests/test_oauth.py +++ b/mkt/api/tests/test_oauth.py @@ -7,7 +7,7 @@ from django.conf import settings from django.contrib.auth.models import User from django.test.client import Client, FakePayload -from django.utils.encoding import smart_str +from django.utils.encoding import iri_to_uri, smart_str from nose.tools import eq_ from oauthlib import oauth1 @@ -157,7 +157,10 @@ class RestOAuthClient(OAuthClient): def __init__(self, access): super(OAuthClient, self).__init__(self) self.access = access - self.get_absolute_url = absolutify + + def get_absolute_url(self, url): + unquoted_url = urlparse.unquote(url) + return absolutify(iri_to_uri(unquoted_url)) class RestOAuth(BaseOAuth): diff --git a/mkt/developers/forms.py b/mkt/developers/forms.py index 1dbbe63c35b..eb08aad74df 100644 --- a/mkt/developers/forms.py +++ b/mkt/developers/forms.py @@ -933,7 +933,7 @@ class AppFormTechnical(addons.forms.AddonFormBase): class Meta: model = Addon - fields = 'public_stats', + fields = ('public_stats',) def __init__(self, *args, **kw): super(AppFormTechnical, self).__init__(*args, **kw) @@ -941,11 +941,11 @@ def __init__(self, *args, **kw): def save(self, addon, commit=False): uses_flash = self.cleaned_data.get('flash') + self.instance = super(AppFormTechnical, self).save(commit=True) af = self.instance.get_latest_file() if af is not None: af.update(uses_flash=bool(uses_flash)) - - return super(AppFormTechnical, self).save(commit=True) + return self.instance class TransactionFilterForm(happyforms.Form): diff --git a/mkt/developers/tasks.py b/mkt/developers/tasks.py index f90d901182b..d16c2d79511 100644 --- a/mkt/developers/tasks.py +++ b/mkt/developers/tasks.py @@ -13,6 +13,7 @@ from django import forms from django.conf import settings from django.core.files.storage import default_storage as storage +from django.db import transaction from django.utils.http import urlencode import requests diff --git a/mkt/developers/tests/test_commands.py b/mkt/developers/tests/test_commands.py index 84165ac37ad..e5b3f1eeda2 100644 --- a/mkt/developers/tests/test_commands.py +++ b/mkt/developers/tests/test_commands.py @@ -81,10 +81,10 @@ def setUp(self): self.webapp = Webapp.objects.get(pk=337141) def _germany_listed(self): - return not self.webapp.geodata.region_de_iarc_exclude + return not self.webapp.geodata.reload().region_de_iarc_exclude def _brazil_listed(self): - return not self.webapp.geodata.region_br_iarc_exclude + return not self.webapp.geodata.reload().region_br_iarc_exclude def test_exclude_unrated(self): amo.tests.make_game(self.webapp, rated=False) diff --git a/mkt/developers/tests/test_forms.py b/mkt/developers/tests/test_forms.py index 1a7e835c273..15feb65f1b4 100644 --- a/mkt/developers/tests/test_forms.py +++ b/mkt/developers/tests/test_forms.py @@ -1,4 +1,4 @@ -# -*- coding: utf8 -*- +# -*- coding: utf-8 -*- import json import os import shutil @@ -657,7 +657,7 @@ def test_changing_cert(self, storefront_mock): ok_(form.is_valid(), form.errors) form.save(self.app) - iarc_info = self.app.iarc_info + iarc_info = self.app.iarc_info.reload() eq_(iarc_info.submission_id, 2) eq_(iarc_info.security_code, 'b') assert storefront_mock.called diff --git a/mkt/developers/tests/test_views_api.py b/mkt/developers/tests/test_views_api.py index 07eaa37f3e3..34103b7631a 100644 --- a/mkt/developers/tests/test_views_api.py +++ b/mkt/developers/tests/test_views_api.py @@ -25,7 +25,7 @@ def test_non_url(self): res = self.client.post( self.url, {'app_name': 'test', 'redirect_uri': 'mailto:cvan@example.com'}) - self.assertFormError(res, 'form', 'redirect_uri', ['Enter a valid value.']) + self.assertFormError(res, 'form', 'redirect_uri', ['Enter a valid URL.']) def test_create(self): Access.objects.create(user=self.user, key='foo', secret='bar') diff --git a/mkt/developers/tests/test_views_edit.py b/mkt/developers/tests/test_views_edit.py index 8988dd204a0..2408bf23d58 100644 --- a/mkt/developers/tests/test_views_edit.py +++ b/mkt/developers/tests/test_views_edit.py @@ -1331,7 +1331,7 @@ def test_mozilla_contact_invalid(self): mozilla_contact='@mozilla.com') webapp = self.get_webapp() self.assertFormError(r, 'form', 'mozilla_contact', - 'Enter a valid e-mail address.') + 'Enter a valid email address.') eq_(webapp.mozilla_contact, '') def test_vip_app_toggle(self): diff --git a/mkt/developers/views.py b/mkt/developers/views.py index b115b0b2e04..c5b4029cf7d 100644 --- a/mkt/developers/views.py +++ b/mkt/developers/views.py @@ -31,7 +31,7 @@ from addons.views import BaseFilter from amo import messages from amo.decorators import (any_permission_required, json_view, login_required, - post_required) + post_required, write) from amo.urlresolvers import reverse from amo.utils import escape_all from devhub.models import AppLog @@ -550,7 +550,6 @@ def ownership(request, addon_id, addon, webapp=False): author.get_role_display(), addon) messages.success(request, _('Changes successfully saved.')) - return redirect(redirect_url) ctx = dict(addon=addon, webapp=webapp, user_form=user_form) @@ -612,6 +611,7 @@ def refresh_manifest(request, addon_id, addon, webapp=False): @post_required @json_view +@write def _upload_manifest(request, is_standalone=False): form = forms.NewManifestForm(request.POST, is_standalone=is_standalone) if (not is_standalone and diff --git a/mkt/search/tests/test_api.py b/mkt/search/tests/test_api.py index 60c661aaf4b..c1693132bc2 100644 --- a/mkt/search/tests/test_api.py +++ b/mkt/search/tests/test_api.py @@ -103,7 +103,8 @@ def setUp(self): def tearDown(self): unindex_webapps(list(Webapp.with_deleted.values_list('id', flat=True))) - Webapp.objects.all().delete() + for w in Webapp.objects.all(): + w.delete() super(TestApi, self).tearDown() def test_verbs(self): diff --git a/mkt/submit/forms.py b/mkt/submit/forms.py index 1b5e204b31f..fa8a02bd764 100644 --- a/mkt/submit/forms.py +++ b/mkt/submit/forms.py @@ -413,15 +413,13 @@ def clean_app_slug(self): return slug.lower() def save(self, *args, **kw): + self.instance = super(AppDetailsBasicForm, self).save(commit=True) uses_flash = self.cleaned_data.get('flash') af = self.instance.get_latest_file() if af is not None: af.update(uses_flash=bool(uses_flash)) - form = super(AppDetailsBasicForm, self).save(commit=False) - form.save() - - return form + return self.instance class AppFeaturesForm(happyforms.ModelForm): diff --git a/mkt/submit/tests/test_forms.py b/mkt/submit/tests/test_forms.py index 3928bfb66be..b865e021a53 100644 --- a/mkt/submit/tests/test_forms.py +++ b/mkt/submit/tests/test_forms.py @@ -187,7 +187,6 @@ def test_slug(self): instance=app) assert form.is_valid() form.save() - app.reload() eq_(app.app_slug, 'thisisaslug') diff --git a/mkt/submit/tests/test_views.py b/mkt/submit/tests/test_views.py index d452c6908de..9b220a6ae48 100644 --- a/mkt/submit/tests/test_views.py +++ b/mkt/submit/tests/test_views.py @@ -723,7 +723,6 @@ def test_success_paid(self): self.webapp = self.get_webapp() self.make_premium(self.webapp) - data = self.get_dict() r = self.client.post(self.url, data) self.assertNoFormErrors(r) @@ -922,7 +921,7 @@ def test_support_email_invalid(self): self._step() r = self.client.post(self.url, self.get_dict(support_email='xxx')) self.assertFormError(r, 'form_basic', 'support_email', - 'Enter a valid e-mail address.') + 'Enter a valid email address.') def test_categories_required(self): self._step() diff --git a/mkt/submit/views.py b/mkt/submit/views.py index d9049ca0ec8..2e37ef990e5 100644 --- a/mkt/submit/views.py +++ b/mkt/submit/views.py @@ -161,14 +161,12 @@ def details(request, addon_id, addon): 'form_icon': form_icon, 'form_previews': form_previews, } - if request.POST and all(f.is_valid() for f in forms.itervalues()): addon = form_basic.save(addon) form_cats.save() form_icon.save(addon) for preview in form_previews.forms: preview.save(addon) - # If this is an incomplete app from the legacy submission flow, it may # not have device types set yet - so assume it works everywhere. if not addon.device_types: diff --git a/mkt/webapps/models.py b/mkt/webapps/models.py index 759d94fd7e9..81b53fc7f6e 100644 --- a/mkt/webapps/models.py +++ b/mkt/webapps/models.py @@ -67,7 +67,7 @@ def reverse_version(version): The try/except AttributeError allows this to be used where the input is ambiguous, and could be either an already-reversed URL or a Version object. """ - if version: + if version and settings.MARKETPLACE: try: return reverse('version-detail', kwargs={'pk': version.pk}) except AttributeError: @@ -886,7 +886,7 @@ def get_cached_manifest(self, force=False): data = json.dumps(data, cls=JSONEncoder) - cache.set(key, data, 0) + cache.set(key, data, None) return data diff --git a/mkt/zadmin/tests/test_views.py b/mkt/zadmin/tests/test_views.py index d3a2bddf6e2..04a55624e28 100644 --- a/mkt/zadmin/tests/test_views.py +++ b/mkt/zadmin/tests/test_views.py @@ -103,7 +103,7 @@ def test_no_webapps(self): doc = pq(res.content) rows = doc('#result_list tbody tr') eq_(rows.length, 1) - eq_(rows.find('a').attr('href'), '337141/') + eq_(rows.find('a').attr('href'), '/admin/models/addons/addon/337141/') class TestManifestRevalidation(amo.tests.TestCase): diff --git a/requirements/dev.txt b/requirements/dev.txt index 811838ff6b9..bca33439758 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,5 +1,5 @@ -r prod.txt -r compiled.txt -django-debug-toolbar==0.9.4 +django-debug-toolbar==0.11.0 django-fixture-magic==0.0.4 diff --git a/requirements/prod.txt b/requirements/prod.txt index 46974e0c99d..8f0880cbf72 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -16,11 +16,10 @@ cssutils==0.9.7b3 curling==0.2.6 defusedxml==0.4.1 dennis==0.3.10 -Django==1.4.8 +Django==1.6.2 dj-database-url==0.2.2 django-aesfield==0.1.2 django-browserid==0.8 -django-cache-machine==0.8 django-cache-nuggets==0.1.1 django-celery==3.0.23 django-cronjobs==0.2.3 @@ -29,10 +28,9 @@ django-extensions==1.2.5 django-filter==0.7 django-memcached-pool==0.4.1 django-multidb-router==0.5.1 -django-mysql-pool==0.2 django-mysql-pymysql==0.1 django-nose==1.2 -django-quieter-formset==0.3 +django-quieter-formset==0.4 django-pymemcache==0.1.1-fixserial django-raven-heka==0.3 django-recaptcha-mozilla==0.0.1 @@ -107,14 +105,14 @@ suds==0.4 # Temporary fork. -e git+https://github.com/jsocol/jingo-minify.git@b7405d3f93628190bf83cc43b0ff44e2fbb8b3c0#egg=jingo_minify --e git+https://github.com/mozilla/nuggets.git@96e80a64aa4bfcfef4f43fc3ab6966450ccd7325#egg=nuggets --e git+https://github.com/jbalogh/test-utils.git@ce5136a257cd44a1c663319124a255c1d10a9834#egg=test-utils --e git+https://github.com/fwenzel/django-mozilla-product-details.git@36ef06539d6b34c4f345fd0d3e16937d0db9a752#egg=django-mozilla-product-details +-e git+https://github.com/washort/nuggets.git@02798dfce84030fca64775eaf56e92400f394e4f#egg=nuggets +-e git+https://github.com/washort/test-utils.git@4917197a64b6d0f2068c9ad1bfea0427c3e412e1#egg=test-utils +-e git+https://github.com/fwenzel/django-mozilla-product-details@f5a3c3c846fb75e12ad890b22ed5375d5b85ccb4#egg=django-mozilla-product-details -e git+https://github.com/mozilla/signing-clients@a8bd730b202391c080113d224d223463e03088e9#egg=signing-clients -e git+https://github.com/mozilla/django-session-csrf@f00ad913c62e139d36078e8a7e07dab65a021386#egg=django-session-csrf - +-e git+https://github.com/washort/django-cache-machine@4690198122a96a88267323566dc18a5e0437681f#egg=django-cache-machine ## Forked. --e git+https://github.com/andymckay/django-piston-oauth2.git@fa28ee#egg=django-piston-oauth2 +-e git+https://github.com/andymckay/django-piston-oauth2.git@177aaf937860318af8d9c2bb74adc27860803eb9#egg=django-piston-oauth2 -e git+https://github.com/kumar303/django-qunit.git@b0f468dcf33439488158c845df37ef3261852b55#egg=django-qunit -e git+https://github.com/andymckay/django-uuidfield.git@029dd1263794ec36c327617cd6c2346da81c8c33#egg=django-uuidfield diff --git a/services/pfs.py b/services/pfs.py index 5c5111655fb..d29b192abde 100644 --- a/services/pfs.py +++ b/services/pfs.py @@ -6,7 +6,6 @@ from time import time from urlparse import parse_qsl -from django.core.management import setup_environ import commonware.log import jinja2 @@ -14,7 +13,6 @@ from utils import log_configure import settings_local as settings -setup_environ(settings) # This has to be imported after the settings so statsd knows where to log to. from django_statsd.clients import statsd diff --git a/services/theme_update.py b/services/theme_update.py index cf66612f061..fb347803c57 100644 --- a/services/theme_update.py +++ b/services/theme_update.py @@ -6,13 +6,10 @@ from time import time from wsgiref.handlers import format_date_time -from django.core.management import setup_environ - from constants import base from utils import log_configure, log_exception, mypool from services.utils import settings -setup_environ(settings) # Configure the log. log_configure() diff --git a/services/update.py b/services/update.py index 8fe51688626..ecff2dbea52 100644 --- a/services/update.py +++ b/services/update.py @@ -7,11 +7,10 @@ from time import time from urlparse import parse_qsl -from django.core.management import setup_environ from django.utils.http import urlencode import settings_local as settings -setup_environ(settings) + # This has to be imported after the settings so statsd knows where to log to. from django_statsd.clients import statsd diff --git a/services/utils.py b/services/utils.py index 7df2c35ba53..aa17e83f7f7 100644 --- a/services/utils.py +++ b/services/utils.py @@ -20,14 +20,11 @@ import MySQLdb as mysql import sqlalchemy.pool as pool -from django.core.management import setup_environ import commonware.log from django.utils import importlib settings = importlib.import_module(settingmodule) -# Pyflakes will complain about these, but they are required for setup. -setup_environ(settings) from lib.log_settings_base import formatters, handlers, loggers # Ugh. But this avoids any zamboni or django imports at all. diff --git a/services/verify.py b/services/verify.py index fc16de1ad32..4e9fca0516b 100644 --- a/services/verify.py +++ b/services/verify.py @@ -5,7 +5,6 @@ from urlparse import parse_qsl, urlparse from wsgiref.handlers import format_date_time -from django.core.management import setup_environ import jwt from browserid.errors import ExpiredSignatureError @@ -16,7 +15,6 @@ from services.utils import settings -setup_environ(settings) from utils import (CONTRIB_CHARGEBACK, CONTRIB_NO_CHARGE, CONTRIB_PURCHASE, CONTRIB_REFUND, diff --git a/settings_test.py b/settings_test.py index dc30342903d..0e1596a8832 100644 --- a/settings_test.py +++ b/settings_test.py @@ -81,7 +81,7 @@ def _polite_tmpdir(): # COUNT() caching can't be invalidated, it just expires after x seconds. This # is just too annoying for tests, so disable it. -CACHE_COUNT_TIMEOUT = None +CACHE_COUNT_TIMEOUT = -1 # No more failures! APP_PREVIEW = False @@ -129,8 +129,8 @@ def lazy_langs(languages): TASK_USER_ID = '4043307' PASSWORD_HASHERS = ( - 'users.models.SHA512PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', + 'users.models.SHA512PasswordHasher', ) SQL_RESET_SEQUENCES = False diff --git a/sites/altdev/settings_base.py b/sites/altdev/settings_base.py index 66cda9342a9..65c5d4e7aae 100644 --- a/sites/altdev/settings_base.py +++ b/sites/altdev/settings_base.py @@ -27,20 +27,12 @@ DATABASES = {} DATABASES['default'] = dj_database_url.parse(private.DATABASES_DEFAULT_URL) -DATABASES['default']['ENGINE'] = 'mysql_pool' DATABASES['default']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave'] = dj_database_url.parse(private.DATABASES_SLAVE_URL) -DATABASES['slave']['ENGINE'] = 'mysql_pool' DATABASES['slave']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave']['sa_pool_key'] = 'slave' -DATABASE_POOL_ARGS = { - 'max_overflow': 10, - 'pool_size': 5, - 'recycle': 30 -} - SERVICES_DATABASE = dj_database_url.parse(private.SERVICES_DATABASE_URL) SLAVE_DATABASES = ['slave'] diff --git a/sites/dev/settings_base.py b/sites/dev/settings_base.py index 83b150d2efc..c2ab0c949ed 100644 --- a/sites/dev/settings_base.py +++ b/sites/dev/settings_base.py @@ -27,20 +27,12 @@ DATABASES = {} DATABASES['default'] = dj_database_url.parse(private.DATABASES_DEFAULT_URL) -DATABASES['default']['ENGINE'] = 'mysql_pool' DATABASES['default']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave'] = dj_database_url.parse(private.DATABASES_SLAVE_URL) -DATABASES['slave']['ENGINE'] = 'mysql_pool' DATABASES['slave']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave']['sa_pool_key'] = 'slave' -DATABASE_POOL_ARGS = { - 'max_overflow': 10, - 'pool_size': 5, - 'recycle': 30 -} - SERVICES_DATABASE = dj_database_url.parse(private.SERVICES_DATABASE_URL) SLAVE_DATABASES = ['slave'] diff --git a/sites/identitystage/settings_base.py b/sites/identitystage/settings_base.py index 2fa02317ec7..f370fd6dd3e 100644 --- a/sites/identitystage/settings_base.py +++ b/sites/identitystage/settings_base.py @@ -25,21 +25,13 @@ DATABASES = {} DATABASES['default'] = dj_database_url.parse(private.DATABASES_DEFAULT_URL) -DATABASES['default']['ENGINE'] = 'mysql_pool' DATABASES['default']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave'] = dj_database_url.parse(private.DATABASES_SLAVE_URL) -DATABASES['slave']['ENGINE'] = 'mysql_pool' DATABASES['slave']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} SERVICES_DATABASE = dj_database_url.parse(private.SERVICES_DATABASE_URL) -DATABASE_POOL_ARGS = { - 'max_overflow': 10, - 'pool_size': 5, - 'recycle': 30 -} - SLAVE_DATABASES = ['slave'] CACHES = { diff --git a/sites/landfill/settings_base.py b/sites/landfill/settings_base.py index 6fb3bfa7e22..e17b46e7e10 100644 --- a/sites/landfill/settings_base.py +++ b/sites/landfill/settings_base.py @@ -25,23 +25,12 @@ DATABASES = {} DATABASES['default'] = dj_database_url.parse(private.DATABASES_DEFAULT_URL) -DATABASES['default']['ENGINE'] = 'mysql_pool' -DATABASES['default']['sa_pool_key'] = 'master' DATABASES['default']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave'] = dj_database_url.parse(private.DATABASES_SLAVE_URL) -DATABASES['slave']['ENGINE'] = 'mysql_pool' DATABASES['slave']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} -DATABASES['slave']['sa_pool_key'] = 'slave' SERVICES_DATABASE = dj_database_url.parse(private.SERVICES_DATABASE_URL) - -DATABASE_POOL_ARGS = { - 'max_overflow': 10, - 'pool_size': 5, - 'recycle': 30 -} - SLAVE_DATABASES = ['slave'] CACHES = { diff --git a/sites/prod/settings_base.py b/sites/prod/settings_base.py index 011820f9af3..25bcba355f8 100644 --- a/sites/prod/settings_base.py +++ b/sites/prod/settings_base.py @@ -24,19 +24,11 @@ DATABASES = {} DATABASES['default'] = dj_database_url.parse(private.DATABASES_DEFAULT_URL) -DATABASES['default']['ENGINE'] = 'mysql_pool' DATABASES['default']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave'] = dj_database_url.parse(private.DATABASES_SLAVE_URL) -DATABASES['slave']['ENGINE'] = 'mysql_pool' DATABASES['slave']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} -DATABASE_POOL_ARGS = { - 'max_overflow': 10, - 'pool_size': 5, - 'recycle': 300 -} - SERVICES_DATABASE = dj_database_url.parse(private.SERVICES_DATABASE_URL) SLAVE_DATABASES = ['slave'] diff --git a/sites/stage/settings_base.py b/sites/stage/settings_base.py index 03af574d6b9..a9ef1d8f5a8 100644 --- a/sites/stage/settings_base.py +++ b/sites/stage/settings_base.py @@ -25,21 +25,13 @@ DATABASES = {} DATABASES['default'] = dj_database_url.parse(private.DATABASES_DEFAULT_URL) -DATABASES['default']['ENGINE'] = 'mysql_pool' DATABASES['default']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} DATABASES['slave'] = dj_database_url.parse(private.DATABASES_SLAVE_URL) -DATABASES['slave']['ENGINE'] = 'mysql_pool' DATABASES['slave']['OPTIONS'] = {'init_command': 'SET storage_engine=InnoDB'} SERVICES_DATABASE = dj_database_url.parse(private.SERVICES_DATABASE_URL) -DATABASE_POOL_ARGS = { - 'max_overflow': 10, - 'pool_size': 5, - 'recycle': 30 -} - SLAVE_DATABASES = ['slave'] CACHES = {