django-expert

Expert guidance for Django - high-level Python web framework for building secure, scalable web applications with batteries included.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "django-expert" with this command: npx skills add personamanagmentlayer/pcl/personamanagmentlayer-pcl-django-expert

Django Expert

Expert guidance for Django - high-level Python web framework for building secure, scalable web applications with batteries included.

Core Concepts

Django Architecture

  • MVT (Model-View-Template) pattern

  • ORM (Object-Relational Mapping)

  • Admin interface

  • Authentication system

  • URL routing

  • Template engine

  • Forms and validation

Key Components

  • Models (database tables)

  • Views (business logic)

  • Templates (presentation)

  • URLs (routing)

  • Forms (user input)

  • Middleware (request/response processing)

Project Setup

Install Django

pip install django

Create project

django-admin startproject myproject cd myproject

Create app

python manage.py startapp myapp

Run migrations

python manage.py migrate

Create superuser

python manage.py createsuperuser

Run server

python manage.py runserver

Models

myapp/models.py

from django.db import models from django.contrib.auth.models import User from django.utils import timezone

class Post(models.Model): STATUS_CHOICES = [ ('draft', 'Draft'), ('published', 'Published'), ]

title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
content = models.TextField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(null=True, blank=True)

class Meta:
    ordering = ['-created_at']
    indexes = [
        models.Index(fields=['-created_at']),
        models.Index(fields=['slug']),
    ]

def __str__(self):
    return self.title

def save(self, *args, **kwargs):
    if self.status == 'published' and not self.published_at:
        self.published_at = timezone.now()
    super().save(*args, **kwargs)

class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True)

class Meta:
    ordering = ['created_at']

def __str__(self):
    return f'Comment by {self.author} on {self.post}'

Views

myapp/views.py

from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.views.generic import ListView, DetailView, CreateView from django.contrib import messages from .models import Post, Comment from .forms import PostForm, CommentForm

Function-based view

def post_list(request): posts = Post.objects.filter(status='published').select_related('author') return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, slug): post = get_object_or_404(Post, slug=slug, status='published') comments = post.comments.select_related('author')

if request.method == 'POST':
    form = CommentForm(request.POST)
    if form.is_valid():
        comment = form.save(commit=False)
        comment.post = post
        comment.author = request.user
        comment.save()
        messages.success(request, 'Comment added successfully')
        return redirect('post_detail', slug=slug)
else:
    form = CommentForm()

return render(request, 'blog/post_detail.html', {
    'post': post,
    'comments': comments,
    'form': form
})

Class-based views

class PostListView(ListView): model = Post template_name = 'blog/post_list.html' context_object_name = 'posts' paginate_by = 10

def get_queryset(self):
    return Post.objects.filter(status='published').select_related('author')

class PostDetailView(DetailView): model = Post template_name = 'blog/post_detail.html' context_object_name = 'post'

def get_queryset(self):
    return Post.objects.filter(status='published')

class PostCreateView(CreateView): model = Post form_class = PostForm template_name = 'blog/post_form.html'

def form_valid(self, form):
    form.instance.author = self.request.user
    return super().form_valid(form)

URLs

myproject/urls.py

from django.contrib import admin from django.urls import path, include

urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), ]

myapp/urls.py

from django.urls import path from . import views

app_name = 'blog'

urlpatterns = [ path('', views.PostListView.as_view(), name='post_list'), path('post/<slug:slug>/', views.post_detail, name='post_detail'), path('post/create/', views.PostCreateView.as_view(), name='post_create'), ]

Forms

myapp/forms.py

from django import forms from .models import Post, Comment

class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'slug', 'content', 'status'] widgets = { 'content': forms.Textarea(attrs={'rows': 10}), }

def clean_slug(self):
    slug = self.cleaned_data['slug']
    if Post.objects.filter(slug=slug).exists():
        raise forms.ValidationError('Slug already exists')
    return slug

class CommentForm(forms.ModelForm): class Meta: model = Comment fields = ['content'] widgets = { 'content': forms.Textarea(attrs={'rows': 3}), }

Django REST Framework

Install: pip install djangorestframework

settings.py

INSTALLED_APPS = [ ... 'rest_framework', ]

serializers.py

from rest_framework import serializers from .models import Post, Comment

class CommentSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField()

class Meta:
    model = Comment
    fields = ['id', 'author', 'content', 'created_at']

class PostSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField() comments = CommentSerializer(many=True, read_only=True)

class Meta:
    model = Post
    fields = ['id', 'title', 'slug', 'author', 'content',
              'status', 'created_at', 'comments']

views.py (API)

from rest_framework import viewsets, permissions from rest_framework.decorators import action from rest_framework.response import Response

class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.filter(status='published') serializer_class = PostSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly]

def perform_create(self, serializer):
    serializer.save(author=self.request.user)

@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
    post = self.get_object()
    post.status = 'published'
    post.save()
    return Response({'status': 'published'})

urls.py (API)

from rest_framework.routers import DefaultRouter

router = DefaultRouter() router.register(r'posts', PostViewSet)

urlpatterns = [ path('api/', include(router.urls)), ]

Admin Interface

myapp/admin.py

from django.contrib import admin from .models import Post, Comment

@admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'author', 'status', 'created_at'] list_filter = ['status', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields = {'slug': ('title',)} date_hierarchy = 'created_at'

actions = ['make_published']

def make_published(self, request, queryset):
    queryset.update(status='published')
make_published.short_description = "Mark selected as published"

@admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ['post', 'author', 'created_at'] list_filter = ['created_at'] search_fields = ['content']

Authentication

views.py

from django.contrib.auth import login, logout from django.contrib.auth.forms import UserCreationForm

def signup(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) return redirect('home') else: form = UserCreationForm() return render(request, 'registration/signup.html', {'form': form})

URLs

from django.contrib.auth import views as auth_views

urlpatterns = [ path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('signup/', signup, name='signup'), ]

Testing

myapp/tests.py

from django.test import TestCase, Client from django.contrib.auth.models import User from .models import Post

class PostModelTest(TestCase): def setUp(self): self.user = User.objects.create_user(username='test', password='test') self.post = Post.objects.create( title='Test Post', slug='test-post', author=self.user, content='Test content', status='published' )

def test_post_creation(self):
    self.assertEqual(self.post.title, 'Test Post')
    self.assertEqual(str(self.post), 'Test Post')

def test_post_slug_unique(self):
    with self.assertRaises(Exception):
        Post.objects.create(
            title='Another Post',
            slug='test-post',  # Duplicate slug
            author=self.user,
            content='Content'
        )

class PostViewTest(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create_user(username='test', password='test')

def test_post_list_view(self):
    response = self.client.get('/')
    self.assertEqual(response.status_code, 200)

def test_post_create_requires_auth(self):
    response = self.client.get('/post/create/')
    self.assertEqual(response.status_code, 302)  # Redirect to login

    self.client.login(username='test', password='test')
    response = self.client.get('/post/create/')
    self.assertEqual(response.status_code, 200)

Best Practices

  • Use select_related/prefetch_related to avoid N+1 queries

  • Implement proper validation in forms

  • Use class-based views for common patterns

  • Protect against CSRF attacks (enabled by default)

  • Use Django's built-in authentication

  • Implement proper permission checks

  • Add database indexes for frequently queried fields

  • Use Django migrations for schema changes

Resources

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

python-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

devops-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-review-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-expert

No summary provided by upstream source.

Repository SourceNeeds Review