**Before using this snippet**, please note that it's largely been superseded by [comment_utils](http://code.google.com/p/django-comment-utils/), which includes a more featureful and extensible version of this system, particularly with respect to additional moderation options and useful things like email notifications of comments.
Once upon a time I hacked the copy of `django.contrib.comments` I'm using on my blog, so that I could have comments get set to `is_public=False` if posted more than 30 days after the entry's publication, and to add Akismet spam filtering. I've regretted it ever since, because it's made upgrading my copy of Django a pain.
So here's an improved version which doesn't require hacking directly on Django. To use it, you'll need to do a few things:
1. Grab the [Python Akismet module](http://www.voidspace.org.uk/python/modules.shtml#akismet) and install it somewhere on your server.
2. In your settings file, add `AKISMET_API_KEY`, and make sure its value is a valid Akismet key. If you don't have an Akismet key, you can [get one at wordpress.com](http://wordpress.com/api-keys/).
3. Put this code -- both the function and the dispatcher calls -- somewhere in your project that's _guaranteed_ to be imported early (until this code is executed, the moderation function won't be set up to listen for comments posting).
To have comments on a certain type of object (say, weblog entries) automatically go into moderation when the object reaches a certain age, define a method on that object's model called `comments_open`, and have it return `False` when comments should be auto-moderated.
You can read the short blog article that gives a [description of what I was thinking](http://unpythonic.blogspot.com/2007/03/using-pexpect-to-control-django.html)
when I attempted this. Note: UNIX only. Sorry!
This will save your del.icio.us bookmarks in your own database with a Bookmarks model. It depends on several things:
1. `DELICIOUS_USER` and `DELICIOUS_PASS` defined in settings.py
2. [pydelicious](http://code.google.com/p/pydelicious/) installed
3. Any view that uses del.icio.us should call the delicious() function, to update the database.
4. The [cache_function](http://www.djangosnippets.org/snippets/109/) decorator must be available. (I have it in a decorators.py file in my app; if you move it, you need to update the import)
5. The [django-tagging](http://code.google.com/p/django-tagging/) app, although it could be modified to not need this rather easily.
Other than all those dependencies, it's actually rather simple. It will call del.icio.us for the 5 most recent posts, and create them if they're not already in the database. Otherwise, it'll check for updates, and change as appropriate. It won't return anything, as it updates the models themselves.
A decorator similar to `cache_page`, which will cache any function for any amount of time using the Django cache API.
I use this to cache API calls to remote services like Flickr in my view, to prevent having to hit their servers on every request.
I posted a sample function which uses the [delicious API](http://www.djangosnippets.org/snippets/110/) in the function, also.
**Update**: It now also will put in a temporary 'in-process' variable (an instance of `MethodNotFinishedError`) in the cache while the function is processing. This will prevent the cache from calling the method again if it's still processing. This does not affect anything **unless you're using threads**.
This is a pretty straightforward bit of code for getting the most-commented objects of a particular model; just drop it into a custom manager for that model, and you should be good to go. Check the docstring for how to make it look at `Comment` instead of `FreeComment`.
A short little bit of code to test for comment spam against Akimet.
Use:
a = Akismet('<AkismetKey>', 'http://sneeu.com/blog/')
a.verify_key()
print a.comment_check(
comment_author='...',
comment_author_email='[email protected]',
user_ip='10.0.0.1',
comment_content="""Comment content!"""
)
I'm posting this here mostly because I need a more permanent home for this than my lappy's hard drive. I hope it's interesting to other people, though.
Anyway - this script is what I use to test Django against multiple versions of Python and multiple databases. To actually run this you'll of course need Python 2.3, 2.4, and 2.5 installed along with MySQL, PostgreSQL, and sqlite3 -- and the associated database wrappers for all 3 Pythons.
Yes, for the record, I've got all those things installed on my laptop.
If you can somehow make that work, though, running this script will print out a nice little summary of what's failing against which versions of Python and which database. Run with `-v` to see the actual failures.
This script automates the process of installing Lighttpd, Flup, Django and a Django app (with init script) on a TextDrive shared server.
Usage instructions: http://textusers.com/wiki/Installing_Django
# Changelog
* April 5, 2007
* Added (www.\)? conditional in Lighty conf
* Updated to Django 0.96
I'm a big fan of Markdown, and often set up models to automatically apply it to certain fields before saving. But that's not really flexible, because if I then distribute the code someone else might want to use reStructuredText or Textile or whatever, and then they have to hack my code.
So here's a function which looks for a setting called `MARKUP_FILTER` and, based on what it finds there (see the docstring for what it looks at), chooses a text-to-HTML conversion function and applies it to a piece of text. Since Textile, Markdown and reStructuredText all support various useful options, it has the ability to pick up arbitrary keyword arguments and pass them through.
This is a minor modification to [Upload a file using newforms](http://www.djangosnippets.org/snippets/95/) as posted by [mboersma](http://www.djangosnippets.org/snippets/95/).
I altered the ZipUploadForm by removing lines 33 - 34:
if 'zip_file' in self.clean_data:
zip_file = self.clean_data['zip_file']
and adding a return statement to clean_zipfile, which returns the validated zip file. I also added the line:
zip_file.clean = clean_zipfile
so that when the full_clean() in called on the form, clean_zipfile will automatically run.
All other code (the view & template) remain the same.
** Disclaimer **
I'm not *that* familiar with newforms, so please forgive me if some the explanation as to why this works is incorrect ;) Who knows, maybe the first guy had it right.
**The code is a bit messy and may include bugs ;-) Anyway, I want to clean it up and add features (see below).**
The `process_latex()` function will read the given template, render it with a custom context and convert it into the desired output format by running it through pdflatex.
Depending on the `outfile` parameter, the output will either be returned (for a response object) or written to the path specified in `outfile` (useful for `save()` in models which generate static, unique files).
**TODO**
* Error handling in case pdflatex breaks (non-zero return value)
* Optionally returning a HttpResponse with the correct mimetype and Content-Disposition set to attachement and an appropriate filename
* RequestContext instead of Context (passing the Context object instead of a dict)
**CREDITS**
Credits also go to ronny for the names dict :-P
create_object and update_project modified to handle newforms (including FileFields). In addition, I have added some extras:
1. extra_fields - this is a dict or callable that contains additional fields to be passed to the form, for example stuff that is in the session.
2. on_success - callback called if form is valid and object created/updated, if this is not set the default behaviour is to send a redirect
3. on_failure - callback called if form is invalid, the default is to redisplay the form.
Note that once newforms are finally done these functions are likely to be redundant, as generic views will be updated to use the newforms API, so use with caution.
*I suppose I'm kind of stubborn, but I prefer to use underscores to replace spaces and other characters. Of course, that shouldn't hold you back from using the build-in slugify filter :)*
** Forcing the slug to use ASCII equivalents: **
Transforming titles like "Äës" to slugs like "aes" was kind of a trial and error job. It now works for me. I hope `_string_to_slug(s):` proves a rather stable solution. Yet the worst-case scenario is that such characters are lost, I guess that is acceptable.
Other ways of dealing with this problem can be found at [Latin1 to ASCII at Activestate](http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/251871) or in the comments below.
**How to use:**
The slug fields in your model must have prepopulate_from set, the fields specified in it are used to build the slug.
To prevent duplicates, a number is added to the slug if the slug already exists for the current field in another, previous, object. I guess there should be a cleaner way to distinguish between creating a new db entry or updating an existing one, sadly, the db back-end is kind of a black-box to me. At least this works ;)
I choose not to alter the slug on an update to keep urls more bookmarkable. You could even extend this further by only updating the slug field if it hasn't been assigned a value.