Startproject

프로젝트(mypjt) 생성

django-admin startproject mypjt .

 

movies 앱 생성

python manage.py startapp movies

 

가상환경 설정

python3 -m venv venv

source venv/Scripts/activate

 

pip install-r requirements.txt

pip install bootstrap

 

requirements.txt로 django, django-extensions, bootstrap5 등을 설치했다. 부트스트랩 cdn으로 가져왔었는데, pip install으로 설치했다. settings.py에 bootstrap5를 등록해야하고, {% load bootstrap5 %} 으로 가져온다.

INSTALLED_APPS = [
    'movies',
    'django_extensions',
    'bootstrap5',

installed_apps에 추가한다.

 

 

 

Model

from django.db import models

class Movie(models.Model):
    title = models.CharField(max_length=20)
    audience = models.IntegerField()
    release_date = models.DateField()
    genre = models.CharField(max_length=30)
    score = models.FloatField()
    poster_url = models.TextField()
    description = models.TextField()

migration 진행한다.

python manage.py makemigrations

python manage.py migrate

 

 

 

URL

from django.urls import path
from . import views

app_name='movies'
urlpatterns = [
    path('',views.index, name='index'),
    path('create/',views.create, name='create'),
    path('<int:pk>/', views.detail, name='detail'),
    path('<int:pk>/update/',views.update, name = 'update'),
    path('<int:pk>/delete/',views.delete, name='delete'),
]

 

 

 

Views

from django.shortcuts import render, redirect
from django.views.decorators.http import require_safe, require_POST,require_http_methods


@require_safe
def index(request):
    return render(request,'movies/index.html',context)


@require_safe
def detail(request, pk):
    return render(request, 'movies/detail.html',context)


@require_http_methods(['GET','POST'])
def create(request):
    return render(request,'movies/create.html',context)


@require_http_methods(['GET','POST'])
def update(request, pk):
    return render(request,'movies/update.html',context)


@require_POST
def delete(request, pk):
    return redirect('movies:index')

views 기본형태

GET, POST 조건을 주기위해 decorators을 추가했다.

redirect, render을 구분해서 공부했다.

 

 

 

from .forms import MovieForm
from django.shortcuts import render, redirect
from .models import Movie
from django.views.decorators.http import require_safe, require_POST,require_http_methods 
@require_safe
def index(request):
 movies = Movie.objects.all()
 context={
 'movies':movies,
 }
 return render(request,'movies/index.html',context)

# Movie모델 객체에서 all()으로 모두 가져와서 movies로 보내줌.

@require_safe
def detail(request, pk):
 movie=Movie.objects.get(pk=pk)
 context={
 'movie':movie,
 }
 return render(request, 'movies/detail.html',context)

# pk로 하나의 인스턴스를 movie로 보내줌.

@require_http_methods(['GET','POST'])
def create(request):
 if request.method=="POST":
 form = MovieForm(request.POST)
 if form.is_valid():
 movie = form.save()
 return redirect('movies:detail', movie.pk)
 form = MovieForm()
 context={
 'form':form,
 }
 return render(request,'movies/create.html',context)

# POST : 입력한 내용(request.POST)을 저장하고 save()가 반환한 생성 객체의 pk를

# detail로 redirect할 때 보내준다.

# GET : 영화 생성 입력 화면.

@require_http_methods(['GET','POST'])
def update(request, pk):
 movie=Movie.objects.get(pk=pk)
 if request.method=="POST":
 form = MovieForm(request.POST, instance=movie)
 if form.is_valid():
 form.save()
 return redirect('movies:detail', movie.pk)
 else:
 form = MovieForm(instance=movie)
 context={
 'form':form,
 'movie':movie,
 }
 return render(request,'movies/update.html',context)

# POST : 수정한 내용 저장
# GET : 수정할 화면



@require_POST
def delete(request, pk):
    if request.method=="POST":
        movie=Movie.objects.get(pk=pk)
        movie.delete()
    return redirect('movies:index')
#삭제는 POST만!, delete()해준다. 

 

 

 

Admin

admin.py

from django.contrib import admin
from .models import Movie
admin.site.register(Movie)

admin 사이트 등록 (모델)

 

 

Form

from django import forms
from .models import Movie

class MovieForm(forms.ModelForm):

    class Meta:
        model = Movie
        fields = '__all__'

form 기본형태

forms.ModelForm을 상속받아 MovieForm을 만들었다.

class Meta: 에 model을 내가 원하는 모델로 변경해주었다.

 

 

 

 

from django import forms
from .models import Movie

choice=[('코미디','코미디'),('액션','액션'),('로맨스','로맨스'),('스릴러','스릴러')]
class MovieForm(forms.ModelForm):
    title = forms.CharField(
        widget=forms.TextInput(
            attrs={
                'placeholder':'Title'
            }
        ))

    audience = forms.FloatField(
        widget=forms.NumberInput(
            attrs={
                'placeholder':'Audience'
            }
        )
    )

    genre = forms.ChoiceField(
        widget=forms.Select,choices=choice)

    score = forms.FloatField(
        widget=forms.NumberInput(
            attrs={'min':'0','max':'5','step':"0.5",
            "placeholder":"Score"}))

    release_date=forms.DateField(
        widget = forms.DateInput(
            attrs={
                'type':'date'
            }
        )
        )

    poster_url = forms.CharField(
        widget = forms.Textarea(
            attrs={
                'placeholder':'Poster url'
            }
        )
    )
    description = forms.CharField(
        widget = forms.Textarea(
            attrs={
                'placeholder':'Description'
            }
        )
    )

    class Meta:
        model = Movie
        fields = '__all__'
  1. genre 필드
  2. ChoiceField를 이용해서 Select를 구현했다.
  3. score 필드
  4. numberinput, max, min, step 설정했다.
  5. release_date 필드

        datefield - dateinput - type:date로 날짜형식을 달력으로 선택할 수 있도록 했다.

나머지는 기본적으로 placeholder를 넣어주기 위해서 widget, attrs를 사용했다.

 

 

 

Template

base.html

{% load bootstrap5 %}

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        {% bootstrap_css %}
        {% bootstrap_javascript %}
    <style>
        *{
            margin:auto;
        }
        h1{
            text-align: center;
            font-weight: bold;
            padding-top: 10px;
        }
        a {
            text-decoration:none;
            color:black;
        }
        a:hover{
            color: blue;
        }
    </style>

</head>
<body>
    {% block content %}
    {% endblock content %}
</body>
</html>

base.html 에는 부트스트랩을 넣어주고, 기본적인 css를 추가했다.

 

 

index.html

{% extends 'base.html' %}
{% block content %}
<h1>INDEX</h1>
<div class="d-flex">
<a class="btn btn-success btn-sm justify-content-end" href="{% url 'movies:create'%}">영화 추가하기</a>
</div>
<hr>
<ul class="list-group m-4" >
{% for movie in movies %}
<li class="list-group-item d-flex justify-content-between align-items-start">
<div style="width:500px" class="ms-2 my-2 me-auto">
    <div class="fw-bold"><a href="{% url 'movies:detail' movie.pk %}">{{movie.title}}</a>
    </div>
  </div>
  <span class="badge bg-warning rounded-pill ">평점 {{movie.score}}</span>
</li>
{% empty %}
영화가 없습니다.
{% endfor %}
</ul>
{% endblock content %}

movies를 받아온 것을 for문을 돌려 movie로 하나씩 요소를 꺼내왔다.

 

 

detail.html

{% extends 'base.html' %}
{% block content %}
<h1>
    DETAIL
</h1>
<hr>
<div class="card" style="width: 20rem;">
    <img src="{{movie.poster_url}}" class="card-img-top" alt="...">
    <div class="card-body m-0">
      <h3 class="card-title fw-bold text-center">{{movie.title}}</h3>
      <p class="card-text">Audience : {{movie.audience}}</p>
      <p class="card-text">Release Dates : {{movie.release_date}}</p>
      <p class="card-text">Genre : {{movie.genre}}</p>
      <p class="card-text">Score : {{movie.score}}</p>
      <p class="card-text overflow-auto bg-light p-2" style="height:300px" >{{movie.description}}</p>
      <div class="d-flex mt-5">
      <a href="{% url 'movies:index' %}" class="btn btn-secondary">목록</a>
      <a href="{% url 'movies:update' movie.pk%}" class="btn btn-success">수정</a>
      <form action="{% url 'movies:delete' movie.pk%}" method="POST" >
        {% csrf_token %}
        <input class="btn btn-danger " type="submit" value="삭제">
      </form>
     </div>
    </div>
  </div>

{% endblock content %}

pk를 가지고 받아온 movie 객체의 요소를 하나씩 꺼내서 모든 정보를 보여주는 화면.

 

 

create.html

{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
    <h1>CREATE</h1>
    <form action="{% url 'movies:create' %}" method="POST">
        {% csrf_token %}
        <div style="width:500px">
        {%bootstrap_form form %}
        <div class="container">
        <input class="my-1 btn btn-primary col-12" type="submit" value="작성">
        <a class="my-1 btn btn-secondary col-12" href="{% url 'movies:index' %}">목록으로</a>
        </div>
    </divc>
    </div>
    </form>
{% endblock content %}

모델폼의 form.as_p를 이용했었는데, 부트스트랩 bootstrap_form form을 이용하니 훨씬 깔끔했다.

버튼을 한줄에 꽉 차게 넣고싶어서 grid를 사용했다.

 

 

update.html

{% extends 'base.html' %}
{% load bootstrap5 %}

{% block content %}
    <h1>UPDATE</h1>
    <form action="{% url 'movies:update' movie.pk %}" method="POST">
        {% csrf_token %}
        <div style="width:500px">
        {%bootstrap_form form %}
    </form>
        <div class="container">
        <input class="my-1 btn btn-primary col-12" type="submit" value="수정">
        <input class="my-1 btn btn-danger col-12" type="reset" value="초기화">
    <a class="my-1 btn btn-secondary col-12" href="{% url 'movies:detail' movie.pk %}">뒤로가기</a>
</div>
    {% endblock content %}

bootstrap_form을 이용했다.

 

 

 

 

 

프로젝트를 만들때 models, views, templates 의 흐름을 따라가면서 장고의 MTV모델을 이해했다.

장고 CRUD는 확실히 이해한 것 같아서 만족!