<center> Five Django Packages We Love at Monadical === *Written by Karim. Originally published 2021-03-02 on the [Monadical blog](https://monadical.com/blog.html).* </center> One of the biggest draws to Django is its ecosystem. If you encounter a problem, the chances are that someone else in the community has too, and has been kind enough to abstract it into a package and release it. Here are five packages we find ourselves coming back to. ## 1 [django-polymorphic](https://github.com/django-polymorphic/django-polymorphic) In object-oriented programming, we use the term ‘polymorphism’ to refer to objects that share an interface but not an implementation. A common pedagogical example is modeling animals in object oriented code: a dog and a cat can both make a sound--which can be invoked by the same function--but the sound is different. A more practical example is items in an e-commerce store: items in the store share some characteristics, but differ in many others. There is an [excellent article on Real Python](https://realpython.com/modeling-polymorphism-django-python) that goes through some approaches to modeling polymorphism. Where does django-polymorphic fit in? It makes modeling concrete, multi-table inheritance easy. If I define the following models to abstract a `Project`: ```python from polymorphic.models import PolymorphicModel class Project(PolymorphicModel): topic = models.CharField(max_length=30) class ArtProject(Project): artist = models.CharField(max_length=30) class ResearchProject(Project): supervisor = models.CharField(max_length=30) ``` Then we create some projects: ```python >>> Project.objects.create(topic="Department Party") >>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner") >>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter") ``` If we query `Projects`, we get polymorphic results: ```python >>> Project.objects.all() [ <Project: id 1, topic "Department Party">, <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">, <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ] ``` ## 2 [django-allauth](https://www.intenct.nl/projects/django-allauth/) django-allauth provides a set of modular, well-integrated apps that allow you to have any of the common signup and login flows, whether local or social. It shines when it comes to supporting both local and social login flows simultaneously, and it just works™. ## 3 [Django Lifecycle Hooks](https://rsinger86.github.io/django-lifecycle/) This is my new personal favorite. It's still in beta so I'm not as eager to recommend it for use in production, but I think it has the potential to increase readability and maintenance of [fat models](https://django-best-practices.readthedocs.io/en/latest/applications.html). It can replace both [Signals](https://docs.djangoproject.com/en/dev/ref/signals/) and `__init__` & `save` hooks. For example, if you are used to doing the following with model hooks: ```python class Article(LifecycleModel): contents = models.TextField() updated_at = models.DateTimeField(null=True) status = models.ChoiceField(choices=['draft', 'published']) editor = models.ForeignKey(AuthUser) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._orig_contents = self.contents self._orig_status = self.status def save(self, *args, **kwargs): if self.pk is not None and self.contents != self._orig_contents): self.updated_at = timezone.now() super().save(*args, **kwargs) if self.status != self._orig_status: send_email(self.editor.email, "An article has published!") ``` You can now do this: ```python from django_lifecycle import LifecycleModel, hook, BEFORE_UPDATE, AFTER_UPDATE class Article(LifecycleModel): contents = models.TextField() updated_at = models.DateTimeField(null=True) status = models.ChoiceField(choices=['draft', 'published']) editor = models.ForeignKey(AuthUser) @hook(BEFORE_UPDATE, when='contents', has_changed=True) def on_content_change(self): self.updated_at = timezone.now() @hook(AFTER_UPDATE, when="status", was="draft", is_now="published") def on_publish(self): send_email(self.editor.email, "An article has published!") ``` The hooks can also reference changes to fields of a related (e.g: foreign key-ed) object. Again, it's important to note that this package is in beta and thus benchmarking and viewing raw SQL is important. ## 4 [Django Extensions](https://github.com/django-extensions/django-extensions) Django Extensions is one of the oldest Django packages that I know of, dating back to at least 2008. It deserves an article of its own, but here are a few of the features I use: ### shell_plus `./manage.py shell_plus` will launch a Django shell with all the models of all your apps imported and ready to use. I like to combine it with IPython for a better autocompletion experience. ### RunScript Sometimes you want to run a script in your Django application. Typically when you are writing your script, you will use the Django shell to drive your development, then wrap the functionality in a Django command. However, with this command, you can run the script directly by wrapping it in a simple function. For example, if you wanted to write a script to delete all Question objects in your database, it would be as simple as: ```python # scripts/delete_all_questions.py from polls.models import Question def run(): # Fetch all questions questions = Question.objects.all() # Delete questions questions.delete() ``` Then to run the script, you would: ``` ./manage.py manage.py runscript delete_all_questions ``` ### syncdata syncdata will reset your database such that it only contains the fixtures you specified, exactly as you specified them in the fixtures file. ### graph_models This command will graph the models of your applications, allowing you to see the underlying [relations](https://en.wikipedia.org/wiki/Relation_(database)), similar to an [entity-relationship diagram](https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model). ![entity-relationship diagram](https://docs.monadical.com/uploads/upload_77f772290b99de48fe5d20418345ec5c.png) ## 5 [Cookiecutter Django](https://github.com/pydanny/cookiecutter-django) If you have set up a Django project before, you will know that `django-admin startproject mysite` is not sufficient for any of the bells and whistles needed for modern web app development, such as environment variables for configuration, Postgres integration out of the box, docker(-compose) setup, etc. Django Cookiecutter takes care of that for you. I recommend using it *after* you have familiarized yourself with each component that you plan on utilizing separately. Otherwise, the amount of magic and decisions made for you will hinder your ability to debug your application in the future. Finally, it can also lead to a lot of dead code in your codebase if you don’t know what’s used for what, so be careful! --- <center> <img src="https://monadical.com/static/logo-black.png" style="height: 80px"/><br/> Monadical.com | Full-Stack Consultancy *We build software that outlasts us* </center>
- The Rust Journey, Part 0: Why Systems Software and Why Rust?
- Operator Overloading in Python
- How to Plot Mathematical Functions in 10 Lines of Python
- Starting and Running Your First Program on a Mainframe
- View more posts...
Back to top