From b82f205ed5c99cdcf41d981f6bdfb3222370254c Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Tue, 16 Oct 2012 17:31:21 -0300 Subject: made height and line-height the same on most text input elements for better text vertical alignment --- askbot/media/style/style.css | 46 +++++++++++++++++++++++++++++++++++++------ askbot/media/style/style.less | 20 ++++++++++++------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/askbot/media/style/style.css b/askbot/media/style/style.css index 3abbd362..79bc422b 100644 --- a/askbot/media/style/style.css +++ b/askbot/media/style/style.css @@ -139,6 +139,9 @@ blockquote { padding: 10px 0px 1px 10px; background-color: #F5F5F5; } +html { + overflow-y: scroll; +} /* http://pathfindersoftware.com/2007/09/developers-note-2/ */ * html .clearfix, * html .paginator { @@ -444,6 +447,7 @@ body.user-messages { #searchBar .searchInputCancelable { font-size: 26px; height: 39px; + line-height: 39px; font-weight: 300; background: #FFF; border: 0px; @@ -484,6 +488,7 @@ body.user-messages { color: #ce8888; background: #fff; height: 42px; + line-height: 42px; border: 0px; border-left: #deded0 1px solid; text-align: center; @@ -661,6 +666,7 @@ body.anon #searchBar .searchInputCancelable { padding-left: 5px; border: #c9c9b5 1px solid; height: 25px; + line-height: 25px; font-size: 14px; } .box .inputs #ab-tag-search { @@ -1434,8 +1440,8 @@ ul#related-tags li { } #askFormBar .questionTitleInput { font-size: 24px; - line-height: 24px; height: 36px; + line-height: 36px; margin: 0px; padding: 0px 0 0 5px; border: #cce6ec 3px solid; @@ -1501,6 +1507,7 @@ ul#related-tags li { .edit-answer-page #id_post_author_email { border: #cce6ec 3px solid; height: 25px; + line-height: 25px; padding-left: 5px; font-size: 14px; width: 186px; @@ -1815,6 +1822,24 @@ ul#related-tags li { .wmd-preview IMG { max-width: 600px; } +.defaultSkin table.mceLayout, +.defaultSkin table.mceLayout tr.mceFirst td { + border: none; +} +.defaultSkin table.mceLayout tr.mceLast td { + border-bottom: none; +} +.mceStatusbar { + height: 5px; + background: #fff; +} +.defaultSkin span.mce_askbot_imageuploader { + background-position: -380px 0px; +} +.defaultSkin span.mce_askbot_attachment { + background-image: url(../images/attachment.png); + background-position: 0px 0px; +} .user-page .wmd-buttons { width: 725px; } @@ -2053,16 +2078,24 @@ ul#related-tags li { } .question-page .post-controls .question-delete, .question-page .answer-controls .question-delete { - background: url(../images/delete.png) no-repeat center left; + background: url(../images/delete.png) no-repeat left 2px; padding-left: 11px; } .question-page .post-controls .question-flag, .question-page .answer-controls .question-flag { background: url(../images/flag.png) no-repeat center left; } +.question-page .post-controls .answer-publish, +.question-page .answer-controls .answer-publish { + background: url(../images/publish.png) no-repeat center left; +} +.question-page .post-controls .answer-unpublish, +.question-page .answer-controls .answer-unpublish { + background: url(../images/unpublish.png) no-repeat 2px center; +} .question-page .post-controls .question-edit, .question-page .answer-controls .question-edit { - background: url(../images/edit2.png) no-repeat center left; + background: url(../images/edit2.png) no-repeat 2px center; } .question-page .post-controls .question-retag, .question-page .answer-controls .question-retag { @@ -2607,6 +2640,7 @@ ul#related-tags li { .user-profile-page select { border: #cce6ec 3px solid; height: 25px; + line-height: 25px; padding-left: 5px; width: 395px; font-size: 14px; @@ -2886,11 +2920,10 @@ a:hover.medal { /* profile page */ .tabBar-profile { width: 100%; - margin-bottom: 15px; + margin-bottom: 5px; float: left; } .user-profile-page { - font-size: 13px; color: #525252; } .user-profile-page p { @@ -3301,7 +3334,7 @@ table.ab-subscr-form { } .errors { line-height: 20px; - color: red; + color: #990000; } .error, .openid-signin p.error { @@ -4084,6 +4117,7 @@ textarea.tipped-input { border: none; font-size: 15px; font-color: #707070; + height: 16px; line-height: 16px; margin-top: 9px; -webkit-box-shadow: none; diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less index a7d035ed..9e2e2a25 100644 --- a/askbot/media/style/style.less +++ b/askbot/media/style/style.less @@ -469,6 +469,7 @@ body.user-messages { .searchInput, .searchInputCancelable{ font-size: 26px; height: 39px; + line-height: 39px; font-weight:300; background:#FFF; border:0px; @@ -515,6 +516,7 @@ body.user-messages { color: #ce8888; background:#fff; height: 42px; + line-height: 42px; border:0px; border-left:#deded0 1px solid; text-align: center; @@ -669,6 +671,7 @@ body.anon { padding-left:5px; border:#c9c9b5 1px solid; height:25px; + line-height:25px; font-size: 14px; } #ab-tag-search { @@ -1433,8 +1436,8 @@ ul#related-tags li { } .questionTitleInput { font-size: 24px; - line-height: 24px; height: 36px; + line-height: 36px; margin: 0px; padding: 0px 0 0 5px; border:#cce6ec 3px solid; @@ -1496,11 +1499,12 @@ ul#related-tags li { .edit-answer-page { #id_post_author_username, #id_post_author_email { - border:#cce6ec 3px solid; - height:25px; - padding-left:5px; - font-size:14px; - width:186px; + border: #cce6ec 3px solid; + height: 25px; + line-height: 25px; + padding-left: 5px; + font-size: 14px; + width: 186px; } #id_post_author_email { margin-left: 10px; @@ -2503,6 +2507,7 @@ ul#related-tags li { input[type="text"],input[type="password"],select{ border:#cce6ec 3px solid; height:25px; + line-height: 25px; padding-left:5px; width:395px; font-size:14px; @@ -2564,7 +2569,7 @@ ul#related-tags li { #email-input-fs,#local_login_buttons,#password-fs,#openid-fs{ margin-top:10px; #id_email,#id_username,#id_password{ - font-size: 12px; + font-size: 12px; line-height: 20px; height: 20px; margin: 0px; @@ -3974,6 +3979,7 @@ textarea.tipped-input { border: none; font-size: 15px; font-color: @info-text; + height: 16px; line-height: 16px; margin-top: 9px; -webkit-box-shadow: none;/* undo bootstrap glow */ -- cgit v1.2.3-1-g7c22 From 5ebe59eca6b06119a986532a89666400da563001 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Tue, 16 Oct 2012 22:04:44 -0300 Subject: fixed a bug where user cannot be deleted with postgres db backend in the django admin --- .../migrations/0154_update_postgres_user_search.py | 390 +++++++++++++++++++++ .../postgresql/user_profile_search_16102012.plsql | 98 ++++++ askbot/tests/user_model_tests.py | 10 + 3 files changed, 498 insertions(+) create mode 100644 askbot/migrations/0154_update_postgres_user_search.py create mode 100644 askbot/search/postgresql/user_profile_search_16102012.plsql diff --git a/askbot/migrations/0154_update_postgres_user_search.py b/askbot/migrations/0154_update_postgres_user_search.py new file mode 100644 index 00000000..54bb5c50 --- /dev/null +++ b/askbot/migrations/0154_update_postgres_user_search.py @@ -0,0 +1,390 @@ +# encoding: utf-8 +import askbot +import datetime +import os +from askbot.search import postgresql +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + db_engine_name = askbot.get_database_engine_name() + if 'postgresql_psycopg2' in db_engine_name: + script_path = os.path.join( + askbot.get_install_directory(), + 'search', + 'postgresql', + 'user_profile_search_16102012.plsql' + ) + postgresql.setup_full_text_search(script_path) + + + def backwards(self, orm): + "Write your backwards methods here." + + + models = { + 'askbot.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True'}), + 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}), + 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.activityauditstatus': { + 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'}, + 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.anonymousanswer': { + 'Meta': {'object_name': 'AnonymousAnswer'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Post']"}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.askwidget': { + 'Meta': {'object_name': 'AskWidget'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'askbot.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'askbot.badgedata': { + 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}) + }, + 'askbot.draftanswer': { + 'Meta': {'object_name': 'DraftAnswer'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"}) + }, + 'askbot.draftquestion': { + 'Meta': {'object_name': 'DraftQuestion'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}) + }, + 'askbot.emailfeedsetting': { + 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"}) + }, + 'askbot.favoritequestion': { + 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"}) + }, + 'askbot.group': { + 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']}, + 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}), + 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}), + 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}), + 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}), + 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}) + }, + 'askbot.groupmembership': { + 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']}, + 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}), + 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}) + }, + 'askbot.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"}) + }, + 'askbot.post': { + 'Meta': {'object_name': 'Post'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_posts'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_posts'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}), + 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.postflagreason': { + 'Meta': {'object_name': 'PostFlagReason'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'askbot.postrevision': { + 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'}, + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}), + 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '125', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + 'askbot.posttogroup': { + 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"}) + }, + 'askbot.questionview': { + 'Meta': {'object_name': 'QuestionView'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}), + 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"}) + }, + 'askbot.questionwidget': { + 'Meta': {'object_name': 'QuestionWidget'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}), + 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}), + 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'askbot.replyaddress': { + 'Meta': {'object_name': 'ReplyAddress'}, + 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}), + 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}), + 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.tag': { + 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.thread': { + 'Meta': {'object_name': 'Thread'}, + 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': "orm['auth.User']"}), + 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}), + 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.threadtogroup': { + 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}), + 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}) + }, + 'askbot.vote': { + 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}), + 'vote': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"}) + }, + 'auth.authusergroups': { + 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/search/postgresql/user_profile_search_16102012.plsql b/askbot/search/postgresql/user_profile_search_16102012.plsql new file mode 100644 index 00000000..ca00a5d3 --- /dev/null +++ b/askbot/search/postgresql/user_profile_search_16102012.plsql @@ -0,0 +1,98 @@ +/* +Script depends on functions defined for general askbot full text search. +to_tsvector(), add_tsvector_column() + +calculates text search vector for the user profile +the searched fields are: +1) user name +2) user profile +3) group names - for groups to which user belongs +*/ +CREATE OR REPLACE FUNCTION get_auth_user_tsv(user_id integer) +RETURNS tsvector AS +$$ +DECLARE + group_query text; + user_query text; + onerow record; + tsv tsvector; +BEGIN + group_query = + 'SELECT user_group.name as group_name ' || + 'FROM auth_group AS user_group ' || + 'INNER JOIN auth_user_groups AS gm ' || + 'ON gm.user_id= ' || user_id || ' AND gm.group_id=user_group.id'; + + tsv = to_tsvector(''); + FOR onerow in EXECUTE group_query LOOP + tsv = tsv || to_tsvector(onerow.group_name); + END LOOP; + + user_query = 'SELECT username, about FROM auth_user WHERE id=' || user_id; + FOR onerow in EXECUTE user_query LOOP + tsv = tsv || to_tsvector(onerow.username) || to_tsvector(onerow.about); + END LOOP; + RETURN tsv; +END; +$$ LANGUAGE plpgsql; + +/* create tsvector columns in the content tables */ +SELECT add_tsvector_column('text_search_vector', 'auth_user'); + +/* populate tsvectors with data */ +UPDATE auth_user SET text_search_vector = get_auth_user_tsv(id); + +/* one trigger per table for tsv updates */ + +/* set up auth_user triggers */ +CREATE OR REPLACE FUNCTION auth_user_tsv_update_handler() +RETURNS trigger AS +$$ +BEGIN + new.text_search_vector = get_auth_user_tsv(new.id); + RETURN new; +END; +$$ LANGUAGE plpgsql; +DROP TRIGGER IF EXISTS auth_user_tsv_update_trigger ON auth_user; + +CREATE TRIGGER auth_user_tsv_update_trigger +BEFORE INSERT OR UPDATE ON auth_user +FOR EACH ROW EXECUTE PROCEDURE auth_user_tsv_update_handler(); + +/* group membership trigger - reindex users when group membership + * changes */ +CREATE OR REPLACE FUNCTION group_membership_tsv_update_handler() +RETURNS trigger AS +$$ +DECLARE + tsv tsvector; + user_query text; +BEGIN + IF (TG_OP = 'INSERT') THEN + user_query = 'UPDATE auth_user SET username=username WHERE ' || + 'id=' || new.user_id; + ELSE + user_query = 'UPDATE auth_user SET username=username WHERE ' || + 'id=' || old.user_id; + END IF; + /* just trigger the tsv update on user */ + EXECUTE user_query; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS group_membership_tsv_update_trigger +ON auth_user_groups; + +CREATE TRIGGER group_membership_tsv_update_trigger +AFTER INSERT OR DELETE +ON auth_user_groups +FOR EACH ROW EXECUTE PROCEDURE group_membership_tsv_update_handler(); + +/* todo: whenever group name changes - also + * reindex users belonging to the group */ + +DROP INDEX IF EXISTS auth_user_search_idx; + +CREATE INDEX auth_user_search_idx ON auth_user +USING gin(text_search_vector); diff --git a/askbot/tests/user_model_tests.py b/askbot/tests/user_model_tests.py index df4974dd..e46cdb77 100644 --- a/askbot/tests/user_model_tests.py +++ b/askbot/tests/user_model_tests.py @@ -15,3 +15,13 @@ class UserModelTests(AskbotTestCase): group=group, user=user ) self.assertEqual(memberships.count(), 1) + + def test_delete_user(self): + user = self.create_user('user') + user.delete() + self.assertRaises(User.DoesNotExist, User.objects.get, username='user') + + def test_rename_user(self): + user = self.create_user('user') + user.username = 'user2' + user.save() -- cgit v1.2.3-1-g7c22 From 86ad5f868338fbee2b05ee5d66b8c6281a7b7fd7 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Tue, 16 Oct 2012 23:19:14 -0300 Subject: cleaned up mysql-specific utility functions --- .../0004_install_full_text_indexes_for_mysql.py | 18 +++++++++-- askbot/models/post.py | 2 +- askbot/search/mysql.py | 29 +++++------------ askbot/utils/mysql.py | 36 ---------------------- 4 files changed, 23 insertions(+), 62 deletions(-) delete mode 100644 askbot/utils/mysql.py diff --git a/askbot/migrations/0004_install_full_text_indexes_for_mysql.py b/askbot/migrations/0004_install_full_text_indexes_for_mysql.py index 7c7c52f0..52321b90 100644 --- a/askbot/migrations/0004_install_full_text_indexes_for_mysql.py +++ b/askbot/migrations/0004_install_full_text_indexes_for_mysql.py @@ -3,11 +3,12 @@ import os import datetime from south.db import db from south.v2 import DataMigration +from django.db import connection from django.db import models -from askbot.utils import mysql Q_INDEX_NAME = 'askbot_question_full_text_index' A_INDEX_NAME = 'askbot_answer_full_text_index' +SUPPORTS_FTS = None NO_FTS_WARNING = """ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -18,6 +19,17 @@ NO_FTS_WARNING = """ !! !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! """ +def supports_full_text_search(): + global SUPPORTS_FTS + if SUPPORTS_FTS is None: + cursor = connection.cursor() + cursor.execute("SHOW CREATE TABLE question") # In migration 0004 model forum.Question used db table `question` + data = cursor.fetchone() + if 'ENGINE=MyISAM' in data[1]: + SUPPORTS_FTS = True + else: + SUPPORTS_FTS = False + return SUPPORTS_FTS def get_create_full_text_index_sql(index_name, table_name, column_list): column_sql = '(%s)' % ','.join(column_list) @@ -35,7 +47,7 @@ class Migration(DataMigration): and will probably fail otherwise """ if db.backend_name == 'mysql': - if mysql.supports_full_text_search_migr0004(): + if supports_full_text_search_migr0004(): #todo: extract column names by introspection question_index_sql = get_create_full_text_index_sql( Q_INDEX_NAME, @@ -55,7 +67,7 @@ class Migration(DataMigration): def backwards(self, orm): "code for removal of full text indices in mysql" - if db.backend_name == 'mysql' and mysql.supports_full_text_search(): + if db.backend_name == 'mysql' and supports_full_text_search(): db.execute( get_drop_index_sql( Q_INDEX_NAME, diff --git a/askbot/models/post.py b/askbot/models/post.py index 10b3cdc7..0c5e2b00 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -40,7 +40,7 @@ from askbot.models.base import BaseQuerySetManager, DraftContent #todo: maybe merge askbot.utils.markup and forum.utils.html from askbot.utils.diff import textDiff as htmldiff -from askbot.utils import mysql +from askbot.search import mysql class PostToGroup(models.Model): post = models.ForeignKey('Post') diff --git a/askbot/search/mysql.py b/askbot/search/mysql.py index df86070d..055b30ca 100644 --- a/askbot/search/mysql.py +++ b/askbot/search/mysql.py @@ -1,7 +1,6 @@ +"""Utilities for the MySQL backend""" from django.db import connection -SUPPORTS_FTS = None -HINT_TABLE = None NO_FTS_WARNING = """ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! !! @@ -11,22 +10,15 @@ NO_FTS_WARNING = """ !! !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! """ +SUPPORTS_FTS = None -def supports_full_text_search(hint_table = None): - """True if the database engine is MyISAM - hint_table - is the table that we look into to determine - whether database supports FTS or not. - """ +def supports_full_text_search(): + """True if the database engine is MyISAM""" + from askbot.models import Post global SUPPORTS_FTS - global HINT_TABLE if SUPPORTS_FTS is None: cursor = connection.cursor() - if hint_table: - table_name = hint_table - HINT_TABLE = hint_table - else: - from askbot.models import Post - table_name = Post._meta.db_table + table_name = Post._meta.db_table cursor.execute("SHOW CREATE TABLE %s" % table_name) data = cursor.fetchone() if 'ENGINE=MyISAM' in data[1]: @@ -35,19 +27,12 @@ def supports_full_text_search(hint_table = None): SUPPORTS_FTS = False return SUPPORTS_FTS - question_index_sql = get_create_full_text_index_sql( - index_name, - table_namee, - ('title','text','tagnames',) - ) def get_create_full_text_index_sql(index_name, table_name, column_list): cursor = connection.cursor() column_sql = '(%s)' % ','.join(column_list) sql = 'CREATE FULLTEXT INDEX %s on %s %s' % (index_name, table_name, column_sql) - cursor.execute(question_index_sql) + cursor.execute(sql) return sql - else: - print NO_FTS_WARNING def get_drop_index_sql(index_name, table_name): return 'ALTER TABLE %s DROP INDEX %s' % (table_name, index_name) diff --git a/askbot/utils/mysql.py b/askbot/utils/mysql.py deleted file mode 100644 index cf0e71ef..00000000 --- a/askbot/utils/mysql.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Utilities for the MySQL backend""" -from django.db import connection - -#in-memory cached variable -SUPPORTS_FTS = None - -def supports_full_text_search(): - """True if the database engine is MyISAM""" - from askbot.models import Post - global SUPPORTS_FTS - if SUPPORTS_FTS is None: - cursor = connection.cursor() - table_name = Post._meta.db_table - cursor.execute("SHOW CREATE TABLE %s" % table_name) - data = cursor.fetchone() - if 'ENGINE=MyISAM' in data[1]: - SUPPORTS_FTS = True - else: - SUPPORTS_FTS = False - return SUPPORTS_FTS - - -# This is needed to maintain compatibility with the old 0004 migration -# Usually South migrations should be self-contained and shouldn't depend on anything but themselves, -# but 0004 is an unfortunate exception -def supports_full_text_search_migr0004(): - global SUPPORTS_FTS - if SUPPORTS_FTS is None: - cursor = connection.cursor() - cursor.execute("SHOW CREATE TABLE question") # In migration 0004 model forum.Question used db table `question` - data = cursor.fetchone() - if 'ENGINE=MyISAM' in data[1]: - SUPPORTS_FTS = True - else: - SUPPORTS_FTS = False - return SUPPORTS_FTS -- cgit v1.2.3-1-g7c22 From ed59d636fa57c8a381a9fb5e19a21c3fde5fce10 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Thu, 18 Oct 2012 02:00:45 -0300 Subject: fixed bugs causing test cases to fail for MySQL backend --- .../0004_install_full_text_indexes_for_mysql.py | 2 +- askbot/models/question.py | 27 ++++++++++++++-------- askbot/tests/db_api_tests.py | 4 ++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/askbot/migrations/0004_install_full_text_indexes_for_mysql.py b/askbot/migrations/0004_install_full_text_indexes_for_mysql.py index 52321b90..e484c59b 100644 --- a/askbot/migrations/0004_install_full_text_indexes_for_mysql.py +++ b/askbot/migrations/0004_install_full_text_indexes_for_mysql.py @@ -47,7 +47,7 @@ class Migration(DataMigration): and will probably fail otherwise """ if db.backend_name == 'mysql': - if supports_full_text_search_migr0004(): + if supports_full_text_search(): #todo: extract column names by introspection question_index_sql = get_create_full_text_index_sql( Q_INDEX_NAME, diff --git a/askbot/models/question.py b/askbot/models/question.py index 6c45f1eb..44653dd0 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -6,6 +6,7 @@ from django.conf import settings as django_settings from django.db import models from django.contrib.auth.models import User from django.core import cache # import cache, not from cache import cache, to be able to monkey-patch cache.cache in test cases +from django.core import exceptions as django_exceptions from django.core.urlresolvers import reverse from django.utils.hashcompat import md5_constructor from django.utils.translation import ugettext as _ @@ -176,7 +177,7 @@ class ThreadManager(BaseQuerySetManager): """returns a query set of questions, matching the full text query """ - if django_settings.ENABLE_HAYSTACK_SEARCH: + if getattr(django_settings, 'ENABLE_HAYSTACK_SEARCH', False): from askbot.search.haystack import AskbotSearchQuerySet hs_qs = AskbotSearchQuerySet().filter(content=search_query) return hs_qs.get_django_queryset() @@ -612,6 +613,8 @@ class Thread(models.Model): group_ids = thread_groups.values_list('group_id', flat=True) + group_ids = list(group_ids)#force query for MySQL + from askbot.models import GroupMembership user_ids = GroupMembership.objects.filter( group__id__in=group_ids @@ -635,7 +638,7 @@ class Thread(models.Model): thread_groups = thread_groups[:max_count] group_ids = thread_groups.values_list('group_id', flat=True) - return Group.objects.filter(id__in=group_ids) + return Group.objects.filter(id__in=list(group_ids))#force list 4 mysql def update_favorite_count(self): self.favourite_count = FavoriteQuestion.objects.filter(thread=self).count() @@ -1158,8 +1161,8 @@ class Thread(models.Model): #modified tags go on to recounting their use #todo - this can actually be done asynchronously - not so important modified_tags, unused_tags = separate_unused_tags(removed_tags) - delete_tags(unused_tags)#tags with used_count == 0 are deleted + delete_tags(unused_tags)#tags with used_count == 0 are deleted modified_tags = removed_tags #add new tags to the relation @@ -1173,8 +1176,9 @@ class Thread(models.Model): added_tags = list(reused_tags) #tag moderation is in the call below created_tags = Tag.objects.create_in_bulk( - tag_names = new_tagnames, user = user - ) + tag_names=new_tagnames, + user=user + ) added_tags.extend(created_tags) #todo: not nice that assignment of added_tags is way above @@ -1216,15 +1220,15 @@ class Thread(models.Model): #################################################################### self.update_summary_html() # regenerate question/thread summary html #################################################################### - #if there are any modified tags, update their use counts + modified_tags = set(modified_tags) - set(unused_tags) if modified_tags: Tag.objects.update_use_counts(modified_tags) signals.tags_updated.send(None, - thread = self, - tags = modified_tags, - user = user, - timestamp = timestamp + thread=self, + tags=modified_tags, + user=user, + timestamp=timestamp ) return True @@ -1251,6 +1255,9 @@ class Thread(models.Model): if None in (retagged_by, retagged_at, tagnames): raise Exception('arguments retagged_at, retagged_by and tagnames are required') + if len(tagnames) > 125:#todo: remove magic number!!! + raise django_exceptions.ValidationError('tagnames value too long') + thread_question = self._question_post() self.tagnames = tagnames.strip() diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index 5477990a..8a6ffe0e 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -186,11 +186,11 @@ class DBApiTests(AskbotTestCase): self.assertTrue(saved_question.thread.answer_count == 1) def test_unused_tag_is_auto_deleted(self): - self.user.retag_question(self.question, tags = 'one-tag') + self.user.retag_question(self.question, tags='one-tag') tag = models.Tag.objects.get(name='one-tag') self.assertEquals(tag.used_count, 1) self.assertEquals(tag.deleted, False) - self.user.retag_question(self.question, tags = 'two-tag') + self.user.retag_question(self.question, tags='two-tag') count = models.Tag.objects.filter(name='one-tag').count() self.assertEquals(count, 0) -- cgit v1.2.3-1-g7c22