تعلّم إنشاء تطبيق CRUD باستخدام Django
إذا كنت تريد أن تفهم Django CRUD Application من الصفر بطريقة عملية وواضحة، فهذا المقال مكتوب لك. سنبني معًا تطبيقًا كاملًا خطوة بخطوة، ونفهم كيف يتم إنشاء البيانات، قراءتها، تعديلها، وحذفها داخل Django بأسلوب بسيط ومريح، مع أمثلة كود حقيقية يمكنك الاعتماد عليها في مشروعك القادم.
الهدف هنا ليس فقط أن تحفظ الأكواد، بل أن تفهم الفكرة وراء كل خطوة. لأنك عندما تفهم المنطق، ستستطيع بناء أي CRUD Application في Django بنفسك، سواء كان تطبيقًا لإدارة الكتب، الطلاب، المنتجات، المقالات، العملاء، أو أي نوع آخر من البيانات.
في هذا الدليل سنفترض أننا نبني تطبيقًا بسيطًا لإدارة الكتب، لكن نفس الفكرة يمكن تطبيقها على أي نموذج آخر. سنشرح كل شيء من البداية: إنشاء المشروع، إعداد التطبيق، بناء الموديل، إنشاء الفورم، كتابة الـ views، ضبط الروابط، تجهيز القوالب، ثم تنفيذ عمليات CRUD كاملة بشكل مرتب ومفهوم.
ما هو CRUD ولماذا هو مهم جدًا؟
كلمة CRUD هي اختصار لأربع عمليات أساسية في أي تطبيق يعتمد على قاعدة بيانات:
C = Create
R = Read
U = Update
D = Delete
هذه العمليات هي قلب أغلب التطبيقات التي تراها يوميًا. عندما تضيف منتجًا إلى متجر، أو تنشر منشورًا في موقع، أو تحفظ طالبًا في نظام إدارة مدرسة، فأنت عمليًا تستخدم CRUD.
في Django، تنفيذ CRUD يصبح أسهل بكثير بفضل قوة الإطار نفسه، لأنه يوفر أدوات جاهزة مثل:
ORM للتعامل مع قاعدة البيانات بدون كتابة SQL في أغلب الأحيان.
Forms لتسهيل إدخال البيانات والتحقق منها.
Views لتنفيذ المنطق.
Templates لعرض الصفحات.
Messages لإظهار التنبيهات للمستخدم.
Admin panel لإدارة البيانات بسرعة.
وهذا ما يجعل Django واحدًا من أفضل الخيارات لبناء تطبيقات الويب بسرعة وبنظافة.
ما الذي سنبنيه في هذا المقال؟
سننشئ تطبيقًا بسيطًا لإدارة الكتب، يحتوي على الحقول التالية:
عنوان الكتاب
اسم المؤلف
وصف الكتاب
تاريخ النشر
الحالة: متاح أو غير متاح
وسنقوم بتنفيذ العمليات التالية:
عرض قائمة الكتب
إضافة كتاب جديد
تعديل كتاب موجود
حذف كتاب
عرض تفاصيل كتاب
وبذلك ستحصل على CRUD متكامل من البداية إلى النهاية.
المتطلبات قبل البدء
قبل أن نبدأ، تأكد من توفر الآتي:
Python مثبت على جهازك
Django مثبت
محرر نصوص مثل VS Code
معرفة أساسية جدًا بـ Python
يمكنك تثبيت Django عبر الأمر:
pip install django
وللتأكد من التثبيت:
django-admin --version
إنشاء مشروع Django جديد
لنبدأ بإنشاء المشروع:
django-admin startproject crudproject
cd crudproject
بعد ذلك ننشئ التطبيق الخاص بنا، ولنسمه books:
python manage.py startapp books
الآن أصبح لدينا هيكل المشروع الأساسي.
إعداد التطبيق داخل settings.py
افتح ملف settings.py داخل مجلد المشروع، ثم أضف اسم التطبيق إلى INSTALLED_APPS:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'books',
]
هذا السطر مهم جدًا، لأنه يخبر Django أن التطبيق books جزء من المشروع ويجب أن يتم تحميله.
تصميم الموديل الخاص بالكتب
الخطوة التالية هي إنشاء الموديل الذي يمثل البيانات داخل قاعدة البيانات.
افتح ملف models.py داخل تطبيق books واكتب:
from django.db import models
class Book(models.Model):
STATUS_CHOICES = [
('available', 'Available'),
('unavailable', 'Unavailable'),
]
title = models.CharField(max_length=200)
author = models.CharField(max_length=150)
description = models.TextField()
published_date = models.DateField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='available')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
ماذا يفعل هذا الموديل؟
هذا الموديل يمثل كتابًا واحدًا في قاعدة البيانات. كل سجل في جدول الكتب سيحمل هذه البيانات.
titleلتخزين عنوان الكتابauthorلاسم المؤلفdescriptionلوصف الكتابpublished_dateلتاريخ النشرstatusلحالة الكتابcreated_atوupdated_atلمعرفة وقت الإنشاء والتعديل
الطريقة __str__ مهمة لأنها تجعل الكتاب يظهر بشكل جميل داخل Django admin أو أثناء الطباعة.
إنشاء قاعدة البيانات وترحيل الموديل
بعد إنشاء الموديل، نحتاج إلى إنشاء migration ثم تطبيقه.
python manage.py makemigrations
python manage.py migrate
الأمر الأول ينشئ ملفات الترحيل، والثاني يطبقها على قاعدة البيانات.
تسجيل الموديل داخل admin.py
حتى نتمكن من رؤية الكتب داخل لوحة الإدارة، نحتاج إلى تسجيل الموديل.
افتح ملف admin.py واكتب:
from django.contrib import admin
from .models import Book
admin.site.register(Book)
بعد ذلك أنشئ superuser:
python manage.py createsuperuser
ثم شغّل السيرفر:
python manage.py runserver
ادخل إلى:
http://127.0.0.1:8000/admin/
وسجل الدخول باستخدام بيانات المدير، وستجد نموذج Book ظاهرًا داخل لوحة الإدارة.
لماذا نستخدم ModelForm؟
في CRUD التطبيقات، من الأفضل استخدام ModelForm عندما يكون لدينا نموذج مرتبط مباشرة بالموديل. لأنه يوفر علينا كتابة الكثير من الأكواد اليدوية، كما يقوم بالتحقق من البيانات تلقائيًا.
لننشئ ملف forms.py داخل تطبيق books:
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'author', 'description', 'published_date', 'status']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter book title'}),
'author': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter author name'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'placeholder': 'Enter book description'}),
'published_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'status': forms.Select(attrs={'class': 'form-control'}),
}
لماذا هذا مهم؟
ModelFormيربط الفورم بالموديل مباشرة.fieldsيحدد الحقول التي نريد عرضها.widgetsتساعدنا في تحسين شكل الحقول داخل الصفحة.
بهذا الشكل يصبح الفورم أكثر تنظيمًا وأسهل في الاستخدام.
كتابة Views الخاصة بالـ CRUD
الآن نصل إلى قلب التطبيق. الـ views هي المسؤولة عن تنفيذ المنطق الحقيقي.
افتح views.py واكتب:
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from .models import Book
from .forms import BookForm
def book_list(request):
books = Book.objects.all().order_by('-created_at')
return render(request, 'books/book_list.html', {'books': books})
def book_detail(request, pk):
book = get_object_or_404(Book, pk=pk)
return render(request, 'books/book_detail.html', {'book': book})
def book_create(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Book created successfully.')
return redirect('book_list')
else:
form = BookForm()
return render(request, 'books/book_form.html', {'form': form, 'title': 'Add Book'})
def book_update(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
form = BookForm(request.POST, instance=book)
if form.is_valid():
form.save()
messages.success(request, 'Book updated successfully.')
return redirect('book_list')
else:
form = BookForm(instance=book)
return render(request, 'books/book_form.html', {'form': form, 'title': 'Edit Book'})
def book_delete(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
book.delete()
messages.success(request, 'Book deleted successfully.')
return redirect('book_list')
return render(request, 'books/book_confirm_delete.html', {'book': book})
شرح الـ views ببساطة
1) book_list
هذه الوظيفة تعرض كل الكتب الموجودة في قاعدة البيانات.
نستخدم:
Book.objects.all()
لجلب جميع السجلات، ثم نرتبها حسب وقت الإنشاء من الأحدث إلى الأقدم.
2) book_detail
تعرض تفاصيل كتاب واحد فقط.
نستخدم get_object_or_404 حتى لو لم يوجد الكتاب، سيعرض Django صفحة 404 بدلًا من حدوث خطأ غريب.
3) book_create
تتعامل مع إضافة كتاب جديد.
إذا كانت الطريقة POST فهذا يعني أن المستخدم أرسل البيانات من الفورم.
نقوم بإنشاء BookForm(request.POST)، ثم نتحقق من صحة البيانات باستخدام is_valid()، وبعدها نحفظها.
4) book_update
تشبه الإضافة، لكن الفرق أننا نمرر instance=book حتى يتم تعديل السجل الموجود بدل إنشاء سجل جديد.
5) book_delete
تعرض رسالة تأكيد قبل الحذف، ثم إذا أكد المستخدم العملية يتم حذف الكتاب.
ضبط الروابط داخل urls.py للتطبيق
الآن نحتاج إلى ربط كل View بمسار URL مناسب.
أنشئ ملف urls.py داخل تطبيق books إذا لم يكن موجودًا، ثم ضع فيه:
from django.urls import path
from . import views
urlpatterns = [
path('', views.book_list, name='book_list'),
path('book/<int:pk>/', views.book_detail, name='book_detail'),
path('book/add/', views.book_create, name='book_create'),
path('book/<int:pk>/edit/', views.book_update, name='book_update'),
path('book/<int:pk>/delete/', views.book_delete, name='book_delete'),
]
ربط urls الخاصة بالتطبيق مع المشروع الرئيسي
افتح ملف urls.py الخاص بالمشروع الرئيسي واكتب:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('books.urls')),
]
الآن أي رابط داخل books.urls سيصبح جزءًا من الموقع الرئيسي.
إنشاء مجلد templates بشكل منظم
من الأفضل تنظيم ملفات القوالب بهذا الشكل:
books/
templates/
books/
base.html
book_list.html
book_detail.html
book_form.html
book_confirm_delete.html
هذا التنظيم يساعد Django على العثور على الملفات بسهولة ويجعل المشروع مرتبًا.
إنشاء القالب الأساسي base.html
ملف base.html سيكون الأساس الذي ترث منه باقي الصفحات.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Django CRUD{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark bg-dark navbar-expand-lg">
<div class="container">
<a class="navbar-brand" href="{% url 'book_list' %}">Book Manager</a>
</div>
</nav>
<div class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
صفحة عرض قائمة الكتب book_list.html
{% extends 'books/base.html' %}
{% block title %}Book List{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3">Books</h1>
<a href="{% url 'book_create' %}" class="btn btn-primary">Add New Book</a>
</div>
<div class="card shadow-sm">
<div class="card-body">
{% if books %}
<div class="table-responsive">
<table class="table table-striped align-middle">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Status</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td>
{% if book.status == 'available' %}
<span class="badge bg-success">Available</span>
{% else %}
<span class="badge bg-secondary">Unavailable</span>
{% endif %}
</td>
<td>{{ book.created_at|date:"Y-m-d" }}</td>
<td>
<a href="{% url 'book_detail' book.pk %}" class="btn btn-sm btn-info">View</a>
<a href="{% url 'book_update' book.pk %}" class="btn btn-sm btn-warning">Edit</a>
<a href="{% url 'book_delete' book.pk %}" class="btn btn-sm btn-danger">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">No books found. Start by adding your first book.</p>
{% endif %}
</div>
</div>
{% endblock %}
صفحة إضافة وتعديل الكتب book_form.html
هذه الصفحة سنستخدمها لكل من الإضافة والتعديل.
{% extends 'books/base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-body">
<h1 class="h4 mb-4">{{ title }}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Save</button>
<a href="{% url 'book_list' %}" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
صفحة تفاصيل الكتاب book_detail.html
{% extends 'books/base.html' %}
{% block title %}Book Detail{% endblock %}
{% block content %}
<div class="card shadow-sm">
<div class="card-body">
<h1 class="h3">{{ book.title }}</h1>
<p><strong>Author:</strong> {{ book.author }}</p>
<p><strong>Published Date:</strong> {{ book.published_date }}</p>
<p><strong>Status:</strong> {{ book.status }}</p>
<p><strong>Description:</strong></p>
<p>{{ book.description }}</p>
<a href="{% url 'book_update' book.pk %}" class="btn btn-warning">Edit</a>
<a href="{% url 'book_delete' book.pk %}" class="btn btn-danger">Delete</a>
<a href="{% url 'book_list' %}" class="btn btn-secondary">Back</a>
</div>
</div>
{% endblock %}
صفحة تأكيد الحذف book_confirm_delete.html
{% extends 'books/base.html' %}
{% block title %}Delete Book{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-sm border-danger">
<div class="card-body">
<h1 class="h4 text-danger">Delete Book</h1>
<p>Are you sure you want to delete <strong>{{ book.title }}</strong>?</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Delete</button>
<a href="{% url 'book_list' %}" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
كيف تعمل عملية Create داخل Django؟
عندما يذهب المستخدم إلى صفحة الإضافة، نعرض له فورمًا فارغًا.
بعد إدخال البيانات والضغط على Save، يتم إرسال البيانات باستخدام POST.
في الـ view نقوم بقراءة البيانات من request.POST، ثم نمررها إلى BookForm.
إذا كانت البيانات صحيحة، نحفظها في قاعدة البيانات.
مثال عملي:
def book_create(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
form.save()
return redirect('book_list')
else:
form = BookForm()
return render(request, 'books/book_form.html', {'form': form})
هذا هو الجوهر الحقيقي لعملية الإنشاء.
كيف تعمل عملية Read؟
القراءة هي عرض البيانات.
لدينا نوعان:
قراءة كل البيانات
books = Book.objects.all()
قراءة سجل واحد
book = get_object_or_404(Book, pk=pk)
الأولى تعرض جميع الكتب، والثانية تعرض كتابًا واحدًا فقط.
كيف تعمل عملية Update؟
عندما نريد تعديل سجل موجود، علينا أولًا جلبه من قاعدة البيانات ثم تمريره إلى الفورم.
def book_update(request, pk):
book = get_object_or_404(Book, pk=pk)
form = BookForm(request.POST or None, instance=book)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('book_list')
return render(request, 'books/book_form.html', {'form': form})
هنا instance=book هي الفكرة الأساسية، لأنها تقول Django: هذا الفورم مرتبط بكتاب موجود بالفعل، وليس بكتاب جديد.
كيف تعمل عملية Delete؟
الحذف يجب أن يتم بحذر، لذلك من الأفضل عرض صفحة تأكيد قبل تنفيذ الحذف.
def book_delete(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
book.delete()
return redirect('book_list')
return render(request, 'books/book_confirm_delete.html', {'book': book})
بهذا الشكل لن يتم حذف البيانات بمجرد فتح الرابط، بل بعد تأكيد المستخدم.
إضافة تحسينات على شكل الفورم
بدل استخدام form.as_p فقط، يمكنك عرض الحقول يدويًا لتتحكم في التصميم بشكل أفضل.
مثال:
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">Title</label>
{{ form.title }}
{% if form.title.errors %}
<div class="text-danger">{{ form.title.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
<label class="form-label">Author</label>
{{ form.author }}
</div>
<div class="mb-3">
<label class="form-label">Description</label>
{{ form.description }}
</div>
<div class="mb-3">
<label class="form-label">Published Date</label>
{{ form.published_date }}
</div>
<div class="mb-3">
<label class="form-label">Status</label>
{{ form.status }}
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
هذا الأسلوب يعطيك مرونة أكبر في التصميم.
إضافة CSS بسيط لتحسين الواجهة
يمكنك إنشاء ملف CSS خاص بك داخل static، أو الاكتفاء بـ Bootstrap.
لكن لو أردت إضافة لمسة بسيطة، يمكنك استخدام:
body {
background-color: #f8f9fa;
}
.card {
border-radius: 12px;
}
table th, table td {
vertical-align: middle;
}
هذا سيجعل الشكل أكثر هدوءًا وراحة للعين.
التعامل مع رسائل النجاح والأخطاء
من الأشياء الجميلة في Django أنك تستطيع إظهار رسائل للمستخدم بعد كل عملية.
مثلاً:
messages.success(request, 'Book created successfully.')
messages.success(request, 'Book updated successfully.')
messages.success(request, 'Book deleted successfully.')
وهذه الرسائل تظهر في base.html داخل جزء messages.
المستخدم يحب أن يعرف ماذا حدث بعد الضغط على زر ما.
هذه التفاصيل الصغيرة تعطي شعورًا بأن التطبيق حيّ ومهني.
إضافة التحقق من صحة البيانات Validation
Django يقوم بالتحقق من صحة البيانات تلقائيًا، لكن يمكنك أيضًا إضافة تحقق مخصص.
مثال داخل forms.py:
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'author', 'description', 'published_date', 'status']
def clean_title(self):
title = self.cleaned_data.get('title')
if len(title) < 3:
raise forms.ValidationError("Title must be at least 3 characters long.")
return title
هذا مفيد لمنع إدخال بيانات غير منطقية.
إضافة بحث بسيط داخل قائمة الكتب
من الإضافات المفيدة جدًا أن تسمح للمستخدم بالبحث عن كتاب.
يمكنك تعديل book_list هكذا:
def book_list(request):
query = request.GET.get('q')
books = Book.objects.all().order_by('-created_at')
if query:
books = books.filter(title__icontains=query)
return render(request, 'books/book_list.html', {'books': books, 'query': query})
ثم في القالب:
<form method="get" class="mb-3">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search by title" value="{{ query|default:'' }}">
<button type="submit" class="btn btn-outline-primary">Search</button>
</div>
</form>
وهذا يجعل التطبيق أكثر فائدة وواقعية.
إضافة pagination عند كثرة الكتب
إذا زادت البيانات، من الأفضل تقسيمها إلى صفحات.
from django.core.paginator import Paginator
def book_list(request):
books = Book.objects.all().order_by('-created_at')
paginator = Paginator(books, 5)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'books/book_list.html', {'page_obj': page_obj})
وفي القالب:
{% for book in page_obj %}
<p>{{ book.title }}</p>
{% endfor %}
<div class="mt-3">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}" class="btn btn-sm btn-secondary">Previous</a>
{% endif %}
<span class="mx-2">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}" class="btn btn-sm btn-secondary">Next</a>
{% endif %}
</div>
استخدام Class-Based Views بدل Function-Based Views
ما شرحناه حتى الآن يعتمد على Function-Based Views، وهي ممتازة للتعلم والفهم.
لكن Django أيضًا يوفر Class-Based Views، وهي مفيدة لتقليل التكرار في الأكواد.
مثال:
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Book
from .forms import BookForm
class BookListView(ListView):
model = Book
template_name = 'books/book_list.html'
context_object_name = 'books'
ordering = ['-created_at']
class BookDetailView(DetailView):
model = Book
template_name = 'books/book_detail.html'
context_object_name = 'book'
class BookCreateView(CreateView):
model = Book
form_class = BookForm
template_name = 'books/book_form.html'
success_url = reverse_lazy('book_list')
class BookUpdateView(UpdateView):
model = Book
form_class = BookForm
template_name = 'books/book_form.html'
success_url = reverse_lazy('book_list')
class BookDeleteView(DeleteView):
model = Book
template_name = 'books/book_confirm_delete.html'
success_url = reverse_lazy('book_list')
هذه الطريقة أنيقة جدًا، لكنها قد تكون أصعب قليلًا على المبتدئين مقارنة بالأسلوب الوظيفي.
أيهما أفضل: Function-Based أم Class-Based؟
لا يوجد جواب واحد صحيح للجميع.
Function-Based Views
مناسبة إذا كنت:
مبتدئًا
تريد فهم المنطق خطوة بخطوة
تفضل الوضوح والبساطة
Class-Based Views
مناسبة إذا كنت:
تعمل على مشروع كبير
تريد تقليل الكود المتكرر
تحب التنظيم والاختصار
في هذا المقال ركزنا على Function-Based Views لأنها الأسهل للفهم، ثم أضفنا نموذجًا سريعًا للـ Class-Based Views حتى تكون الصورة كاملة.
مثال كامل عملي لتطبيق CRUD بسيط
لنجمع الفكرة في مشهد واحد:
المستخدم يفتح صفحة قائمة الكتب.
يرى الكتب الموجودة داخل جدول.
يضغط على زر Add New Book.
يظهر له الفورم.
يكتب البيانات ثم يحفظها.
تنتقل البيانات إلى قاعدة البيانات.
تظهر رسالة نجاح.
يعود إلى صفحة القائمة ويشاهد الكتاب الجديد.
يمكنه بعد ذلك فتح صفحة التفاصيل.
أو تعديل الكتاب.
أو حذفه بعد التأكيد.
هذه الدورة الكاملة هي ما يسمى CRUD Workflow.
أخطاء شائعة عند بناء CRUD في Django
عند العمل على CRUD، قد تواجه بعض الأخطاء الشائعة. من المهم معرفتها حتى لا تضيع وقتك.
1) نسيان csrf_token
إذا نسيت:
{% csrf_token %}
فإن Django سيرفض إرسال الفورم.
2) نسيان include للتطبيق في urls.py
إذا لم تضف:
path('', include('books.urls'))
فلن تعمل الروابط الخاصة بالتطبيق.
3) نسيان إضافة التطبيق في INSTALLED_APPS
إذا لم تضف books داخل الإعدادات، فقد لا يتعرف Django على الموديل أو القوالب بشكل صحيح.
4) نسيان migrations
بعد تعديل models.py يجب دائمًا تنفيذ:
python manage.py makemigrations
python manage.py migrate
5) عدم استخدام instance في التعديل
إذا نسيت:
instance=book
فإن Django قد ينشئ سجلًا جديدًا بدل تعديل السجل الحالي.
أفكار لتطوير المشروع بعد الانتهاء
بعد أن تنتهي من CRUD الأساسي، يمكنك تطوير المشروع بسهولة بإضافة:
رفع صورة للكتاب
تصنيف الكتب حسب النوع
ربط الكتاب بالمؤلف بشكل منفصل
إضافة نظام تسجيل دخول
تحديد من يمكنه الإضافة أو التعديل أو الحذف
إضافة API باستخدام Django REST Framework
تحسين الواجهة باستخدام Tailwind أو Bootstrap 5
دعم البحث المتقدم والفلاتر
تصدير البيانات إلى Excel أو PDF
هذه الإضافات تجعل التطبيق أقرب إلى مشروع حقيقي قابل للنشر.
نسخة محسنة من الموديل مع صورة اختيارية
إذا أردت تطوير التطبيق أكثر، يمكنك إضافة صورة للكتاب:
from django.db import models
class Book(models.Model):
STATUS_CHOICES = [
('available', 'Available'),
('unavailable', 'Unavailable'),
]
title = models.CharField(max_length=200)
author = models.CharField(max_length=150)
description = models.TextField()
published_date = models.DateField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='available')
cover_image = models.ImageField(upload_to='book_covers/', blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
ثم لا تنسَ إعداد media files في Django، وهو موضوع مهم جدًا إذا كنت ستتعامل مع الصور.
لماذا هذا المشروع مهم جدًا للمبتدئين؟
لأن CRUD هو الأساس.
إذا أتقنت CRUD، فأنت قطعت نصف الطريق تقريبًا في فهم Django.
هذا النوع من المشاريع يعلمك:
كيفية ربط الموديل بقاعدة البيانات
كيفية التعامل مع الفورمات
كيفية عرض البيانات داخل القوالب
كيفية استخدام الروابط
كيفية تنظيم الملفات
كيفية التفكير في بناء التطبيقات بشكل عملي
ومن الجميل أن نفس البنية تتكرر في عدد هائل من المشاريع الحقيقية، لذلك ما تتعلمه هنا لن يكون نظريًا فقط، بل سيفيدك في العمل الفعلي.
نصيحة من القلب أثناء التعلم
لا تحاول أن تحفظ كل شيء مرة واحدة.
ابدأ بفهم الفكرة، ثم اكتب الكود بيدك، ثم أضف تعديلًا صغيرًا، ثم جرّب مرة أخرى.
البرمجة ليست سباقًا، بل رحلة.
وفي كل مرة تعيد فيها بناء CRUD من جديد، ستلاحظ أنك أصبحت أسرع، وأوضح، وأكثر ثقة بنفسك. وهذا شعور جميل جدًا، خاصة عندما ترى الصفحة تعمل أمامك وتستجيب كما خططت لها.
إذا واجهتك مشكلة، لا تعتبرها فشلًا.
في الحقيقة، الأخطاء هي أفضل معلم في عالم البرمجة.
كل رسالة خطأ تقرأها وتفهمها تجعلك أقوى.
الخلاصة
بناء Django CRUD Application خطوة بخطوة ليس أمرًا صعبًا كما يبدو في البداية.
عندما تفكك المشروع إلى أجزاء صغيرة، يصبح كل شيء أوضح بكثير:
أنشأنا مشروع Django
أضفنا التطبيق
صنعنا الموديل
نفذنا migrations
أنشأنا ModelForm
كتبنا views
ضبطنا urls
أنشأنا templates
نفذنا Create و Read و Update و Delete
أضفنا رسائل وتحسينات
وتحدثنا عن التطوير المستقبلي
هذا هو الأساس الذي يمكنك البناء عليه في أي مشروع Django آخر.
وإذا أردت أن تتقدم أكثر، يمكنك تحويل هذا المشروع إلى نظام كامل لإدارة مكتبة أو متجر أو إدارة محتوى أو لوحة تحكم داخلية.
الجميل في Django أنه منظم، عملي، ويمنحك إحساسًا بأنك تسيطر على المشروع من البداية إلى النهاية.
ومع كل مشروع CRUD جديد، ستشعر أن الأمور أصبحت أسهل وأقرب إلى ذهنك.