--- id: personamanagmentlayer/pcl/django-expert version: "66c0fd04" license: Apache-2.0 install: manual updated: 2026-03-30 --- # django-expert — Django Expert delivers in-depth instruction on the Django framework for constructing robust Python web applications. It covers the MVT pattern, database modeling through ORM, view logic, template rendering, URL routing, form handling, and Django's authentication system. The skill also explores REST API development and admin interface customization. Publisher: personamanagmentlayer · Stars: 38 · Updated: 2026-03-30 Install (manual): `git clone https://github.com/personamanagmentlayer/pcl` ## SKILL.md # 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 ```bash # 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 ```python # 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 ```python # 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 ```python # 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//', views.post_detail, name='post_detail'), path('post/create/', views.PostCreateView.as_view(), name='post_create'), ] ``` ## Forms ```python # 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 ```python # 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 ```python # 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 ```python # 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 ```python # 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 - Django Docs: https://docs.djangoproject.com/ - Django REST Framework: https://www.django-rest-framework.org/ - Django Packages: https://djangopackages.org/ [View on SkillFed](https://skillfed.io/personamanagmentlayer/pcl/django-expert) · [View on GitHub](https://github.com/personamanagmentlayer/pcl)