Open In App

Django 5.0: Significant Features for Web Development in 2024

Django released its latest major version, Django 5.0, on December 4, 2023. This version also offers new features, deprecations, updated functionalities, and more that Django developers can utilize to enhance web application development processes. For a better understanding, let us delve deeper into this version’s core and minor features. In this article we discuss the Django 5.0: Significant Features for Web Development in 2024 those are following.

Django 5.0

Django 5.0 Facets: Managing Admin Changelist Facet Counts

Django, a high-level Python Web Framework, empowers Developers to build secure and scalable web applications easily. Its elegant syntax, rapid development tools, and vast ecosystem of readily available Django packages make it a popular choice for web development projects of all sizes and complexities. Furthermore, an active developer community continuously improves its ecosystem through major and minor version updates for optimized web application development. Minor versions deliver bug updates, adjustments, and deprecations, while major versions unveil new features, improved functionality, and bug updates, adjustments, and deprecations. Django 5.0 recently introduced the concept of Facets. Facets refer to numerical indicators showing the number of results that match each filter.

The new “ModelAdmin.show_facets” attribute enables developers to determine whether to displace facet counts in the admin changelist. The attribute, by default, is set to “ShowFacets.ALLOW,” which automatically updates/changes facet counts to display the current filter set applied in the admin changelist during the presence of a specific query string parameter. However, this default setting can increase database queries and affect an application’s performance.



Alternatively, Django programmers can use the “ModelAdmin.show_facets” attribute’s other two settings to manage the display of facet counts as required:

Enabling Facets with Automatic Updates: This code shows how to enable Facets with the default behavior of automatic updates based on the current filter set:




from django.contrib import admin
from django.contrib.admin import ShowFacets
  
class MyModelAdmin(admin.ModelAdmin):
    show_facets = ShowFacets.ALLOW
      
    # ... other model admin definitions
admin.site.register(MyModel, MyModelAdmin)

Django 5.0 : Form Field Rendering Enhancements

Django 5.0 introduced field group and field group templates. These concepts enable developers to render different parts of a form field easily, like errors, help texts, labels, and widgets. Developers can access each form field “{{ form.name_of_field }}” in templates in Django forms. Each field comprises an “as_field_group()” function that renders the various parts of the form field. Therefore, the “as_field_group()” function helps developers create generic templates for organizing field elements in the preferred/required layout.

Django, by default, uses the “django/forms/field.html” template and the “django/forms/div.html” form style. However, developers can customize the “django/forms/field.html” template on a Customization options:

Forms in django:




# Example Code for Form Types
class MyForm(forms.Form):
    name = forms.CharField(label='Name', max_length=100)
    email = forms.EmailField(label='Email')
    file = forms.FileField(label='File')

Creating Forms in Django :




from django import forms
class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)

Customized Form Example :




from django.shortcuts import render
def my_view(request):
    if request.method == 'POST':
        form = MyForm(request.POST)
        if form.is_valid():
            # Process the form data
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            age = form.cleaned_data['age']
            # …
    Else:
        form = MyForm()
    return render(request, 'my_template.html', {'form': form})

Rendering Form Fields:




<form method="post">
  {% csrf_token %}
  {% for field in form %}
    <div class="form-group">
      {{ field.label_tag }}
      {{ field }}
      {% if field.help_text %}
        <small class="form-text text-muted">{{ field.help_text }}</small>
      {% endif %}
      {% for error in field.errors %}
        <div class="alert alert-danger">{{ error }}</div>
      {% endfor %}
    </div>
  {% endfor %}
  <button type="submit">Submit</button>
</form>

Django 5.0: Simplifies Database-Computed Values

The newly introduced Field.db_default parameter enables a Django developer to set a database-computed value for a particular field. The value can be a simple one or a database function like “Now”. For example, a “DateTimeField” can use “Now()” as its default value.

Developers can also set more complex defaults if those defaults are made from simple values and database functions. However, the defaults cannot refer to other fields or models. When both “db_default” and “Field.default” are specified, Django will apply the former at the database level and the latter in the Python code. This helps developers add rows from the ORM or insert new fields during migration.

Simple default value:




from django.db import models
from django.db.models.functions import Now
class MyModel(models.Model):
    created_at = models.DateTimeField(db_default=Now())  # Sets default to current time
    priority = models.IntegerField(db_default=0)       # Sets default to 0

More Complex Default Value:




from django.db import models
from django.db.models.functions import TruncMonth, Now, F
class MyModel(models.Model):
    month_due = models.DateField(
        db_default=TruncMonth(Now() + timedelta(days=90), output_field=models.DateField())
    # Sets default to the first day of the month 90 days from now
    end = models.IntegerField(db_default=F("start") + 50# Sets default to 50 more than the "start" field

Django 5.0 : Database-Generated Columns with “GeneratedField”

The new “GeneratedField” is always computed based on the model’s other fields. It uses the GENERATED ALWAYS SQL syntax. The database itself manages and updates the “GeneratedField” class. Furthermore, it also allows developers to create database-generated columns.

The “GeneratedField” delivers two types of generated columns, including stored and virtual. The value of a stored generated column is stored in the database and calculated when data is written, added, or updated. On the other hand, the value of a virtual generated column does not require any storage and is calculated when the data is read.

The three primary attributes of the “GeneratedField” class includes:

Simple Area Calculation from Dimensions:




from django.db import models
from django.db.models import F
  
class Square(models.Model):
    side = models.PositiveIntegerField()
    area = models.GeneratedField(
        expression=F("side") * F("side"),
        output_field=models.IntegerField(),
        db_persist=True)

More Complex discount calculation:




from django.db import models
from django.db.models import F, Func
  
class Product(models.Model):
    price = models.DecimalField(max_digits=10, decimal_places=2)
    discount_rate = models.DecimalField(max_digits=2, decimal_places=2)
    final_price = models.GeneratedField(
        expression=F("price") * (1 - Func('greatest', F("discount_rate"), 0.1)),
        output_field=models.DecimalField(max_digits=10, decimal_places=2),
        db_persist=True
    )

Django 5.0: Flexible Model and Form Field Choices

Previously, we have choices had to be a list of 2-tuples or a subclass of Enumeration types. Furthermore, the subclass must be able to access the “.choices” attribute to deliver values in expected formats. Django 5.0 enabled support for using mapping and callables for defining model fields (Field.choices) and form fields (ChoiceField.choices). This not only provides greater flexibility but also more versatility while declaring choices.

Furthermore, developers no longer need to use the “.choices” attribute directly when using Enumeration types. Django now automatically converts the provided choices into a standardized list of 2-tuples. This ensures greater consistency irrespective of how the choices are initially defined.




from django.db import models
  
class MyChoices(models.TextChoices):
    CHOICE_A = 'A', 'Option A'
    CHOICE_B = 'B', 'Option B'
    CHOICE_C = 'C', 'Option C'
      
class MyModel(models.Model):
    option = models.CharField(max_length=1, choices=MyChoices)

In this example, you can directly use MyChoices when defining the choices attribute for the option field. You no longer need to access .choices explicitly. Django will automatically handle the conversion to the expected format. This change streamlines the process of using enumeration types with Django and ensures consistency in how choices are represented.

Django 5.0: Minor Features Overview

In the above sections, we have defined the core features of Django 5.0. However, the version also has some minor features in reserve, which can benefit developers significantly. Let’s have a quick look at those.

Customizing Querysets For Log Entries:




from django.contrib import admin
from django.contrib.admin.models import LogEntry, CHANGE
  
class MyAdminSite(admin.AdminSite):
    def get_log_entries(self, request, **kwargs):
        queryset = super().get_log_entries(request, **kwargs)
        # Customize the queryset here:
        queryset = queryset.filter(action_flag=CHANGE)  # Example: Filter for only change actions
        queryset = queryset.order_by('-action_time'# Example: Order by action time descending
        return queryset
admin_site = MyAdminSite()  # Register your custom admin site

Checking User Passwords Asynchronously:




from django.contrib import admin
from myapp.models import MyModel
# Assuming MyModel is registered with the admin site:
model_admin = admin.site.get_model_admin(MyModel)  # Returns the model admin class
# If MyModel is not registered:
try:
    model_admin = admin.site.get_model_admin(MyModel)
except admin.sites.NotRegistered:
    print("MyModel is not registered with the admin site.")

Receiving Admin Classes For Registered Model Classes:




from django.contrib.auth.models import AbstractBaseUser
class MyUser(AbstractBaseUser):
    # ... other user model fields and methods
    async def check_password(self, password):
        # Implement your asynchronous password verification logic here
        # (e.g., compare password with hashed password stored in database)
        # …
        return is_valid_password
async def my_login_view(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = await User.objects.get_by_natural_key(username)
        if user and await user.acheck_password(password):
            # Login successful
            # …
        Else:
            # Invalid credentials
            # …

Checking distance threshold –




from django.contrib.gis.measure import D
threshold = 10  # Define your acceptable distance threshold
point1 = MyModel.objects.first().geom
point2 = AnotherModel.objects.first().geom
closest_point = point1.ClosestPoint(point2)
if point1.distance(closest_point) < D(m=threshold):
    # Valid nearest point based on distance
    # …
else:
    # Invalid due to exceeding distance threshold
    # …

Analyzing Geometries’ Equivalence: The GEOSGeometry.equals_identical() method checks if two geometries are dimensionally identical and equivalent in terms of orders, structure, and values. It returns “True” if it finds both the geometries are equivalent.




from django.contrib.gis.geos import GEOSGeometry
point1 = GEOSGeometry('POINT(5 23)')
point2 = GEOSGeometry('POINT(5 23)')
identical = point1.equals_identical(point2)  # True
  
polygon1 = GEOSGeometry('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))')
polygon2 = GEOSGeometry('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))')
identical = polygon1.equals_identical(polygon2)  # True
  
different_point = GEOSGeometry('POINT(5.1 23)')
identical = point1.equals_identical(different_point)  # False (different coordinates)
  
different_polygon = GEOSGeometry('POLYGON((0 0, 10 5, 10 10, 0 10, 0 0))')
identical = polygon1.equals_identical(different_polygon)  # False (different structure)




from django.core.paginator import Paginator
paginator = Paginator(my_object_list, 10, error_messages={
    'invalid_page': 'Please enter a valid page number.',
    'min_page': 'There is no page before the first page.',
    'no_results': 'This page does not contain any results.',
})
Try:
    page = paginator.page(request.GET.get('page'))
except Paginator.InvalidPage:
    # Handle invalid page error with your customized message
    pass

Escaping elements of sequences: This “escapeseq template” filter allows developers to apply escape to a sequence’s elements.

{% for item in my_list %}
<p>{{ item|escape }}</p>
{% endfor %}


Detecting slowest test cases: This “test –durations” option displays the duration of the slowest test cases on Python 3.12+.

Output:

$ python manage.py test --durations
slowest tests (times in seconds):
1. (5.23s) tests.test_slow_function
2. (2.17s) tests.test_complex_calculation
3. (1.89s) tests.test_large_database_query


Simplified setting of offsets for valid values: The newly added offset argument in the “StepValueValidator” function allows developers to set offsets for valid values.




from django.db import models
class Product(models.Model):
    price = models.IntegerField(
        validators=[StepValueValidator(10, offset=5)]
    # Valid values: 5, 15, 25, 35, …

Django 5.0 Syntax Updates

Here’s a concise overview of the notable syntax changes and additions in Django 5.0 release:

Additionally, Specific Feature Enhancements Were Made:

These changes mark a significant advancement in Django, bringing enhancements and innovative features that streamline the development process.

Conclusion

The latest version also offers changes, deprecations, and more that will help optimize web application development approaches. You can check Django’s official documentation to learn more about them. Django will offer new versions comprising more advanced features and modifications in the upcoming years. Also, Django 5.0 enhances the utility of Django packages by ensuring compatibility with the latest features, fostering a community-driven approach for robust, reusable components, and streamlining development with pre-built, easily integrable modules. So stay tuned to learn more about them.


Article Tags :