장고 (Django)를 사용한 CRUD 예제 만들기
Django CRUD
Django와 Mysql을 사용해서 CRUD를 작성 해 보자
- github -
Django 프레임워크를 사용해서 CRUD 를 작업 해 보려고 한다.
CRUD with Django
Project Structure
.
├── config
│ ├── __init__.py
│ ├── __pycache__
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── notebook
│ ├── __init__.py
│ ├── __pycache__
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── static
│ ├── css
│ └── js
└── templates
├── index.html
├── note
└── partial
Template 세팅
장고에서 Template을 사용하기 위해서는, Template의 경로를 적어주어야 한다.
Template의 경로는 Settings.py에서 정해주고 있다. Templates 설정의 'DIRS'를 아래와 같이 수정 해 준다
# settings.py
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
...
수정 해 준 'DIRS'에 맞게 templates 폴더를 생성 해 준다. 나의 경우에는 config와 동일한 레벨인 프로젝트 루트 폴더에 생성이 된다.
.
├── config --> settings.py가 있는 폴더
├── manage.py
└── templates --> 생성 해줄 templates 폴더
notebook 앱을 만들어주기
장고 세팅을 했으니까, CRUD를 할 앱을 만들어 주도록 한다.
$ python manage.py startapp notebook
아래는 생성된 노트북 모듈이다. 주로, models, urls, views에서 작업을 하려고 한다.
notebook/
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── **models.py**
├── tests.py
├── **urls.py**
└── **views.py**
Model 세팅
이제 CRUD를 위한 데이터베이스 모델을 생성 해 주어야 한다. 어떻게 저장하고, 어떻게 불러오고에 대한 기본적인 데이터를 다룰 수 있는 Model이다.
DB 연동
우선 DB를 사용하기 위해서는 DB를 연동해야 한다. 내가 연결하는 DB의 정보는 Mysql을 사용하고, localhost의 3306포트를 사용한다. DB 연동을 위해서 settings.py의 DATABASE영역을 수정 해 준다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_test',
'USER': 'wool',
'PASSWORD': 'qwerqwer123',
'HOST': '127.0.0.1',
'PORT': '3306',
'OPTIONS': {
"init_command": "SET foreign_key_checks = 0;"
}
}
}
ORM Model 생성 or 긁어오기
Django의 ORM Model은 두가지 방법으로 세팅 할 수 있다. 데이터베이스는 models.py에 넣어 준다
-
inpectdb - django에서 제공하는 툴로, 현재 연되어있는 데이터베이스 정보를 기반으로 클래스를 만들어줌
$ python3 manage.py inspectdb > notebbok/models.py
-
DB모델 만들기 - 사용자가 직접 만들고 migration을 함
#notebbok/models.py from django.db import models from django.contrib.auth.models import User class Notebook(models.Model): note_data = models.TextField() draft_user = models.ForeignKey(User, models.DO_NOTHING, db_column='draft_user') created_at = models.DateTimeField(blank=True, null=True) updated_at = models.DateTimeField(blank=True, null=True) class Meta: db_table = 'notebook' unique_together = (('id', 'draft_user'),)
created_at과 updated_at 에 'auto_now' 혹은 'auto_now_add'를 사용하면 데이터를 수동으로 넣지 않아도 현재 시점의 시간 데이터를 넣어준다.
URL & View 연동
URL (notebook/urls.py)
#notebbok/urls.py
from django.urls import path,include
from . import views
urlpatterns = [
path('', views.note_list, name='notepage'),
path('create/', views.note_create ,name='create'),
path('<int:pk>/update', views.note_update, name='update'),
path('<int:pk>/delete', views.note_delete, name='delete'),
]
VIEW (notebook/views.py)
# notebook/views.py
from django.shortcuts import render,redirect
from .models import Notebook
import datetime
# Create your views here.
def note_list(request):
notes = Notebook.objects.all().order_by('-created_at')
return render(request, 'note/note_list.html', {'notes':notes})
def note_create(request):
if request.method == 'POST':
note_data = request.POST
login_user = request.user
data = Notebook(draft_user=login_user,note_data=note_data.get('note_data'),created_at=datetime.datetime.now(),updated_at=datetime.datetime.now())
data.save()
return redirect('notepage')
def note_update(request,pk):
if request.method == 'POST':
note_data = request.POST
login_user = request.user
my_note = Notebook.objects.get(id=pk)
if my_note.draft_user != login_user:
return redirect('notepage')
else:
my_note.updated_at = datetime.datetime.now()
my_note.note_data = note_data.get('note_data')
my_note.save()
return redirect('notepage')
else:
my_note = Notebook.objects.get(id=pk)
return render(request,'note/note_update.html',{'note':my_note})
def note_delete(request,pk):
note = Notebook.objects.get(id=pk)
note.delete()
return redirect('notepage')
HTML
생성할 templates의 구조는 아래와 같다
└── templates
├── index.html
├── note
│ ├── note_create.html
│ ├── note_list.html
│ └── note_update.html
└── partial
└── nav.html
-
index.html
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> <title>{% block title %}{% endblock %} Django-CRUD</title> </head> <body> {% include 'partial/nav.html' %} <div class="container"> {% include 'note/note_create.html' %} {% block content %} {% endblock %} </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> </body> </html>
-
partial/nav.html
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <a class="navbar-brand" href="#">Django CRUD</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a> </li> <li class="nav-item"> <a class="nav-link" href="/note">NoteFeed</a> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Dropdown </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="#">Another action</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="#">Something else here</a> </div> </li> <li class="nav-item"> <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a> </li> </ul> <form class="form-inline my-2 my-lg-0"> <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"> <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button> </form> </div> </nav> <br> <br>
-
note
-
note_create.html
<div class="card"> <div class="card-body"> <form action="/note/create/" method="post"> {% csrf_token %} <div class="form-group"> <label for="note_data_help">지금 무슨 생각을 하고있나요 ?? </label> <input type="text" class="form-control" id="note_data" name ="note_data" aria-describedby="note_data_help"> <small id="note_data_help" class="form-text text-muted">지금 무슨 생각을 하고있는지 얘기 해 주세요!</small> </div> <button type="submit" class="btn btn-sm btn-primary">작성하기</button> </form> </div> </div>
-
note_list.html
{% extends 'index.html' %} {% block content %} <hr> {% if notes %} {% for note in notes %} <div class="card mb-2"> <div class="card-header"> {{ note.draft_user }} <a href="{% url 'update' note.id %}">수정</a> <a href="{% url 'delete' note.id %}">삭제</a> </div> <div class="card-body"> <blockquote class="blockquote mb-0"> {{ note.note_data }} <footer class="blockquote-footer">last updated at {{ note.updated_at | timesince }} ago by {{ note.draft_user }}</footer> </blockquote> </div> </div> {% endfor %} {% endif %} {% endblock %}
-
note_update.html
{% extends 'index.html' %} {% block content %} <div class="card"> <div class="card-body"> <form action="/note/{{ note.id }}/update" method="post"> {% csrf_token %} <div class="form-group"> <input type="text" class="form-control" id="note_data" name ="note_data" aria-describedby="note_data_help" value={{ note.note_data }}> <input type="text" style="display: none" value={{ user.username }}/> <small id="note_data_help" class="form-text text-muted">지금 무슨 생각을 하고있는지 얘기 해 주세요!</small> </div> <button type="submit" class="btn btn-sm btn-primary">수정</button> </form> </div> </div> {% endblock %}
-