First Look at Django 2.1: Model View Permissions

python backend django

Django 2.1 landed with a feature teams have wanted for years: model view permissions. Finally, you can give users read-only access to the admin without ugly workarounds.

The Problem Before

Django’s permission system had add, change, and delete. But no view permission. If you wanted someone to see data without modifying it, you had to:

All hacky solutions for a common need.

View Permissions in 2.1

Now every model automatically gets a view permission:

# Automatically created permissions for Book model:
# - books.add_book
# - books.change_book
# - books.delete_book
# - books.view_book  # NEW!

Admin Integration

The admin respects view permissions automatically:

from django.contrib import admin
from .models import Book

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    pass

A user with only view_book permission can:

Checking Permissions in Code

# In views
if request.user.has_perm('books.view_book'):
    # Show read-only view

# In templates
{% if perms.books.view_book %}
    <a href="{% url 'book-list' %}">View Books</a>
{% endif %}

Custom Permissions

You can still define custom permissions:

class Book(models.Model):
    title = models.CharField(max_length=200)
    
    class Meta:
        permissions = [
            ("export_book", "Can export books to CSV"),
            ("archive_book", "Can archive books"),
        ]

Migration Considerations

When upgrading to 2.1, view permissions are created automatically. Run migrations:

python manage.py migrate

Existing users won’t have view permission granted automatically—you’ll need to assign it.

Granting Permissions

Via Admin

Add users to groups with appropriate permissions.

Programmatically

from django.contrib.auth.models import Permission

view_perm = Permission.objects.get(codename='view_book')
user.user_permissions.add(view_perm)

Default for Staff

You might want all staff to have view access:

from django.contrib.auth.signals import user_logged_in

def grant_view_permissions(sender, user, request, **kwargs):
    if user.is_staff:
        # Grant view permissions for all models
        view_perms = Permission.objects.filter(codename__startswith='view_')
        user.user_permissions.add(*view_perms)

Other 2.1 Features

While we’re here, a few more highlights:

Model __str__ in Admin

The admin now shows __str__() in more places by default.

Relative Database URLs

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',  # Path object support
    }
}

Minor Improvements

Upgrade Path

From Django 2.0:

  1. Update requirements: Django>=2.1,<2.2
  2. Run python manage.py check for deprecation warnings
  3. Run migrations
  4. Test permission-sensitive flows

The upgrade should be smooth—no major breaking changes.

Final Thoughts

View permissions solve a real problem elegantly. If you’ve been waiting for this feature, Django 2.1 delivers.

For most projects, the upgrade is worth it just for this addition. Cleaner permission models make applications easier to reason about and maintain.


Small features, big improvements.

All posts