There’s no way in Django (that I’ve found) to render a field’s name in a
template. This means you end up with <th>Field Name</th>
all over your
templates. Why hello there, DRY violation!
The fields are stored in model._meta.fields
, but templates don’t allow you to
access variables which start with an underscore. I’ve got two little utility
functions I wrote for myself to generate a dict
of labels I can use in my
templates:
def get_labels_for(model, cap=True, esc=True):
from django.template.defaultfilters import capfirst
from django.utils.html import escape
labels = {}
for field in model._meta.fields:
label = field.verbose_name
if cap:
label = capfirst(label)
if esc:
label = escape(label)
labels[field.name] = label
return labels
def with_labels(context, cap=True, esc=True):
from django.db.models import Model
result = context.copy()
for k, v in context.iteritems():
if isinstance(v, Model):
result[k + '_labels'] = get_labels_for(v, cap, esc)
elif hasattr(v, '__getitem__') and len(v) > 0:
if isinstance(v[0], Model):
result[k + '_labels'] = get_labels_for(v[0], cap, esc)
return result
The parameters:
model
can be a model class or a model instance.
- If
cap
is True
, the first letter of each label will be capitalized.
- If
esc
is True
, the labels will be escaped for HTML.
So, in your view:
def some_view(request, foo_id):
foo = get_object_or_404(Foo, id=foo_id)
context = {'foo': foo, 'foo_labels': get_labels_for(foo)}
return render_to_response('foo.html', context)
And in your template:
{{ foo_labels.bar }}: {{ foo.bar }}
with_labels
works the same way, except you can just surround your context
with it:
def some_view(request, foo_id):
foo = get_object_or_404(Foo, id=foo_id)
bars = Bars.objects.all()
context = {'foo': foo, 'bars': bars}
return render_to_response('foo.html', with_labels(context))
It will detect the models and lists of models in the context and add
foo_labels
and bars_labels
to the context.
In Python:
import os.path, random, string
def random_filename(chars=string.hexchars, length=16, prefix='',
suffix='', verify=True, attempts=10):
for attempt in range(attempts):
filename = ''.join([random.choice(chars) for i in range(length)])
filename = prefix + filename + suffix
if not verify or not os.path.exists(filename):
return filename
>>> random_filename()
'DC53e02B08eF47e9'
>>> random_filename(chars='hi', length=32)
'hhhihihhhiiihhhihhiiiiiihhhhiihh'
>>> random_filename(prefix='username', suffix='.txt')
'username7dbd29aBdD25BeB9.txt'
# returns None if it can't find a filename
>>> open('xxx', 'w').close()
>>> str(random_filename(chars='x', length=3))
'None'
In Ruby:
def random_filename(opts={})
opts = {:chars => ('0'..'9').to_a + ('A'..'F').to_a + ('a'..'f').to_a,
:length => 16, :prefix => '', :suffix => '',
:verify => true, :attempts => 10}.merge(opts)
opts[:attempts].times do
filename = ''
opts[:length].times do
filename << opts[:chars][rand(opts[:chars].size)]
end
filename = opts[:prefix] + filename + opts[:suffix]
return filename unless opts[:verify] && File.exists?(filename)
end
nil
end
When most pages in a site require authentication, decorating all the views with
@login_required
can be annoying. You can reverse the default behavior by
creating a custom middleware class:
import urllib
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect
def allow_anonymous(view_func):
view_func.allow_anonymous = True
return view_func
class RequireLogin:
def process_view(self, request, view_func, view_args, view_kwargs):
if request.path != settings.LOGIN_URL and
not request.user.is_authenticated() and
not getattr(view_func, 'allow_anonymous', False):
url = '%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME,
urllib.quote(request.get_full_path()))
return HttpResponseRedirect(url)
That’s an ugly block of code but it’s not too complex. allow_anonymous
is a
function decorator, like login_required
. It just tags the function to tell the
middleware that authentication isn’t required. The RequireLogin
class verifies
that the user is logged in. If not, and if the function is not decorated with
allow_anonymous
, it redirects to settings.LOGIN_URL
.
So put that code in a file in your project – let’s say,
yourproject/yourapp/middleware.py
. Then open settings.py
and add
"yourproject.yourapp.middleware.RequireLogin"
to
MIDDLEWARE_CLASSES
. This tells Django about your new middleware class, and
RequireLogin.process_view
will be called any time a view is about to be
rendered.
Now you can use it in your view:
from yourproject.yourapp.middleware import allow_anonymous
def some_private_view(request):
# won't be accessible unless user is logged in
return HttpResponse('Hello, user!')
@allow_anonymous
def some_public_view(request):
return HttpResponse('Hello, world!')
If all is working correctly, some_private_view
should ask for a login, but
some_public_view
will allow viewing without it.
Update: I’ve updated the code to fix a bug when using
django.contrib.auth.views.login
for your login view. As you can’t mark this
view function with @allow_anonymous
, it would infinitely redirect back to it.
Oops! Thanks for pointing it out, Phil.
Simon Willison linked to an article which
argues:
Warnings cause us to lose our work, to mistrust our computers, and to blame
ourselves. A simple but foolproof design methodology solves the problem:
Never use a warning when you mean undo. And when a user is deleting their
work, you always mean undo.
The post spawned a discussion on undo techniques for Django. I
decided to implement one method and post the results here. It only offers undo
for deleting, and not for editing. Other than that, I like it.
How it works
It’s a pretty simple concept: add a trashed_at
field to your model, with the
default value of None
. When delete()
is called on an object, if trashed_at
is None
, set it to the current time but don’t delete it. If it’s not None
,
actually delete it from the database.
Model
Here’s what I did:
from datetime import datetime
from django.db import models
class SomeModel(models.Model):
# ... other fields ...
trashed_at = models.DateTimeField(blank=True, null=True)
objects = NonTrashManager()
trash = TrashManager()
def __str__(self):
trashed = (self.trashed_at and 'trashed' or 'not trashed')
return '%d (%s)' % (self.id, trashed)
def delete(self, trash=True):
if not self.trashed_at and trash:
self.trashed_at = datetime.now()
self.save()
else:
super(SomeModel, self).delete()
def restore(self, commit=True):
self.trashed_at = None
if commit:
self.save()
The custom managers are used to make it so SomeModel.objects
and
SomeModel.trash
only query against the appropriate rows:
class NonTrashManager(models.Manager):
''' Query only objects which have not been trashed. '''
def get_query_set(self):
query_set = super(NonTrashManager, self).get_query_set()
return query_set.filter(trashed_at__isnull=True)
class TrashManager(models.Manager):
''' Query only objects which have been trashed. '''
def get_query_set(self):
query_set = super(TrashManager, self).get_query_set()
return query_set.filter(trashed_at__isnull=False)
Usage
Here are some examples:
# use the managers to see what's what
>>> SomeModel.objects.count()
5L
>>> SomeModel.trash.count()
0L
# grab a non-trashed object
>>> object = SomeModel.objects.get(id=1)
>>> object
<Item: 1 (not trashed)>
# now delete it (move it to the trash)
>>> object.delete()
>>> object
<Item: 1 (trashed)>
>>> SomeModel.objects.count()
4L
>>> SomeModel.trash.count()
1L
# undo the delete
>>> object.restore()
>>> object
<Item: 1 (not trashed)>
# trash it again
>>> object.delete()
# calling delete again will *really* delete it
>>> object.delete()
# you could also force it to skip the trash
>>> object = SomeModel.objects.get(id=2)
>>> object.delete(trash=False)
# you could use a date range filter to delete
# everything trashed over a month ago
>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> month_ago = datetime.now() - relativedelta(months=1)
>>> objects = SomeModel.trashed.filter(trashed_at__lte=month_ago)
>>> for object in objects:
... object.delete()
>>>
Code
Here’s a zip of the source code for my test. It
ain’t perfect. To keep it simple, I didn’t use AJAX for the deleting and
undoing, but it wouldn’t be hard to add it yourself.
Sometimes users want to bring text from an editor like Word into your web forms.
You will often find nasty little characters hiding in the text, like ‘\u2022’
(a.k.a. the notorious bullet). These characters will normally throw errors
if you try to convert them to ASCII:
>>> u'\u2022'.encode('ascii')
Traceback (most recent call last):
File "<console>", line 1, in ?
UnicodeEncodeError: 'ascii' codec can't encode ... (yadda yadda)
To sanitize these strings and make them XML/HTML safe:
>>> u'\u2022'.encode('ascii', 'xmlcharrefreplace')
'•'
It translates the invalid characters into their XML equivalents. Woo! You can
also use 'ignore'
or 'replace'
(replaces with ?
):
>>> u'\u2022'.encode('ascii', 'ignore')
''
>>> u'\u2022'.encode('ascii', 'replace')
'?'
If you’re getting nasty Unicode errors from your templates in Django now that
they’ve merged the Unicode branch, this might help as a quick
fix.
To quote the web site:
Startup Weekend is an idea, an experiment, a
chance to get together and create something beautiful over one jam packed
weekend.
Ideas were submitted for possible web apps. At the start of the weekend, the
attendees filtered those ideas down to one: VoSnap, a
mobile app for group polling and decision making.
And then they all started buiding it. Not just the web app, but a whole company.
They had the lawyers there and everything. Each attendee gets an equal share in
the company.
Pretty inspiring. It must be an amazing experience. I know I would love to have
that on my resume.
“I can work with 70 strangers and take a brand new product to completion in an
insanely short period of time.”
I’ve been sitting here all day with the live stream open in the background
listening in and watching every so often. I’ve heard some funny quotes… “Wait,
wait. Are we really incorporated?” “Yeah.” “… when did we do
that?”
They’re supposed to launch at midnight. Here’s to hoping group speed developing
works out.
Django’s Foo.objects.get(keywords__contains='bar')
will make your database cry
when you run it against a 30 million row table. At times it becomes necessary to
segment a large table into many smaller tables.
Say you have these models:
class City(models.Model):
city = models.CharField(maxlength=255)
state = models.USStateField()
class Listings(models.Model):
name = models.CharField(maxlength=100)
street = models.CharField(maxlength=100)
city = models.ForeignKey(City)
zip = models.CharField(maxlength=10)
keywords = models.CharField(maxlength=255)
The listings table has gotten huge. Users only search within a single city, so you want to break the listings table into a table for each city. Instead of yourapp_listing
, you now have yourapp_listing_1
, yourapp_listing_2
, etc. All the listings are in the matching table for their city.
I couldn’t find a documented way to make db_table
dynamic in Django. So, how do you get Django to use the right table when you’re querying for listings? Here’s how I did it:
class ListingManager(models.Manager):
def get_table_for(self, city):
'''
someapp_listing if city == None
someapp_listing_012 if city.id == 12
'''
table = '_'.join((self.model._meta.app_label,
self.model._meta.module_name))
if city:
table += '_%03d' % city.id
return table
def in_city(self, city):
self.city = city
self.model._meta.db_table = self.get_table_for(city)
return self
class Listing(models.Model):
city = models.ForeignKey(City)
objects = ListingManager()
def delete(self):
Listing.objects.in_city(self.city)
super(Listing, self).delete()
def save(self):
Listing.objects.in_city(self.city)
super(Listing, self).save()
It’s simple to use:
>>> city = City.objects.get(id=2)
>>> Listings.objects.in_city(city).all()
Edit 2007/08: Simplified the code. When I first wrote this I replaced db_table
with a subclass of str
that called ListingManager.get_table_for
. It was overkill, and didn’t always work as expected (e.g., 'foo' + Listing._meta.db_table
didn’t work).
This post on 2D Boy states:
“As love and effort increase, the probability of self destruction approaches
1.” – Kyle’s Theorem of Destruction #2a
Or, the more you care about what you’re working on, the less likely you’ll
actually turn it into something totally awesome.
The more you work on something, the more personally invested you become. The
more personally invested you are, the less likely you are to look at it
objectively and trash or rethink problem areas. The product suffers. This
applies to any type of development, but it’s especially true for game
development.
How to fix it? 2D Boy suggests occasional distraction with side projects. I have
some more thoughts for large teams…
First: organize your staff into small, independent, scrum-ish groups.
Each group must be self-sufficient, with an artist, programmer, etc. When
assigned a task, it must be able to create a playable product without outside
interaction with other departments.
Next: identify the areas of development for the project. For example, in
Battlefield 2142, these might be: infantry, vehicles, conquest
gameplay, titan gameplay, statistics and awards, and user interface.
Finally: each group works on one specific area for one iteration of your scrum
development cycle. In the next iteration, rotate the group to a new area of
development.
This results in:
- Fresh eyes looking over the important parts of your game each iteration of the
development cycle.
- Less competition between departments.
- The game won’t suffer because one group doesn’t want to make changes to the
precious content they’ve been slaving over for the past 9 months.
Did a bit of running around today to get Django sending email via
Gmail. It’s simple once you figure it out.
If you’re running 0.96, upgrade to the latest development version
or apply the patch from ticket #2897. 0.96 does not support
TLS, which Gmail requires. Then add the appropriate values to settings.py
:
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'youremail@gmail.com'
EMAIL_HOST_PASSWORD = 'yourpassword'
EMAIL_PORT = 587
You can use the shell to test it:
>>> from django.core.mail import send_mail
>>> send_mail('Test', 'This is a test', to=['youremail@somewhere.com'])
Edit: Bryan commented that send_mail
is deprecated. Use
EmailMessage
instead:
>>> from django.core.mail import EmailMessage
>>> email = EmailMessage('Hello', 'World', to=['youremail@somewhere.com'])
>>> email.send()
To install Python, pygame, and PyOpenGL on Windows:
-
Download Python here and install it.
-
Download pygame here and
install it. The “Windows” section has an executable installer (e.g.
pygame-1.7.1release.win32-py2.5.exe).
-
You should also download and install Numeric from the pygame page.
-
Download ez_setup.py and
save it to a folder outside your Python installation (e.g. C:\
).
-
Open a command prompt, change to the folder you saved ez_setup.py to, and run
it:
c:\python\python c:\ez_setup.py`
It will download the easy_install executables and put them in your
Python\Scripts folder.
-
Now install PyOpenGL using easy_install:
c:\python\scripts\easy_install PyOpenGL
All done!