Badword Filter für Kommentare

Veröffentlich am 11.03.2009, 12:56

Mich hat in den letzten Tagen eine heftige Spamwelle erwischt. Da ich die im django.contrib verfügbare Comments App verwende, gibt es neben dem honeypot Feld keinen Spamschutz.

Da mir Rechenaufgaben, Captchas und handische Moderation zu aufwändig sind, werde ich es mit einem Badword Filter versuchen.

Der Filter arbeitet sehr einfach. Über das comment_will_ne_posted Signal wird der Kommentartext auf Badwords überprüft und im Falle eines Treffers auf versteckt gesetzt. Dadurch wird der Kommentar nicht gelöscht und ich kann False-Positives wieder zum Leben erwecken.

models.py

from django.db import models
from django.utils.translation import ugettext as _

import re

class CommentBadword(models.Model):
    word = models.CharField(_('Bad word'), max_length=255, blank=False)
    is_regex = models.BooleanField(_('Is regex'), default=False)

    def __unicode__(self):
        return self.word

    def validate(self, text):
        if self.is_regex:
            if re.compile(self.word).search(text):
                return False
        else:
            if self.word in text:
                return False
        return True

    class Meta:
        verbose_name = _('Comment Badword')
        verbose_name_plural = _('Comment Badwords')

forms.py

from django import forms
from django.utils.translation import ugettext as _
from .models import CommentBadword

import re

class CommentBadwordForm(forms.ModelForm):
    def clean(self):
        if self.cleaned_data['is_regex'] and not self._errors.get('word', None):
            try:
                test = re.compile(self.cleaned_data['word'])
            except:
                self._errors['word'] = forms.util.ErrorList([_('Invalid regex pattern')])
        return self.cleaned_data

    class Meta:
        model = CommentBadword

admin.py

from django.contrib import admin
from .models CommentBadword
from .forms import CommentBadwordForm

class CommentBadwordAdmin(admin.ModelAdmin):
    list_display = ('word', 'is_regex')
    list_filter = ('is_regex',)
    search_fields = ('word',)
    form = CommentBadwordForm

admin.site.register(CommentBadword, CommentBadwordAdmin)

init.py oder auch woanders, hauptsache der Connect wird ausgeführt

from django.contrib.comments.models import Comment
from django.contrib.comments.signals import comment_will_be_posted

from .models import CommentBadword

def comment_badword_filter(sender, **kwargs):
    instance = kwargs['comment']
    for badword in CommentBadword.objects.all():
        if not badword.validate(instance.comment):
            instance.is_public = False
    return True

comment_will_be_posted.connect(comment_badword_filter, sender=Comment, dispatch_uid='comment_badword_filter')

Ich denke, dass sich der Code zu großen Teilen selbst erklärt. Zwei Anmerkungen möchte ich jedoch loswerden.

Das CommenBadword Model kann sowohl mit einzelnen gesperrten Wörtern als auch mit regulären Ausdrücken umgehen. Eine entsprechende Validierung im Django Admin wird durchgeführt.

Theoretisch kann man diesen Badword Filter auch für andere Texte verwenden (Foren, etc.). Zur Validierung muss lediglich die Methode validate des CommentBadword Models mit dem zu prüfenden Text aufgerufen werden.Beispiel:

mybadword.validate(mytext)

.. gibt True zurück, wenn der Text keine Badwords enthält und somit freigegeben werden kann.

Viel Spass damit!

Tags: comments, django, moderation

Weiterlesen, 6 Kommentar(e)

 

comments: Mail bei neuem Kommentar

Veröffentlich am 18.02.2009, 21:53

Eines der Dinge, die ich in django.contrib.comments vermisse, ist eine E-Mail Benachrichtigung bei neuen Kommentaren. Nach etwas Suchen habe ich nur eine alte Variante aus dem Jahr 2006 gefunden.
Das nehme ich als Anlass, hier mal meine aktuelle - 1.0 kompatible - Umsetzung zu veröffentlichen.

Ich habe diesen Code einfach in die __init__.py meiner Blog App gespeichert (kann auch woanders gespeichert werden, hauptsache Django verarbeitet die Datei automatisch).

from django.contrib.comments.models import Comment
from django.contrib.sites.models import Site
from django.core.mail import mail_managers
from django.db.models import signals

def comment_notification(sender, instance, **kwargs):
    subject = 'New Comment on %s' % instance.content_object
    msg = 'http://%s%s\n\nComment:\n%s' % (
        Site.objects.get_current().domain,
        instance.content_object.get_absolute_url(),
        instance.comment
    )
    mail_managers(subject, msg, fail_silently=True)

signals.post_save.connect(comment_notification, sender=Comment)

Wichtig: MANAGERS in der settings.py korrekt konfigurieren, z.B.:

MANAGERS = (
    ('Webmaster', 'mail@example.com'),
)

Tags: comments, django, mail, signals

Weiterlesen, 14 Kommentar(e)

 

DB Update für Django, ein Konzept

Veröffentlich am 08.11.2008, 11:17

Ich habe mir heute morgen einige Schema Evolution Tools für Django angesehen. django-evolution fällt aufgrund schlechter Erfahrung aus, dmigrations kommt nicht in Frage da nur MySQL unterstützt wird.

Bis jetzt mache ich meine Datenbank-Änderungen manuell mit einem Ordner voller sql Dateien, für jede Änderung eine Datei. Das klappt auch super, einziges Manko: man muss wissen, bei welcher "Version" man mit dem ausführen der sql Dateien beginnt.

Diesen Vorgang möchte ich nun automatisieren. Grundsätzlich muss erwähnt werden, das ich solch ein Tool nur für das Aktualisieren von Webseiten, die sich bereits im Produktionsbetrieb befinden, verwenden würde. Zum Zeitpunkt der Entwicklung ist die Erstellung der Änderungsscripte zu aufwändig.

Die Anwendung und Vorgehensweise stelle ich mir wie folgt vor:

Es existiert ein Model welches für jedes Model die aktuelle Version speichert:

class ModelVersion(models.Model):
    model = models.ForeignKey(ContentType, unique=True)
    version = models.PositiveIntegerField()

.. für in jeder Application existiert ein Python Modul "dbupdates" welches ein Dictionary CHANGES enthält:

CHANGES = {
    1, (
        'ATLER TABLE myapp_mymodel ADD COLUMN ...',
    ),
    2, (
        'ALTER TABLE myapp_mymodel DROP COLUMN ...',
        'DROP TABLE ...',
    ),
}

.. um Änderungen auszuführen, wird folgender Befehl ausgeführt:

alle Anwendungen aktualisieren:
./manage dbupdate

oder nur eine Anwendung aktualisieren:
./manage dbupdate [application]

Alternativ kann über den Parameter --startat=version die Startversion übergeben werden (ich merkte bereits an, das die Version zum Zeitpunkt des Checkouts bekannt sein muss, dannach wird die aktuelle Version in der Datenbank gespeichert.

Möglich ist auch, die CHANGES optional Datenbank-abhängig zu gestalten um auf eigenarten gewisser RDBMS einzugehen.

Soweit meine Idee, Feedback ist ausdrücklich erwünscht. Entweder als Kommentar, per Mail oder im IRC (#django-de / Freenode).

Nachtrag: Ich muss feststellen, dass ich hier bis auf wenige Kleinigekeiten dmigrations "nachbaue". Jedoch sehe ich derzeit keine andere Möglichkeit, da dmigrations ausschließlich mit MySQL arbeitet. Abgesehen davon wäre dmigrations jedoch genau das richtige für meine Anwendungszwecke.

Tags: database, dbupdate, django, schema evolution

Weiterlesen, 0 Kommentar(e)

 

ProgressBar leakt user handles

Veröffentlich am 13.03.2008, 07:59

.. ich werde noch verrückt hier, seit gestern Nachmittag bin ich auf der Suche nach einem recht eigenartigen Problem und nun habe ich den Übeltäter endlich gefunden.

In einem meiner Kundenprojekte läuft unter anderem eine sehr komplexe GUI für die Erfassung und statistische Auswertung diverser Daten. Für die Auswertung verwende ich an verschiedenen Stellen ProgressBars aus dem Standardsortiment von Microsofts .NET Libraries. (warum ich gerade die ProgressBars ansprechen? dazu kommen wir gleich).

Das Programm hat immer mal wieder win32Exceptions ala "Cannot create window handle" geworfen. Sehr aufschlussreich - muss ich schon sagen.

Nach einiger Recherche ist mir dann aufgefallen, dass der NativeErrorCode "14" ist. Dieser bedeutet soviel wie "OutOfMemory". Da die Testmaschine 1GB Ram hat und außer Windows+Updates nichts installiert ist, konnte ich das beim besten Willen nicht glauben.

Ich habe mich dann auf die Suche nach dem Übeltäter gemacht und mit Hilfe durch den ProcessExplorer festgestellt, dass das Programm immer mehr USER Handles vom System frisst.. und irgendwann ist Schluss. GDI wirft dann die besagte "Cannot create window handle" Exception.

Durch Try'n'Error habe ich dann den Täterkreis immer weiter gegrenzt und am Ende blieben nur noch die ProgressBars übrig (hat eine Weile gedauert, in der GUI liegen ca. 1400 Controls rum). Scheinbar disposen die ProgressBars nicht ordentlich, anders kann ich mir die unmenschlich hohe Anzahl an Userhandles derzeit nicht erklären.

Das Ende vom Lied? Ich zeichne mir die ProgressBars nun selbst, ist eh "cooler" ;-)

Tags: .net, c#, gdi

Weiterlesen, 0 Kommentar(e)

 

Cisco Systems

Veröffentlich am 12.06.2006, 19:14

.. leider ohne Ton, wäre noch 'en Stück lustiger ;-)

cisco

Tags: cisco, fun, simpsons

Weiterlesen, 0 Kommentar(e)