reserve the slugs "favorites" and "mobile" (bug 586426)
Also adding a unique key on (author, slug) and fixing up tests.
This commit is contained in:
Родитель
fab5aceeae
Коммит
75b42a47d6
|
@ -145,24 +145,3 @@ def collections_add_slugs():
|
|||
c.slug = 'collection'
|
||||
c.save(force_update=True)
|
||||
task_log.info(u'%s. %s => %s' % (next(cnt), c.name, c.slug))
|
||||
|
||||
# Uniquify slug names by user.
|
||||
cursor = connections[multidb.get_slave()].cursor()
|
||||
dupes = cursor.execute("""
|
||||
SELECT user_id, slug FROM (
|
||||
SELECT user_id, slug, COUNT(1) AS cnt
|
||||
FROM collections c INNER JOIN collections_users cu
|
||||
ON c.id = cu.collection_id
|
||||
GROUP BY user_id, slug) j
|
||||
WHERE j.cnt > 1""")
|
||||
task_log.info('Uniquifying %s (user, slug) pairs' % dupes)
|
||||
cnt = itertools.count()
|
||||
for user, slug in cursor.fetchall():
|
||||
q = Collection.objects.filter(slug=slug, collectionuser__user=user)
|
||||
# Skip the first one since it's unique without any appendage.
|
||||
for idx, c in enumerate(q[1:]):
|
||||
# Give enough space for appending a two-digit number.
|
||||
slug = c.slug[:max_length - 3]
|
||||
c.slug = u'%s-%s' % (slug, idx + 1)
|
||||
c.save(force_update=True)
|
||||
task_log.info(u'%s. %s => %s' % (next(cnt), slug, c.slug))
|
||||
|
|
|
@ -20,6 +20,12 @@ from users.models import UserProfile
|
|||
from translations.fields import TranslatedField, LinkifiedField
|
||||
|
||||
|
||||
SPECIAL_SLUGS = {
|
||||
'mobile': amo.COLLECTION_MOBILE,
|
||||
'favorites': amo.COLLECTION_FAVORITES,
|
||||
}
|
||||
|
||||
|
||||
class TopTags(object):
|
||||
"""Descriptor to manage a collection's top tags in cache."""
|
||||
|
||||
|
@ -108,6 +114,7 @@ class Collection(amo.models.ModelBase):
|
|||
|
||||
class Meta(amo.models.ModelBase.Meta):
|
||||
db_table = 'collections'
|
||||
unique_together = (('author', 'slug'),)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s (%s)' % (self.name, self.addon_count)
|
||||
|
@ -117,6 +124,7 @@ class Collection(amo.models.ModelBase):
|
|||
self.uuid = unicode(uuid.uuid4())
|
||||
if not self.slug:
|
||||
self.slug = self.uuid[:30]
|
||||
self.clean_slug()
|
||||
|
||||
# Maintain our index of add-on ids.
|
||||
if self.id:
|
||||
|
@ -125,6 +133,21 @@ class Collection(amo.models.ModelBase):
|
|||
|
||||
super(Collection, self).save(**kw)
|
||||
|
||||
def clean_slug(self):
|
||||
if (self.slug in SPECIAL_SLUGS and
|
||||
self.type != SPECIAL_SLUGS[self.slug]):
|
||||
self.slug += '~'
|
||||
if not self.author:
|
||||
return
|
||||
qs = self.author.collections.using('default')
|
||||
slugs = dict((slug, id) for slug, id in qs.values_list('slug', 'id'))
|
||||
if self.slug in slugs and slugs[self.slug] != self.id:
|
||||
for idx in range(len(slugs)):
|
||||
new = '%s-%s' % (self.slug, idx + 1)
|
||||
if new not in slugs:
|
||||
self.slug = new
|
||||
return
|
||||
|
||||
def get_url_path(self):
|
||||
if settings.NEW_COLLECTIONS:
|
||||
return reverse('collections.detail',
|
||||
|
|
|
@ -4,6 +4,7 @@ from pyquery import PyQuery as pq
|
|||
|
||||
from amo.urlresolvers import reverse
|
||||
from bandwagon.models import Collection
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
class AjaxTest(test_utils.TestCase):
|
||||
|
@ -12,9 +13,10 @@ class AjaxTest(test_utils.TestCase):
|
|||
'base/collections')
|
||||
|
||||
def setUp(self):
|
||||
status = self.client.login(username='clouserw@gmail.com',
|
||||
password='yermom')
|
||||
assert status, "Didn't log in."
|
||||
assert self.client.login(username='clouserw@gmail.com',
|
||||
password='yermom')
|
||||
self.user = UserProfile.objects.get(email='clouserw@gmail.com')
|
||||
self.other = UserProfile.objects.exclude(id=self.user.id)[0]
|
||||
|
||||
def test_list_collections(self):
|
||||
r = self.client.get(reverse('collections.ajax_list')
|
||||
|
@ -54,7 +56,7 @@ class AjaxTest(test_utils.TestCase):
|
|||
|
||||
def test_add_other_collection(self):
|
||||
"403 when you try to add to a collection that isn't yours."
|
||||
c = Collection()
|
||||
c = Collection(author=self.other)
|
||||
c.save()
|
||||
|
||||
r = self.client.post(reverse('collections.ajax_add'),
|
||||
|
@ -63,7 +65,7 @@ class AjaxTest(test_utils.TestCase):
|
|||
|
||||
def test_remove_other_collection(self):
|
||||
"403 when you try to add to a collection that isn't yours."
|
||||
c = Collection()
|
||||
c = Collection(author=self.other)
|
||||
c.save()
|
||||
|
||||
r = self.client.post(reverse('collections.ajax_remove'),
|
||||
|
|
|
@ -24,14 +24,12 @@ class TestHelpers(test.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.client.get('/')
|
||||
self.user = UserProfile.objects.create(nickname='uniq', email='uniq')
|
||||
|
||||
def test_collection_favorite(self):
|
||||
c = {}
|
||||
u = UserProfile()
|
||||
u.nickname = 'unique'
|
||||
u.save()
|
||||
c['request'] = Mock()
|
||||
c['request'].amo_user = u
|
||||
c['request'].amo_user = self.user
|
||||
collection = Collection.objects.get(pk=80)
|
||||
|
||||
# Not subscribed yet.
|
||||
|
@ -39,7 +37,7 @@ class TestHelpers(test.TestCase):
|
|||
eq_(doc('button').text(), u'Add to Favorites')
|
||||
|
||||
# Subscribed.
|
||||
collection.subscriptions.create(user=u)
|
||||
collection.subscriptions.create(user=self.user)
|
||||
doc = pq(collection_favorite(c, collection))
|
||||
eq_(doc('button').text(), u'Remove from Favorites')
|
||||
|
||||
|
@ -66,9 +64,9 @@ class TestHelpers(test.TestCase):
|
|||
reverse('collections.vote', args=['clouserw', 'mccrackin', 'up']))
|
||||
|
||||
def test_user_collection_list(self):
|
||||
c1 = Collection.objects.create(
|
||||
c1 = Collection.objects.create(author=self.user,
|
||||
uuid='eb4e3cd8-5cf1-4832-86fb-a90fc6d3765c')
|
||||
c2 = Collection.objects.create(
|
||||
c2 = Collection.objects.create(author=self.user,
|
||||
uuid='61780943-e159-4206-8acd-0ae9f63f294c',
|
||||
nickname='my_collection')
|
||||
heading = 'My Heading'
|
||||
|
|
|
@ -24,17 +24,18 @@ class TestCollections(test_utils.TestCase):
|
|||
'bandwagon/test_models'
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.user = UserProfile.objects.create(nickname='uhhh', email='uh@hh')
|
||||
self.other = UserProfile.objects.exclude(id=self.user.id)[0]
|
||||
|
||||
def test_icon_url(self):
|
||||
c = Collection.objects.get(pk=512)
|
||||
assert c.icon_url.endswith('img/amo2009/icons/collection.png')
|
||||
|
||||
def test_is_subscribed(self):
|
||||
c = Collection.objects.get(pk=512)
|
||||
u = UserProfile()
|
||||
u.nickname = 'unique'
|
||||
u.save()
|
||||
c.subscriptions.create(user=u)
|
||||
assert c.is_subscribed(u), "User isn't subscribed to collection."
|
||||
c.subscriptions.create(user=self.user)
|
||||
assert c.is_subscribed(self.user)
|
||||
|
||||
def test_translation_default(self):
|
||||
"""Make sure we're getting strings from the default locale."""
|
||||
|
@ -47,19 +48,20 @@ class TestCollections(test_utils.TestCase):
|
|||
# make a private collection
|
||||
private = Collection(
|
||||
name="Hello", uuid="4e2a1acc-39ae-47ec-956f-46e080ac7f69",
|
||||
listed=False)
|
||||
listed=False, author=self.user)
|
||||
private.save()
|
||||
|
||||
listed = Collection.objects.listed()
|
||||
eq_(len(listed), listed_count)
|
||||
|
||||
def test_auto_uuid(self):
|
||||
c = Collection.objects.create()
|
||||
c = Collection.objects.create(author=self.user)
|
||||
assert c.uuid != ''
|
||||
assert isinstance(c.uuid, basestring)
|
||||
|
||||
def test_addon_index(self):
|
||||
c = Collection.objects.get(pk=80)
|
||||
c.author = self.user
|
||||
eq_(c.addon_index, None)
|
||||
ids = c.addons.values_list('id', flat=True)
|
||||
c.save()
|
||||
|
@ -67,17 +69,17 @@ class TestCollections(test_utils.TestCase):
|
|||
|
||||
def test_synced_collection(self):
|
||||
"""SyncedCollections automatically get type=sync."""
|
||||
c = SyncedCollection.objects.create()
|
||||
c = SyncedCollection.objects.create(author=self.user)
|
||||
eq_(c.type, amo.COLLECTION_SYNCHRONIZED)
|
||||
|
||||
def test_recommended_collection(self):
|
||||
"""RecommendedCollections automatically get type=rec."""
|
||||
c = RecommendedCollection.objects.create()
|
||||
c = RecommendedCollection.objects.create(author=self.user)
|
||||
eq_(c.type, amo.COLLECTION_RECOMMENDED)
|
||||
|
||||
def test_set_addons(self):
|
||||
addons = list(Addon.objects.values_list('id', flat=True))
|
||||
c = Collection.objects.create()
|
||||
c = Collection.objects.create(author=self.user)
|
||||
|
||||
# Check insert.
|
||||
random.shuffle(addons)
|
||||
|
@ -96,26 +98,45 @@ class TestCollections(test_utils.TestCase):
|
|||
eq_(c.addons.count(), len(addons))
|
||||
|
||||
def test_publishable_by(self):
|
||||
c = Collection()
|
||||
u = UserProfile(nickname='f')
|
||||
c.save()
|
||||
u.save()
|
||||
CollectionUser(collection=c, user=u).save()
|
||||
eq_(c.publishable_by(u), True)
|
||||
c = Collection.objects.create(author=self.other)
|
||||
CollectionUser(collection=c, user=self.user).save()
|
||||
eq_(c.publishable_by(self.user), True)
|
||||
|
||||
def test_collection_meta(self):
|
||||
c = Collection.objects.create()
|
||||
c = Collection.objects.create(author=self.user)
|
||||
eq_(c.addon_count, 0)
|
||||
c.add_addon(Addon.objects.all()[0])
|
||||
c = Collection.objects.get(id=c.id)
|
||||
assert not c.from_cache
|
||||
eq_(c.addon_count, 1)
|
||||
|
||||
def test_favorites_slug(self):
|
||||
c = Collection.objects.create(author=self.user, slug='favorites')
|
||||
eq_(c.type, amo.COLLECTION_NORMAL)
|
||||
eq_(c.slug, 'favorites~')
|
||||
|
||||
c = Collection.objects.create(author=self.user, slug='favorites')
|
||||
eq_(c.type, amo.COLLECTION_NORMAL)
|
||||
eq_(c.slug, 'favorites~-1')
|
||||
|
||||
def test_slug_dupe(self):
|
||||
c = Collection.objects.create(author=self.user, slug='boom')
|
||||
eq_(c.slug, 'boom')
|
||||
c.save()
|
||||
eq_(c.slug, 'boom')
|
||||
c = Collection.objects.create(author=self.user, slug='boom')
|
||||
eq_(c.slug, 'boom-1')
|
||||
c = Collection.objects.create(author=self.user, slug='boom')
|
||||
eq_(c.slug, 'boom-2')
|
||||
|
||||
|
||||
class TestRecommendations(test_utils.TestCase):
|
||||
fixtures = ['base/addon-recs']
|
||||
ids = [5299, 1843, 2464, 7661, 5369]
|
||||
|
||||
def setUp(self):
|
||||
self.user = UserProfile.objects.create(nickname='uhhh', email='uh@hh')
|
||||
|
||||
def expected_recs(self):
|
||||
scores, ranked = [], {}
|
||||
# Get all the add-on => rank pairs.
|
||||
|
@ -133,7 +154,7 @@ class TestRecommendations(test_utils.TestCase):
|
|||
eq_(recs, self.expected_recs())
|
||||
|
||||
def test_get_recommendations(self):
|
||||
c = Collection.objects.create()
|
||||
c = Collection.objects.create(author=self.user)
|
||||
c.set_addons(self.ids)
|
||||
recs = c.get_recommendations()
|
||||
eq_(recs.type, amo.COLLECTION_RECOMMENDED)
|
||||
|
|
|
@ -17,11 +17,10 @@ from addons.models import Addon
|
|||
from addons.views import BaseFilter
|
||||
from tags.models import Tag
|
||||
from translations.query import order_by_translation
|
||||
from .models import Collection, CollectionAddon, CollectionUser, CollectionVote
|
||||
from .models import (Collection, CollectionAddon, CollectionUser,
|
||||
CollectionVote, SPECIAL_SLUGS)
|
||||
from . import forms
|
||||
|
||||
|
||||
SPECIAL_COLLECTIONS = ['mobile', 'favorites']
|
||||
log = commonware.log.getLogger('z.collections')
|
||||
|
||||
|
||||
|
@ -258,7 +257,7 @@ def ajax_list(request):
|
|||
@login_required
|
||||
@post_required
|
||||
def collection_alter(request, username, slug, action):
|
||||
if slug in SPECIAL_COLLECTIONS:
|
||||
if slug in SPECIAL_SLUGS:
|
||||
c = getattr(request.amo_user, slug + '_collection')()
|
||||
else:
|
||||
c = get_object_or_404(Collection.objects,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE collections ADD UNIQUE (`author_id`, `slug`);
|
Загрузка…
Ссылка в новой задаче