| 1 | It's common for me to want a unique slug based on a user supplied name of an object. Following the pattern outlined at [http://www.b-list.org/weblog/2006/11/02/django-tips-auto-populated-fields djang-tips-auto-populated-fields], it's easy to get the field populated without user intervention. However, by setting the slug field in the save() method the field isn't validated and thus unique=True restrictions aren't checked. I wrote the following code to attempt to pick slugs which are unique within a particular model. It's certainly not ideal (it suffers from race condition as well as potential for pounding a database with lots of small queries until it finds a good slug field), but it works for my small/mid-sized apps. |
| 2 | |
| 3 | Feel free to suggest improvements to 'wam-djangowiki spam@wamber.net' (remove the ' spam' from the address there). |
| 4 | |
| 5 | {{{ |
| 6 | def SlugifyUniquely(value, model, slugfield="slug"): |
| 7 | """Returns a slug on a name which is unique within a model's table |
| 8 | |
| 9 | This code suffers a race condition between when a unique |
| 10 | slug is determined and when the object with that slug is saved. |
| 11 | It's also not exactly database friendly if there is a high |
| 12 | likelyhood of common slugs being attempted. |
| 13 | |
| 14 | A good usage pattern for this code would be to add a custom save() |
| 15 | method to a model with a slug field along the lines of: |
| 16 | |
| 17 | from django.template.defaultfilters import slugify |
| 18 | |
| 19 | def save(self): |
| 20 | if not self.id: |
| 21 | # replace self.name with your prepopulate_from field |
| 22 | self.slug = SlugifyUniquely(self.name, self.__class__) |
| 23 | super(self.__class__, self).save() |
| 24 | |
| 25 | Original pattern discussed at |
| 26 | http://www.b-list.org/weblog/2006/11/02/django-tips-auto-populated-fields |
| 27 | """ |
| 28 | suffix = 0 |
| 29 | potential = base = slugify(value) |
| 30 | while True: |
| 31 | if suffix: |
| 32 | potential = "-".join([base, str(suffix)]) |
| 33 | if not model.objects.filter(**{slugfield: potential}).count(): |
| 34 | return potential |
| 35 | # we hit a conflicting slug, so bump the suffix & try again |
| 36 | suffix += 1 |
| 37 | }}} |