Lettuce Tags

Lettuce is a BDD (Behavior Driven Development) testing tool for Python based on the excellent Cucumber project. It has most of the same features that Cucumber has, and has proven invaluable in my projects. I discovered an undocumented feature the other day called “Tags”. Cucumber has them, so I also assumed that Lettuce had them. Tags allow you to selectively skip or run scenarios. For instance:

Feature: Some Feature
 
	Scenario: This is scenario 1
		Given I do stuff
		And I see stuff
		Then I am stuff
 
	@mytag
	Scenario: This is scenario 2
		Given I do more stuff
		And I see more stuff
		Then I am more stuff

You can use tags in many ways.

lettuce --tag=mytag # Run only scenarios with this tag
lettuce --tag=-mytag # Don't run scenarios with this tag
./manage.py harvest --tag=mytag # Django/Lettuce way of using tags.

Requirements

There is a grand myth about requirements–if you write them down, users will get exactly what they want. That’s not true. At best, users will get exactly what was written down, which may or may not be anything like what they really want.
– Mike Cohn, “Succeeding with Agile”

Saving within a post_save signal in Django

One of the more useful features of the Django framework is it’s extensive signaling capabilities. The ORM throws off a handful of signals every time a model is initialized, modified, saved, or deleted. They include:

  • pre_init
  • post_init
  • pre_save
  • post_save
  • pre_delete
  • post_delete
  • m2m_changed
  • class_prepared

I tend to use the post_save signal fairly often as a good way to get around overriding the default save method on models. Recently though I ran into an issue where I was hitting the “maximum recursion depth exceeded” error when I was saving the current model from within the post_save signal. If you think about it, that makes a lot of sense. You save once, then save again in the signal and then it triggers the signal again. BOOM, infinite loop.

To get around the saving within a post_save signal problem, you just need to disconnect the post_save signal before you call save. After save, you can re-connect it.

from django.db.models import signals
signals.post_save.disconnect(some_method, sender=SomeModel)
some_instance.save()
signals.post_save.connect(some_method, sender=SomeModel)

Programmatically Adding Users to Groups in Django

I recently needed to add a large number of users to a permission group in Django. I had a hard time finding a way to do this in the documentation, so I thought I’d share my solution.

from django.contrib.auth.models import Group, User
 
g = Group.objects.get(name='My Group Name')
users = User.objects.all()
for u in users:
    g.user_set.add(u)

Easy as pie. I originally attempted to do this from the perspective of a user, but as it turns out, doing it from the group perspective is much easier.

Copying Django Model Objects

Django’s ORM is top notch. It provides facilities to do almost anything you can think of with a database, and if it doesn’t, it still lets you execute arbitrary SQL to your hearts content. I’ve been developing Django for close to 2 years now, and still discover facets of it that I never knew existed. For instance, I had a need to duplicate a row in a table, but give it a different primary key. After a quick Google search, I discovered that Django allows you to do the following to copy instantiated model objects.

my_model = MyModel.objects.get(pk=4)
my_model.id = None
my_model.save()

There are a few caveats with doing things this way.

  • Unique Constraints – If you have any unique constraints on the model, the save will not pass validation and fail.
  • ManyToMany Fields – If you need new copies of ManyToMany field values, you’ll need to handle this yourself.

That being said, in many cases duplicating a model instance is as easy as changing it’s ID and saving.

Update All PIP Packages

Revisiting an old Python + Django project made me realize that I needed to upgrade it’s PIP packages. Unfortunately, PIP doesn’t provide a way out of the box to update all of your installed packages at once. To update all of the PIP packages at once, use the following script.

import pip
from subprocess import call
 
for dist in pip.get_installed_distributions():
    call("pip install --upgrade " + dist.project_name, shell=True)

For more detail, check out this question on Stack Overflow.

28 Questions

Over the past few years I’ve tried my hand at launching a couple of different start ups. Over this time period, I’ve collected a set of questions from many different sources (HN, YCombinator, TechStars, etc) that I ask myself before proceeding. They help be decided if this is a real or manufactured problem, if it can be profitable, and if people will buy what I’m selling. My latest project idea is the only idea I’ve had that passes all of these questions to my satisfaction. How do yours hold up?

  1. What are you going to build?
  2. What is the actual problem?
  3. How will you sell your product/service?
  4. What are some potential obstacles?
  5. What are some existing options that solve this problem? How are you different?
  6. Who needs what you’re making?
  7. How do you know that they need it?
  8. How is the problem being solved now? Is it being solved now?
  9. Why isn’t this being done your way already?
  10. How will customers find out about you?
  11. What resistance will they have to trying your product?
  12. What are some key things about your project that outsiders don’t understand?
  13. Who will your first paying customer be?
  14. How might you expand if your initial idea succeeds?
  15. Why did you choose to work on this idea?
  16. Six months from now, what will be you biggest problem?
  17. What are the hard parts of this idea?
  18. Who would you hire/add to your team?
  19. What is the next step in product evolution?
  20. How does your product work?
  21. How big is the opportunity? [market]
  22. How do you know customers need what you’re making?
  23. What domain expertise do you have? Why should you be the one to do this?
  24. What part of your project will you build first? (could be business connections, hardware, software, etc)
  25. How much money could you make per year?
  26. How will you make money?
  27. What have you built in the past?
  28. How would you spend $5,000, how would you use it?

Installing Local Flavor with Django 1.5

I just started a new project using the latest release candidate of Django 1.5. One thing that I needed was the local flavor add-on so I could get a list of US states. This functionality used to be rolled into the main codebase, but it was rightfully removed in Django 1.5. To install the US local flavor package, just use PIP.

pip install https://github.com/django/django-localflavor-us/zipball/master

Now you should be able to import anything you need from the package in the usual manner, except from the new package instead of the old one.

from django_localflavor_us.us_states import STATE_CHOICES

For more information, check out the How To Migrate section of the local flavor docs on the Django documentation site.