Django Syntax
Django Syntax — meaning the most important pieces of Python + Django-specific syntax that you will write 1000 times in every project.
I’m not going to give you a boring list of rules. Instead, I’m going to teach it like I’m sitting with you in a café, writing real code together on your laptop, explaining why each syntax choice exists, when to use which style, and what beginners usually get wrong.
We’ll cover the syntax patterns you’ll use every single day in models, views, templates, urls, admin, forms, etc.
Let’s go file-by-file, piece-by-piece — with real examples from your polls project.
1. Model Syntax – The Heart of Django
File: polls/models.py
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
from django.db import models from django.utils import timezone from django.utils.text import slugify class Question(models.Model): # 1. Basic fields – always start with these question_text = models.CharField( max_length=500, # required for CharField verbose_name="Question text", # human name in admin/forms help_text="Keep it under 500 chars", # tooltip in admin ) pub_date = models.DateTimeField( verbose_name="Date published", default=timezone.now, # callable, not timezone.now() ) # 2. Choices field (very common pattern) category = models.CharField( max_length=50, default="general", choices=[ ("general", "General"), ("fun", "Fun & Trivia"), ("politics","Politics & News"), ("sports", "Sports"), ("tech", "Technology"), ], ) # 3. Boolean + status fields is_active = models.BooleanField( default=True, verbose_name="Active (visible to public)", ) # 4. Slug – SEO-friendly URL part slug = models.SlugField( max_length=100, unique=True, blank=True, # allow blank during creation ) # 5. Important: override save() for auto-slug def save(self, *args, **kwargs): if not self.slug: base = slugify(self.question_text)[:90] self.slug = base counter = 1 while Question.objects.filter(slug=self.slug).exclude(pk=self.pk).exists(): self.slug = f"{base}-{counter}" counter += 1 super().save(*args, **kwargs) # 6. String representation – MUST HAVE def __str__(self): return self.question_text[:50] + "…" if len(self.question_text) > 50 else self.question_text # 7. Custom method – usable in admin/templates def was_published_recently(self): return self.pub_date >= timezone.now() - timezone.timedelta(days=1) # 8. Meta class – table options class Meta: verbose_name = "Poll Question" verbose_name_plural = "Poll Questions" ordering = ["-pub_date"] # newest first indexes = [ models.Index(fields=["pub_date"]), models.Index(fields=["slug"]), ] |
Common syntax mistakes beginners make here
- default=timezone.now (wrong) → default=timezone.now (callable, no parentheses)
- max_length missing on CharField/SlugField → error
- __str__ missing → admin shows ugly <Question object (1)>
- save() not calling super().save() → breaks everything
2. Admin Syntax – Where You Spend Most Time
File: polls/admin.py
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
from django.contrib import admin from django.utils.html import format_html from .models import Question, Choice class ChoiceInline(admin.TabularInline): model = Choice extra = 3 fields = ("choice_text", "votes") ordering = ["id"] @admin.register(Question) class QuestionAdmin(admin.ModelAdmin): # Most important line – controls columns list_display = ( "question_text_short", "category_colored", "pub_date", "was_published_recently", "is_active_badge", "vote_count", ) list_display_links = ("question_text_short",) list_filter = ("is_active", "category", "pub_date") search_fields = ("question_text", "slug") list_per_page = 20 date_hierarchy = "pub_date" inlines = [ChoiceInline] prepopulated_fields = {"slug": ("question_text",)} fieldsets = ( (None, {"fields": ("question_text", "slug")}), ("Status & Timing", { "fields": ("pub_date", "is_active", "category"), "classes": ("collapse",) }), ) # Custom column methods @admin.display(description="Question", ordering="question_text") def question_text_short(self, obj): return obj.question_text[:70] + "…" if len(obj.question_text) > 70 else obj.question_text @admin.display(description="Category") def category_colored(self, obj): color = {"fun": "#28a745", "politics": "#dc3545", "sports": "#0d6efd"}.get(obj.category, "#6c757d") return format_html('<span style="color:{};">{}</span>', color, obj.category.title()) @admin.display(description="Recent?", boolean=True) def was_published_recently(self, obj): return obj.was_published_recently() @admin.display(description="Active") def is_active_badge(self, obj): return format_html( '<span style="color:{};">{}</span>', "#198754" if obj.is_active else "#dc3545", "✔ Active" if obj.is_active else "✘ Inactive" ) @admin.display(description="Total Votes") def vote_count(self, obj): return obj.choices.aggregate(total=Sum("votes"))["total"] or 0 |
3. URL Syntax – Clean & Namespaced
File: polls/urls.py
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from django.urls import path from . import views app_name = "polls" # ← very important for {% url %} tag urlpatterns = [ path("", views.index, name="index"), path("<slug:slug>/", views.detail, name="detail"), path("<slug:slug>/vote/", views.vote, name="vote"), path("<slug:slug>/results/", views.results, name="results"), ] |
Main project mysite/urls.py:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
from django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), path("", include("pages.urls")), # home path("polls/", include("polls.urls")), # ← notice the prefix ] |
4. Template Syntax – Daily Bread
File: polls/templates/polls/index.html
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{% extends "base.html" %} {% block title %}Latest Polls{% endblock %} {% block content %} <h2>Active Polls</h2> {% if latest_questions %} <ul> {% for question in latest_questions %} <li> <a href="{% url 'polls:detail' slug=question.slug %}"> {{ question.question_text }} </a> <small>({{ question.vote_count }} votes)</small> </li> {% empty %} <li>No polls yet…</li> {% endfor %} </ul> {% else %} <p>No active polls available.</p> {% endif %} {% endblock %} |
Key syntax points:
- {% url ‘app_name:view_name’ arg1=var %} → never hardcode URLs
- {{ var|filter }} → filters like |date, |pluralize, |default:”N/A”
- {% if %}, {% for %}, {% empty %}, {% block %}, {% extends %}
5. View Syntax – Two Styles
Function-based (simple & clear):
|
0 1 2 3 4 5 6 7 8 9 10 11 |
def detail(request, slug): question = get_object_or_404(Question, slug=slug, is_active=True) return render(request, "polls/detail.html", { "question": question, "choices": question.choices.all(), }) |
Class-based (DRY, powerful):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from django.views.generic import DetailView class QuestionDetailView(DetailView): model = Question template_name = "polls/detail.html" slug_field = "slug" slug_url_kwarg = "slug" context_object_name = "question" def get_queryset(self): return Question.objects.filter(is_active=True) |
Quick Syntax Cheat-Sheet (Keep This in Mind)
- Model field: field_name = models.Type(…, verbose_name=”…”, default=…, choices=…)
- Admin: @admin.register(Model) + list_display = (…) + @admin.display()
- URL: path(“<converter:var>”, view, name=”name”) + app_name = “app”
- Template link: {% url ‘app:view’ arg=var %}
- ORM: Model.objects.filter(field__lookup=value).order_by(“-date”)[:5]
- F-expression: update(votes=F(“votes”) + 1)
Now tell me, boss:
- Which syntax part is still confusing? (models? admin? templates? urls?)
- Want to zoom in on one area (e.g. “explain all lookup types in filter()”)?
- Or ready to move to next big feature: Forms + Voting + POST handling?
You’re building strong fundamentals — keep asking, keep coding, you’re doing great! 🚀🇮🇳
