Old Branch

장고 (Django)를 사용한 CRUD 예제 만들기

woolbro 2020. 7. 22. 13:05
반응형

Django CRUD

Django와 Mysql을 사용해서 CRUD를 작성 해 보자

  • github -

paullee714/django-projects

 

paullee714/django-projects

Create Django Projects! Contribute to paullee714/django-projects development by creating an account on GitHub.

github.com

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에 넣어 준다

  1. inpectdb - django에서 제공하는 툴로, 현재 연되어있는 데이터베이스 정보를 기반으로 클래스를 만들어줌

     $ python3 manage.py inspectdb > notebbok/models.py
  2. 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 %}