{"id":1035,"date":"2019-11-12T22:54:12","date_gmt":"2019-11-12T21:54:12","guid":{"rendered":"http:\/\/35.180.88.53\/?p=1035"},"modified":"2019-11-12T22:54:17","modified_gmt":"2019-11-12T21:54:17","slug":"advanced-django-models-improve-your-python-development","status":"publish","type":"post","link":"https:\/\/www.sergilehkyi.com\/uk\/2019\/11\/advanced-django-models-improve-your-python-development\/","title":{"rendered":"Advanced Django Models: Improve Your Python Development"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Models are a core concept of the Django framework. According to Django\u2019s&nbsp;<a href=\"https:\/\/docs.djangoproject.com\/en\/2.2\/misc\/design-philosophies\/#models\">design philosophies for models<\/a>, we should be as explicit as possible with the naming and functionality of our fields, and ensure that we\u2019re including all relevant functionality related to our model in the model itself, rather than in the views or somewhere else. If you\u2019ve worked with Ruby on Rails before, these design philosophies won\u2019t seem new as both Rails and Django implement the&nbsp;<a href=\"https:\/\/www.martinfowler.com\/eaaCatalog\/activeRecord.html\">Active Record pattern<\/a>&nbsp;for their object-relational mapping (ORM) systems to handle stored data.&nbsp;<\/p>\n\n\n\n<p>In this post we\u2019ll look at some ways to leverage these philosophies, core Django features, and even some libraries to help make our models better.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><\/strong>getter\/setter\/deleter&nbsp;properties<\/h2>\n\n\n\n<p>As a feature of Python since version 2.2, a property\u2019s usage looks like an attribute but is actually a method. While using a property on a model isn\u2019t that advanced, we can use some underutilized features of the Python property to make our models more powerful.&nbsp;<\/p>\n\n\n\n<p>If you\u2019re using Django\u2019s built-in authentication or have customized your authentication using&nbsp;<code>AbstractBaseUser<\/code>, you\u2019re probably familiar with the&nbsp;<code>last_login<\/code>&nbsp;field defined on the&nbsp;<code>User<\/code>&nbsp;model, which is a saved timestamp of the user\u2019s last login to your application. If we want to use&nbsp;<code>last_login<\/code>, but also have a field named&nbsp;<code>last_seen<\/code>&nbsp;saved to a cache more frequently, we could do so pretty easily.<\/p>\n\n\n\n<p>First, we\u2019ll make a Python&nbsp;<em>property<\/em>&nbsp;that finds a value in the cache, and if it can\u2019t, it returns the value from the database.<\/p>\n\n\n\n<p><code>accounts\/models.py<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from django.contrib.auth.base_user import AbstractBaseUser\nfrom django.core.cache import cache\n\n\nclass User(AbstractBaseUser):\n    ...\n    \n    @property\n    def last_seen(self):\n        \"\"\"\n        Returns the 'last_seen' value from the cache for a User.\n        \"\"\"\n        last_seen = cache.get('last_seen_{0}'.format(self.pk))\n\n        # Check cache result, otherwise return the database value\n        if last_seen:\n            return last_seen\n\n        return self.last_login<\/code><\/pre>\n\n\n\n<p>Note: I\u2019ve slimmed the model down a bit as there\u2019s a separate tutorial on this blog about specifically&nbsp;<a href=\"https:\/\/kite.com\/blog\/python\/custom-django-user-model\/\">customizing the built-in Django user model<\/a>.<\/p>\n\n\n\n<p>The property above checks our cache for the user\u2019s&nbsp;<code>last_seen<\/code>&nbsp;value, and if it doesn\u2019t find anything, it will return the user\u2019s stored&nbsp;<code>last_login<\/code>&nbsp;value from the model. Referencing&nbsp;<code>&lt;instance&gt;.last_seen<\/code>&nbsp;now provides a much more customizable attribute on our model behind a very simple interface.<\/p>\n\n\n\n<p>We can expand this to include custom behavior when a value is assigned to our property (<code>some_user.last_seen = some_date_time<\/code>), or when a value is deleted from the property (<code>del some_user.last_seen<\/code>).&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\n    \n@last_seen.setter\ndef last_seen(self, value):\n    \"\"\"\n    Sets the 'last_seen_[uuid]' value in the cache for a User.\n    \"\"\"\n    now = value\n\n    # Save in the cache\n    cache.set('last_seen_{0}'.format(self.pk), now)\n\n@last_seen.deleter\ndef last_seen(self):\n    \"\"\"\n    Removes the 'last_seen' value from the cache.\n    \"\"\"\n    # Delete the cache key\n    cache.delete('last_seen_{0}'.format(self.pk))\n    \n...<\/code><\/pre>\n\n\n\n<p>Now, whenever a value is assigned to our&nbsp;<code>last_seen<\/code>&nbsp;property, we save it to the cache, and when a value is removed with&nbsp;<code>del<\/code>, we remove it from the cache. Using&nbsp;<code>setter<\/code>&nbsp;and&nbsp;<code>deleter<\/code>&nbsp;is described in the Python documentation but is rarely seen in the wild when looking at Django models.&nbsp;<\/p>\n\n\n\n<p>You may have a use case like this one, where you want to store something that doesn\u2019t necessarily need to be persisted to a traditional database, or for performance reasons, shouldn\u2019t be. Using a custom property like the above example is a great solution.<\/p>\n\n\n\n<p>In a similar use case, the&nbsp;<code>python-social-auth<\/code>&nbsp;library, a tool for managing user authentication using third-party platforms like GitHub and Twitter, will create and manage updating information in your database based on information from the platform the user logged-in with. In some cases, the information returned won\u2019t match the fields in our database. For example, the&nbsp;<code>python-social-auth<\/code>&nbsp;library will pass a&nbsp;<code>fullname<\/code>&nbsp;keyword argument when creating the user. If, perhaps in our database, we used&nbsp;<code>full_name<\/code>&nbsp;as our attribute name then we might be in a pinch.<\/p>\n\n\n\n<p>A simple way around this is by using the&nbsp;<code>getter\/setter<\/code>&nbsp;pattern from above:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@property\ndef fullname(self) -> str:\n    return self.full_name\n\n@fullname.setter\ndef fullname(self, value: str):\n    self.full_name = value<\/code><\/pre>\n\n\n\n<p>Now, when&nbsp;<code>python-social-auth<\/code>&nbsp;saves a user\u2019s&nbsp;<code>fullname<\/code>&nbsp;to our model (<code>new_user.fullname = 'Some User'<\/code>), we\u2019ll intercept it and save it to our database field,&nbsp;<code>full_name<\/code>, instead.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><\/strong>through&nbsp;model relationships<\/h2>\n\n\n\n<p>Django\u2019s&nbsp;<a href=\"https:\/\/docs.djangoproject.com\/en\/2.2\/topics\/db\/examples\/many_to_many\/#many-to-many-relationships\">many-to-many relationships<\/a>&nbsp;are a great way of handling complex object relationships simply, but they don\u2019t afford us the ability to add custom attributes to the&nbsp;<code>intermediate models<\/code>&nbsp;they create. By default, this simply includes an identifier and two foreign key references to join the objects together.<\/p>\n\n\n\n<p>Using the Django&nbsp;<a href=\"https:\/\/docs.djangoproject.com\/en\/2.2\/topics\/db\/models\/#extra-fields-on-many-to-many-relationships\"><code>ManyToManyField through<\/code><\/a>&nbsp;parameter, we can create this intermediate model ourselves and add any additional fields we deem necessary.&nbsp;<\/p>\n\n\n\n<p>If our application, for example, not only needed users to have memberships within groups, but wanted to track when that membership started, we could use a custom intermediate model to do so.<\/p>\n\n\n\n<p><code>accounts\/models.py<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import uuid\n\nfrom django.contrib.auth.base_user import AbstractBaseUser\nfrom django.db import models\nfrom django.utils.timezone import now\n\n\nclass User(AbstractBaseUser):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    \u2026\n\nclass Group(models.Model):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    members = models.ManyToManyField(User, through='Membership')\n\nclass Membership(models.Model):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    user = models.ForeignKey(User, on_delete=models.CASCADE)\n    group = models.ForeignKey(Group, on_delete=models.CASCADE)\n    joined = models.DateTimeField(editable=False, default=now)<\/code><\/pre>\n\n\n\n<p>In the example above, we\u2019re still using a&nbsp;<code>ManyToManyField<\/code>&nbsp;to handle the relationship between a user and a group, but by passing the&nbsp;<code>Membership<\/code>&nbsp;model using the&nbsp;<code>through<\/code>&nbsp;keyword argument, we can now add our&nbsp;<code>joined<\/code>&nbsp;custom attribute to the model to track when the group membership was started. This&nbsp;<code>through<\/code>&nbsp;model is a standard Django model, it just requires a primary key (we use UUIDs here), and two foreign keys to join the objects together.<\/p>\n\n\n\n<p>Using the same three model pattern, we could create a simple subscription database for our site:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import uuid\n\nfrom django.contrib.auth.base_user import AbstractBaseUser\nfrom django.db import models\nfrom django.utils.timezone import now\n\n\nclass User(AbstractBaseUser):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    ...\n\nclass Plan(models.Model):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    name = models.CharField(max_length=50, unique=True, default='free')\n    subscribers = models.ManyToManyField(User, through='Subscription', related_name='subscriptions', related_query_name='subscriptions')\n\nclass Subscription(models.Model):\n    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)\n    user = models.ForeignKey(User, on_delete=models.CASCADE)\n    plan = models.ForeignKey(Plan, on_delete=models.CASCADE)\n    created = models.DateTimeField(editable=False, default=now)\n    updated = models.DateTimeField(auto_now=True)\n    cancelled = models.DateTimeField(blank=True, null=True)<\/code><\/pre>\n\n\n\n<p>Here we\u2019re able to track when a user first subscribed, when they updated their subscription, and if we added the code paths for it, when a user canceled their subscription to our application.<\/p>\n\n\n\n<p>Using&nbsp;<code>through<\/code>&nbsp;models with the&nbsp;<code>ManyToManyField<\/code>&nbsp;is a great way to add more data to our intermediate models and provide a more thorough experience for our users without much added work.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><\/strong>Proxy models<\/h2>\n\n\n\n<p>Normally in Django, when you subclass a model (this doesn\u2019t include&nbsp;<em>abstract models<\/em>) into a new class, the framework will create new database tables for that class and link them (via&nbsp;<code>OneToOneField<\/code>) to the parent database tables. Django calls this \u201c<a href=\"https:\/\/docs.djangoproject.com\/en\/2.2\/topics\/db\/models\/#multi-table-inheritance\">multi-table inheritance<\/a>\u201d and it\u2019s a great way to re-use existing model fields and structures and add your own data to them. \u201cDon\u2019t repeat yourself,\u201d as the Django design philosophies state.<\/p>\n\n\n\n<p>Multi-table inheritance example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from django.db import models\n\nclass Vehicle(models.Model):\n    model = models.CharField(max_length=50)\n    manufacturer = models.CharField(max_length=80)\n    year = models.IntegerField(max_length=4)\n\nclass Airplane(Vehicle):\n    is_cargo = models.BooleanField(default=False)\n    is_passenger = models.BooleanField(default=True)<\/code><\/pre>\n\n\n\n<p>This example would create both&nbsp;<code>vehicles_vehicle<\/code>&nbsp;and&nbsp;<code>vehicles_airplane<\/code>&nbsp;database tables,linked with foreign keys. This allows us to leverage the existing data that lives inside&nbsp;<code>vehicles_vehicle<\/code>, while adding our own vehicle specific attributes to each subclass,&nbsp;<code>vehicle_airplane<\/code>, in this case.<\/p>\n\n\n\n<p>In some use cases, we may not need to store extra data at all. Instead, we could change some of the parent model\u2019s behavior, maybe by adding a method, property, or model manager. This is where&nbsp;<code>proxy models<\/code>&nbsp;shine.&nbsp;<code>Proxy models<\/code>&nbsp;allow us to change the Python behavior of a model&nbsp;<em>without<\/em>&nbsp;changing the database.&nbsp;<\/p>\n\n\n\n<p><code>vehicles\/models.py<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from django.db import models\n\nclass Car(models.Model):\n    vin = models.CharField(max_length=17)\n    model = models.CharField(max_length=50)\n    manufacturer = models.CharField(max_length=80)\n    year = models.IntegerField(max_length=4)\n    ...\n\nclass HondaManager(models.Manager):\n    def get_queryset(self):\n        return super(HondaManager, self).get_queryset().filter(model='Honda')\n\nclass Honda(Car):\n    objects = HondaManager()\n    \n    class Meta:\n        proxy = True\n    \n    @property\n    def is_domestic(self):\n        return False\n    \n    def get_honda_service_logs(self):\n        ...<\/code><\/pre>\n\n\n\n<p><code>Proxy models<\/code>&nbsp;are declared just like normal models. In our example, we tell Django that Honda is a&nbsp;<code>proxy model<\/code>&nbsp;by setting the&nbsp;<code>proxy<\/code>&nbsp;attribute of the Honda&nbsp;<code>Meta<\/code>&nbsp;class to&nbsp;<code>True<\/code>. I\u2019ve added a property and a method stub example, but you can see we\u2019ve added a custom model manager to our Honda&nbsp;<code>proxy model<\/code>.<\/p>\n\n\n\n<p>This ensures that whenever we request objects from the database using our Honda model, we get only&nbsp;<code>Car<\/code>&nbsp;instances back where&nbsp;<code>model= 'Honda'<\/code>. Proxy models make it easy for us to quickly add customizations on top of existing models using the same data. If we were to delete, create, or update any&nbsp;<code>Car<\/code>&nbsp;instance using our Honda model or manager, it would be saved into the&nbsp;<code>vehicles_car<\/code>&nbsp;database just as if we were using the parent (<code>Car<\/code>) class.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><\/strong>Wrap up<\/h2>\n\n\n\n<p>If you\u2019re already comfortable working in Python classes, then you\u2019ll feel right at home with Django\u2019s models: inheritance, multiple inheritance, method overrides, and introspection. These models are all part of how the Django object-relational mapper was designed.&nbsp;<\/p>\n\n\n\n<p>Multi-table inheritance and manually defining intermediate tables for SQL joins aren\u2019t necessarily basic concepts, but are implemented simply with a bit of Django and Python knowh-ow. Being able to leverage features of the language and framework alongside one another is one of the reasons Django is a popular web framework.<\/p>\n\n\n\n<p>For further reading, check out Django\u2019s\u00a0<a href=\"https:\/\/docs.djangoproject.com\/en\/2.2\/topics\/db\/models\/\">documentation topic for models<\/a>. The second half of the page covers some of what we\u2019ve covered here and more\u2013 I\u2019m always learning something new when I read a page of their high-level topics.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p><em>This article was originally published at\u00a0<a href=\"https:\/\/kite.com\/blog\/python\/advanced-django-models-python-overview\/\">Kite\u2018s<\/a>\u00a0blog and republished here as part of content partnership program.\u00a0<a href=\"https:\/\/kite.com\/\">Kite<\/a>\u00a0is a plugin for your IDE that uses machine learning to give you\u00a0useful code completions for Python. Available for Atom, PyCharm, Sublime, VS Code, and Vim.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Models are a core concept of the Django framework. According to Django\u2019s&nbsp;design philosophies for models, we should be as&hellip;<\/p>\n","protected":false},"author":1,"featured_media":1036,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,5],"tags":[],"translation":{"provider":"WPGlobus","version":"3.0.0","language":"uk","enabled_languages":["gb","es","uk"],"languages":{"gb":{"title":true,"content":true,"excerpt":false},"es":{"title":false,"content":false,"excerpt":false},"uk":{"title":false,"content":false,"excerpt":false}}},"_links":{"self":[{"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/posts\/1035"}],"collection":[{"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/comments?post=1035"}],"version-history":[{"count":1,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/posts\/1035\/revisions"}],"predecessor-version":[{"id":1037,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/posts\/1035\/revisions\/1037"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/media\/1036"}],"wp:attachment":[{"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/media?parent=1035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/categories?post=1035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sergilehkyi.com\/uk\/wp-json\/wp\/v2\/tags?post=1035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}