Views & Routes
Implementasi CRUD views dan konfigurasi routing untuk API Mahasiswa
Membuat Views untuk CRUD Mahasiswa
Views adalah fungsi yang menangani request dan mengembalikan response. Kita akan membuat views untuk operasi CRUD (Create, Read, Update, Delete) pada data Mahasiswa.
Buat File Views Mahasiswa
Buat file baru pyramid_mahasiswa/views/mahasiswa.py:
import datetime
from pyramid.view import view_config
from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
HTTPBadRequest,
)
from ..models import Mahasiswa
@view_config(route_name='mahasiswa_list', renderer='json')
def mahasiswa_list(request):
"""View untuk menampilkan daftar mahasiswa"""
dbsession = request.dbsession
mahasiswas = dbsession.query(Mahasiswa).all()
return {'mahasiswas': [m.to_dict() for m in mahasiswas]}
@view_config(route_name='mahasiswa_detail', renderer='json')
def mahasiswa_detail(request):
"""View untuk melihat detail satu mahasiswa"""
dbsession = request.dbsession
mahasiswa_id = request.matchdict['id']
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=mahasiswa_id).first()
if mahasiswa is None:
return HTTPNotFound(json_body={'error': 'Mahasiswa tidak ditemukan'})
return {'mahasiswa': mahasiswa.to_dict()}
@view_config(route_name='mahasiswa_add', request_method='POST', renderer='json')
def mahasiswa_add(request):
"""View untuk menambahkan mahasiswa baru"""
try:
# Ambil data dari request JSON
json_data = request.json_body
# Validasi data minimal
required_fields = ['nim', 'nama', 'jurusan']
for field in required_fields:
if field not in json_data:
return HTTPBadRequest(
json_body={'error': f'Field {field} wajib diisi'}
)
# Parse tanggal lahir jika ada
tanggal_lahir = None
if 'tanggal_lahir' in json_data and json_data['tanggal_lahir']:
try:
tanggal_lahir = datetime.datetime.fromisoformat(
json_data['tanggal_lahir']
).date()
except ValueError:
return HTTPBadRequest(
json_body={
'error': 'Format tanggal lahir tidak valid. Gunakan YYYY-MM-DD'
}
)
# Buat objek Mahasiswa baru
mahasiswa = Mahasiswa(
nim=json_data['nim'],
nama=json_data['nama'],
jurusan=json_data['jurusan'],
tanggal_lahir=tanggal_lahir,
alamat=json_data.get('alamat')
)
# Simpan ke database
dbsession = request.dbsession
dbsession.add(mahasiswa)
dbsession.flush() # Untuk mendapatkan ID yang baru dibuat
return {'success': True, 'mahasiswa': mahasiswa.to_dict()}
except Exception as e:
return HTTPBadRequest(json_body={'error': str(e)})
@view_config(route_name='mahasiswa_update', request_method='PUT', renderer='json')
def mahasiswa_update(request):
"""View untuk mengupdate data mahasiswa"""
dbsession = request.dbsession
mahasiswa_id = request.matchdict['id']
# Cari mahasiswa yang akan diupdate
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=mahasiswa_id).first()
if mahasiswa is None:
return HTTPNotFound(json_body={'error': 'Mahasiswa tidak ditemukan'})
try:
# Ambil data dari request JSON
json_data = request.json_body
# Update atribut yang ada di request
if 'nim' in json_data:
mahasiswa.nim = json_data['nim']
if 'nama' in json_data:
mahasiswa.nama = json_data['nama']
if 'jurusan' in json_data:
mahasiswa.jurusan = json_data['jurusan']
if 'alamat' in json_data:
mahasiswa.alamat = json_data['alamat']
# Parse tanggal lahir jika ada
if 'tanggal_lahir' in json_data:
if json_data['tanggal_lahir']:
try:
mahasiswa.tanggal_lahir = datetime.datetime.fromisoformat(
json_data['tanggal_lahir']
).date()
except ValueError:
return HTTPBadRequest(
json_body={
'error': 'Format tanggal lahir tidak valid. Gunakan YYYY-MM-DD'
}
)
else:
mahasiswa.tanggal_lahir = None
return {'success': True, 'mahasiswa': mahasiswa.to_dict()}
except Exception as e:
return HTTPBadRequest(json_body={'error': str(e)})
@view_config(route_name='mahasiswa_delete', request_method='DELETE', renderer='json')
def mahasiswa_delete(request):
"""View untuk menghapus data mahasiswa"""
dbsession = request.dbsession
mahasiswa_id = request.matchdict['id']
# Cari mahasiswa yang akan dihapus
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=mahasiswa_id).first()
if mahasiswa is None:
return HTTPNotFound(json_body={'error': 'Mahasiswa tidak ditemukan'})
# Hapus dari database
dbsession.delete(mahasiswa)
return {
'success': True,
'message': f'Mahasiswa dengan id {mahasiswa_id} berhasil dihapus'
}Renderer JSON
Kita menggunakan renderer='json' pada decorator @view_config untuk mengonversi return value dari function view menjadi JSON response secara otomatis. Ini berguna untuk membuat API web yang mengembalikan data dalam format JSON.
Penjelasan View Functions
Mari kita pahami setiap view function yang telah dibuat:
1. mahasiswa_list
@view_config(route_name='mahasiswa_list', renderer='json')
def mahasiswa_list(request):
dbsession = request.dbsession
mahasiswas = dbsession.query(Mahasiswa).all()
return {'mahasiswas': [m.to_dict() for m in mahasiswas]}View ini:
- Mengambil semua data mahasiswa dari database
- Mengkonversi setiap objek Mahasiswa ke dictionary dengan
to_dict() - Mengembalikan list dictionary dalam format JSON
2. mahasiswa_detail
@view_config(route_name='mahasiswa_detail', renderer='json')
def mahasiswa_detail(request):
mahasiswa_id = request.matchdict['id']
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=mahasiswa_id).first()View ini:
- Mengambil ID dari URL parameter dengan
request.matchdict['id'] - Mencari mahasiswa dengan ID tersebut
- Mengembalikan 404 jika tidak ditemukan
3. mahasiswa_add
@view_config(route_name='mahasiswa_add', request_method='POST', renderer='json')
def mahasiswa_add(request):
json_data = request.json_bodyView ini:
- Menerima data JSON dari request body
- Melakukan validasi field yang wajib diisi
- Membuat objek Mahasiswa baru dan menyimpannya ke database
4. mahasiswa_update & mahasiswa_delete
View-view ini mengikuti pola yang sama dengan update dan delete operasi pada database.
Membuat Routes dan Update routes.py
Sekarang kita perlu mendefinisikan routes untuk endpoints CRUD Mahasiswa.
Update File routes.py
Edit file pyramid_mahasiswa/routes.py:
def includeme(config):
"""Add routes to the config."""
config.add_static_view('static', 'static', cache_max_age=3600)
# Default route
config.add_route('home', '/')
# Mahasiswa routes
config.add_route('mahasiswa_list', '/api/mahasiswa', request_method='GET')
config.add_route('mahasiswa_detail', '/api/mahasiswa/{id}', request_method='GET')
config.add_route('mahasiswa_add', '/api/mahasiswa', request_method='POST')
config.add_route('mahasiswa_update', '/api/mahasiswa/{id}', request_method='PUT')
config.add_route('mahasiswa_delete', '/api/mahasiswa/{id}', request_method='DELETE')Request Method Parameter
Perhatikan penambahan parameter request_method pada setiap route. Ini sangat penting untuk membedakan endpoint yang memiliki URL sama tetapi method berbeda. Tanpa parameter ini, Pyramid mungkin akan selalu mengarahkan request ke satu view function saja, yang menyebabkan endpoint POST/PUT/DELETE tidak berfungsi.
RESTful API Pattern
API yang kita buat mengikuti pola RESTful dengan mapping sebagai berikut:
| HTTP Method | URL Pattern | View Function | Deskripsi |
|---|---|---|---|
| GET | /api/mahasiswa | mahasiswa_list | Mendapatkan semua mahasiswa |
| GET | /api/mahasiswa/{id} | mahasiswa_detail | Mendapatkan detail satu mahasiswa |
| POST | /api/mahasiswa | mahasiswa_add | Menambahkan mahasiswa baru |
| PUT | /api/mahasiswa/{id} | mahasiswa_update | Mengupdate data mahasiswa |
| DELETE | /api/mahasiswa/{id} | mahasiswa_delete | Menghapus data mahasiswa |
RESTful API Best Practices
RESTful API menggunakan HTTP methods untuk menentukan jenis operasi:
- GET: Mengambil data (read-only)
- POST: Membuat data baru
- PUT: Mengupdate data yang ada
- DELETE: Menghapus data
Scan Views Module
Agar views yang telah kita buat dapat digunakan, kita perlu memastikan Pyramid melakukan scan pada module views.
Edit file pyramid_mahasiswa/__init__.py dan pastikan ada kode berikut:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
(...)
# Update Scan views module
config.scan('.views')
return config.make_wsgi_app()Baris config.scan('.views') membuat Pyramid mencari semua decorator @view_config di dalam module views dan mendaftarkannya.
Config Scan
Pyramid menggunakan config scan untuk menemukan dan mendaftarkan views, routes, dan komponen lainnya secara otomatis. Tanpa config.scan(), decorator @view_config tidak akan berfungsi.
SQLAlchemy Query Patterns
Berikut beberapa pattern query SQLAlchemy yang sering digunakan:
# Mendapatkan semua data
mahasiswas = dbsession.query(Mahasiswa).all()
# Dengan ordering
mahasiswas = dbsession.query(Mahasiswa).order_by(Mahasiswa.nama).all()
# Dengan limit
mahasiswas = dbsession.query(Mahasiswa).limit(10).all()# Filter by single field
mahasiswa = dbsession.query(Mahasiswa).filter_by(nim='12345').first()
# Filter dengan kondisi
mahasiswas = dbsession.query(Mahasiswa).filter(
Mahasiswa.jurusan == 'Teknik Informatika'
).all()
# Multiple conditions
mahasiswas = dbsession.query(Mahasiswa).filter(
Mahasiswa.jurusan == 'Teknik Informatika',
Mahasiswa.nama.like('%budi%')
).all()# Get first result or None
mahasiswa = dbsession.query(Mahasiswa).first()
# Get one result (raises if not found or multiple found)
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=1).one()
# Get one or None
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=1).one_or_none()# Add new object
mahasiswa = Mahasiswa(nim='12345', nama='Budi')
dbsession.add(mahasiswa)
# Delete object
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=1).first()
dbsession.delete(mahasiswa)
# Update object (directly modify attributes)
mahasiswa = dbsession.query(Mahasiswa).filter_by(id=1).first()
mahasiswa.nama = 'Budi Updated'
# No need to call update, SQLAlchemy tracks changesMenjalankan Aplikasi
Setelah semua komponen diimplementasikan, jalankan aplikasi:
# Pastikan virtual environment aktif
# Di root proyek
pserve development.ini --reloadFlag --reload akan menyebabkan server restart secara otomatis saat ada perubahan kode. Server akan berjalan pada port 6543 (http://localhost:6543).
Server Ready
Jika server berhasil dijalankan, Kalian akan melihat output:
Starting server in PID xxxxx.
Serving on http://localhost:6543Langkah Selanjutnya
Setelah views dan routes selesai dibuat, kita akan melanjutkan ke pengujian API dan mengerjakan tugas praktikum pada bagian selanjutnya.
