[Django] CRUD 알아보기

56125 단어 TILdjagoTIL

🏷 Django 파일 살펴보기

  • manage.py

    • 장고 앱을 관리하기 위한 각종 명령어를 내장하고 있는 스크립트 파일
      • 이 파일을 실행함으로써 장고가 제공하는 각종 명령어 실행 가능
  • firstdjango(프로젝트 폴더)

    • settings.py : 프로젝트 전반에 걸친 각종 설정을 위한 파일
    • urls.py : 이 프로젝트를 실행시킬 때 어떤 웹 앱의 페이지를 어떤 주소에 연결 시킬지 정의하는 파일
    • wsgi.py : 웹 사이트 실행 프로세스와 관견하여 사용되는 파일
  • p1(웹 앱 폴더)

    • admin.py : 장과 관리자 웹 구성을 위한 파일
    • apps.py : 장고 웹 앱에 대한 설정을 위한 파일
    • models.py : db에 정의하기 위한 모델들을 정의해두는 코드를 위한 파일
    • views.py : 웹 페이지나 웹 요청등을 처리하는 코드를 작성하는 파일

    🏷 CRUD 실습

CRUD
➡️ 대부분의 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능
➡️ Create(생성), Read(읽기), Update(갱신), Delete(삭제)

음식점 리뷰 사이트를 만들어 보자!


1️⃣ Create(생성)

def create(request):
    if request.method == 'POST':
        form = RestaurantForm(request.POST)
        if form.is_valid():
            new_item = form.save()
        return HttpResponseRedirect('/p1/list/')
    form = RestaurantForm()
    return render(request, 'p1/create.html', {'form': form})
  • form : 데이터 유효성 검사에 용이하다.


2️⃣ Read(읽기)

def list(request):
    restaurants = Restaurant.objects.all()\
        .annotate(reviews_count=Count('review'))\
        .annotate(average_point=Avg('review__point'))
    paginator = Paginator(restaurants, 5)
    page = request.GET.get('page')
    items = paginator.get_page(page)

    context = {
        'restaurants': items
    }
    return render(request, 'p1/list.html', context)
.
.
.
def detail(request, id):
    if id is not None:
        item = get_object_or_404(Restaurant, pk=id)
        reviews = Review.objects.filter(restaurant=item).all()
        return render(request, 'p1/detail.html', {'item': item, 'reviews': reviews})
    return HttpResponseRedirect('/p1/list/')
  • get_object_or_404 : Django 모델을 첫 번째 인자로 받고, 몇 개의 키워드 인수를 모델 관리자인 get() 함수로 넘긴다. 만약 존재하지 않을 경우 HTTP404 예외가 발생한다.

1️⃣ Update(수정/갱신)

def update(request):
    if request.method == 'POST' and 'id' in request.POST:
        item = get_object_or_404(Restaurant, pk=request.POST.get('id'))
        password = request.POST.get('password', '')
        form = UpdateRestaurantForm(request.POST, instance=item)
        if form.is_valid() and password == item.password:
            item = form.save()
    elif request.method == 'GET':
        item = get_object_or_404(Restaurant, pk=request.GET.get('id'))
        form = RestaurantForm(instance=item)
        return render(request, 'p1/update.html', {'form': form})
    return HttpResponseRedirect('/p1/list/')
  • 요청이 POST 일때와 GET 일때를 구분해야 한다.


1️⃣ Delete(삭제)

def delete(request, id):
    item = get_object_or_404(Restaurant, pk=id)
    if request.method == 'POST' and 'password' in request.POST:
        if item.password == request.POST.get('password') or item.password is None:
            item.delete()
            return redirect('list')
        return redirect('restaurant-detail', id=id)
    return render(request, 'p1/delete.html', {'item': item})

📌 전체코드

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'p1',
]

urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('p1/', include('p1.urls')),
    path('admin/', admin.site.urls),
]

p1/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('list/', views.list, name='list'),
    path('create/', views.create, name='restaurant-create'),
    path('update/', views.update, name='restaurant-update'),
    # path('detail/', views.detail, name='restaurant-detail'),
    path('restaurant/<int:id>/delete', views.delete, name='restaurant-delete'),

    path('restaurant/<int:id>/', views.detail, name='restaurant-detail'),
    path('restaurant/<int:restaurant_id>/review/create/', views.review_create, name='review-create'),
    path('restaurant/<int:restaurant_id>/review/delete/<int:review_id>', views.review_delete, name='review-delete'),
    path('review/list/', views.review_list, name='review-list'),
]

p1/models.py

from django.db import models


class Restaurant(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=200)

    # 왜 charfield를 썼냐면 여기에 이미지의 url을 넣을거기 때문에 실제로는 문자열로 취급 됨
    image = models.CharField(max_length=500, default=None, null=True)

    # 디폴트 속성을 어떠한 값 이라도 넣어줘야 함
    # 우리가 이미 디폴트 속성을 None 이라고 선언해놨으므로 null값을 허용한다는 선언을 해줘야 함
    password = models.CharField(max_length=20, default=None, null=True)

    # auto_now_add : 최초 저장시에만 현재 날짜로 적용
    created_at = models.DateTimeField(auto_now_add=True)
    # auto_now : 수정이 될 때 마다 현재 날짜로 적용
    updated_at = models.DateTimeField(auto_now=True)


class Review(models.Model):
    point = models.IntegerField()
    comment = models.CharField(max_length=500)

    # on_delete=models.CASCADE : FK를 포함하는 모든 모델 인스턴스도 같이 삭제
    restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

p1/forms.py

from django.forms import ModelForm
from django import forms
from p1.models import Restaurant, Review
from django.utils.translation import gettext_lazy as _

# 리뷰 선택지 정의
REVIEW_POINT_CHOICES = (
    ('1', 1),
    ('2', 2),
    ('3', 3),
    ('4', 4),
    ('5', 5),
)


class ReviewForm(ModelForm):
    class Meta:
        model = Review
        fields = ['point', 'comment', 'restaurant']
        labels = {
            'point': _('평점'),
            'comment': _('코멘트'),
        }
        widgets = {
            # 리뷰를 달 식당 정보가 사용자에게는 보이면 안됨
            'restaurant': forms.HiddenInput(),
            # 선택지를 인자로 전달
            'point': forms.Select(choices=REVIEW_POINT_CHOICES)

        }
        help_texts = {
            'point': _('평점을 입력해주세요.'),
            'comment': _('코멘트를 입력해주세요.'),
        }


class RestaurantForm(ModelForm):
    class Meta:
        model = Restaurant
        # 입력받을 필드를 정의
        fields = ['name', 'address', 'image', 'password']

        labels = {
            'name': _('이름'),
            'address': _('주소'),
            'image': _('이미지 url'),
            'password': _('게시물 비밀번호'),
        }
        help_texts = {
            'name': _('이름을 입력해주세요.'),
            'address': _('주소를 입력해주세요.'),
            'image': _('이미지의 url을 입력해주세요.'),
            'password': _('비밀번호를 입력해주세요.'),
        }
        widgets = {
            # 사용자 입력 필드에서 비밀번호 입력하는 필드처럼 글자가 가려지는 필드가 나옴
            'password': forms.PasswordInput()
        }
        error_messages = {
            'name': {
                'max_length': _("이름이 너무 깁니다. 30자 이하로 해주세요."),
            },
            'image': {
                'max_length': _("이미지 주소의 길이 너무 깁니다. 400자 이하로 해주세요."),
            },
            'password': {
                'max_length': _("비밀번호가 너무 깁니다. 20자 이하로 해주세요."),
            },
        }


class UpdateRestaurantForm(RestaurantForm):
    class Meta:
        model = Restaurant
        exclude = ['password']

p1/views.py

from django.shortcuts import render, get_object_or_404, redirect
from p1.models import Restaurant, Review
from django.core.paginator import Paginator
from p1.forms import RestaurantForm, ReviewForm, UpdateRestaurantForm
from django.http import HttpResponseRedirect
from django.db.models import Count, Avg


def list(request):
    restaurants = Restaurant.objects.all()\
        .annotate(reviews_count=Count('review'))\
        .annotate(average_point=Avg('review__point'))
    paginator = Paginator(restaurants, 5)
    page = request.GET.get('page')
    items = paginator.get_page(page)

    context = {
        'restaurants': items
    }
    return render(request, 'p1/list.html', context)


def create(request):
    if request.method == 'POST':
        form = RestaurantForm(request.POST)
        if form.is_valid():
            new_item = form.save()
        return HttpResponseRedirect('/p1/list/')
    form = RestaurantForm()
    return render(request, 'p1/create.html', {'form': form})


def update(request):
    if request.method == 'POST' and 'id' in request.POST:
        item = get_object_or_404(Restaurant, pk=request.POST.get('id'))
        password = request.POST.get('password', '')
        form = UpdateRestaurantForm(request.POST, instance=item)
        if form.is_valid() and password == item.password:
            item = form.save()
    elif request.method == 'GET':
        item = get_object_or_404(Restaurant, pk=request.GET.get('id'))
        form = RestaurantForm(instance=item)
        return render(request, 'p1/update.html', {'form': form})
    return HttpResponseRedirect('/p1/list/')


def detail(request, id):
    if id is not None:
        item = get_object_or_404(Restaurant, pk=id)
        reviews = Review.objects.filter(restaurant=item).all()
        return render(request, 'p1/detail.html', {'item': item, 'reviews': reviews})
    return HttpResponseRedirect('/p1/list/')


def delete(request, id):
    item = get_object_or_404(Restaurant, pk=id)
    if request.method == 'POST' and 'password' in request.POST:
        if item.password == request.POST.get('password') or item.password is None:
            item.delete()
            return redirect('list')
        return redirect('restaurant-detail', id=id)
    return render(request, 'p1/delete.html', {'item': item})


def review_create(request, restaurant_id):
    if request.method == 'POST':
        form = ReviewForm(request.POST)
        if form.is_valid():
            new_item = form.save()
        return redirect('restaurant-detail', id=restaurant_id)

    item = get_object_or_404(Restaurant, pk=restaurant_id)
    form = ReviewForm(initial={'restaurant': item})
    return render(request, 'p1/review_create.html', {'form': form, 'item': item})


def review_delete(request, restaurant_id, review_id):
    item = get_object_or_404(Review, pk=review_id)
    item.delete()

    return redirect('restaurant-detail', id=restaurant_id)


def review_list(request):
    reviews = Review.objects.all().select_related().order_by('-created_at')
    paginator = Paginator(reviews, 10)

    page = request.GET.get('page')
    items = paginator.get_page(page)

    context = {
        'reviews': items
    }
    return render(request, 'p1/review_list.html', context)

좋은 웹페이지 즐겨찾기