Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Code style adjustments

  • Loading branch information...
commit 9c32c6357fba4277c1936165baee0e12122b9bbd 1 parent 783e198
@MarcinOS authored
View
62 _posts/2010-08-24-generic-relationships-in-django.markdown
@@ -18,49 +18,33 @@ tags:
Last week I've completed an interesting task within our project: building internal link-shortening system based on persisted objects, not on constant URLs. The main requirement was to get as loosely-coupled design as possible. In the perfect world the "shortenable" classes should know absolutely nothing about link-shortening mechanism. How to get such a flexibility in Django? I've used the [ContentTypes framework](http://docs.djangoproject.com/en/1.2/ref/contrib/contenttypes/) with [generic relations](http://docs.djangoproject.com/en/1.2/ref/contrib/contenttypes/#generic-relations) and it works pretty well!
-
-
-
First of all, we need to prepare the model class which will keep references to every "shortanable" object and its shortening key which will be used in URLs. The key-related part is quite easy, but what about the former requirement? How it is possible to keep a relation to an object of type we don't even know? The answer is: [ContentTypes framework](http://docs.djangoproject.com/en/1.2/ref/contrib/contenttypes/). It gives us an ability to get an identifier of every class within Django-based application. So, we've got all we need now. Just take a look into a small code snippet to see this feature in action:
-
-
-
-
+{% highlight python %}
class ShorteningKey(models.Model):
""" Contains a key for a generic object used in short link resolving """
key = models.SlugField(unique=True)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
-
-
-
+{% endhighlight %}
Quite neat, isn't it? Now it is possible to get a shortening key for a given object. We'll do that with help of the following method.
-
-
-
-
-
- @classmethod
- def get_shortening_key_for_instance(cls, instance):
- """ Return ShorteningKey object associated with given model object.
- May throw DoesNotExist exception """
- type = ContentType.objects.get_for_model(instance)
- return ShorteningKey.objects.get(content_type__pk=type.id, object_id=instance.id)
-
-
+{% highlight python %}
+ @classmethod
+ def get_shortening_key_for_instance(cls, instance):
+ """ Return ShorteningKey object associated with given model object.
+ May throw DoesNotExist exception """
+ type = ContentType.objects.get_for_model(instance)
+ return ShorteningKey.objects.get(content_type__pk=type.id, object_id=instance.id)
+{% endhighlight %}
The last thing to do is to generate a unique key for every object. As I've mentioned, it was extremely important to keep "shortenable" classes as clean as possible, so hard-coding shortening key generating method within the scope of each relevant class wasn't a good idea. Instead of this, I've used [Signals](http://docs.djangoproject.com/en/1.2/topics/signals/). Now, we have to write key-generation method and associate it with `post_save` signal. Why do we use the `post_save`? Because we need persisted instance to use it within ContentTypes framework. The described function is listed below while process of attaching it to the signal will be shown later.
-
-
-
-
+{% highlight python %}
def generate_shortening_key(sender, **kwargs):
""" Generates shortening key for given object """
instance = kwargs['instance']
@@ -77,22 +61,15 @@ The last thing to do is to generate a unique key for every object. As I've menti
attempt += 1
else:
logging.warning("Cannot create link shortening for " + str(instance))
-
-
-
+{% endhighlight %}
We'll also need a function which will delete unnecessary key after object deletion. It's quite simple and similar so I won't put its code here.
-
-
-
The whole thing is almost ready, we just need to provide a function which is responsible for retrieving shortened URL.
-
-
-
+{% highlight python %}
def get_shortened_url(instance):
""" Returns shortened url to given instance. If there is no shortened url,
it tries to return full url to object. If there is no one, it returns empty string.
@@ -105,25 +82,18 @@ The whole thing is almost ready, we just need to provide a function which is res
return instance.get_absolute_url()
except (TypeError, AttributeError):
return ''
-
-
-
+{% endhighlight %}
It will be perfect if the function would be a member of each "shortenable" class, won't it? We will attach it dynamically to each of them in the same loop which is responsible for signals attaching.
-
-
-
-
+{% highlight python %}
LINK_SHORTENING_ENABLED_CLASSES = (Class1,Class2,...)
for cls in LINK_SHORTENING_ENABLED_CLASSES:
post_save.connect(generate_shortening_key, sender=cls)
post_delete.connect(delete_shortening_key, sender=cls)
cls.get_shortened_url = get_shortened_url
-
-
-
+{% endhighlight %}
That's all! We've got fully featured link-shortening system with loosely-coupled classes. You can easily adjust this example and make it appropriate for your particular needs. For example, you can build a [comment system](http://docs.djangoproject.com/en/1.2/ref/contrib/comments/), just like Django team did :)
View
4 css/syntax.css
@@ -1,5 +1,5 @@
-.highlight { background: #000000; color: #FFFFFF;}
-.highlight code { background: #000000; color: #FFFFFF;}
+.highlight { background: #000000; color: #FFFFFF; padding: 15px 0; margin: 20px 0; }
+.highlight code { background: #000000; color: #FFFFFF; font: 1.2em "Andale Mono", monospace; }
.highlight .c { color: #9933CC; font-style: italic } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #FF6600; font-weight: bold } /* Keyword */
Please sign in to comment.
Something went wrong with that request. Please try again.