Django 실전 정리

패스트캠퍼스 Django 실전 정리

프로젝트 및 앱 구성

django-admin startproject fc_django
django-admin startapp fcuser
django-admin startapp product
django-admin startapp order

모델 정의 및 앱 등록

유저

from django.db import models

class Fcuser(models.Model):
    email = models.EmailField(verbose_name='이메일')
    password = models.CharField(max_length=64, verbose_name='비밀번호')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='등록날짜')

    def __str__(self):
      return self.email

    class Meta:
        db_table = 'fastcampus_fcuser'
        verbose_name = '사용자'
        verbose_name_plural = '사용자'

제품

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=256, verbose_name='상품명')
    price = models.IntegerField(verbose_name='상품가격')
    description = models.TextField(verbose_name='상품설명')
    stuck = models.IntegerField(verbose_name='재고')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='등록날짜')

    def __str__(self):
      return self.name

    class Meta:
        db_table = 'fastcampus_product'
        verbose_name = '상품'
        verbose_name_plural = '상품'

주문

from django.db import models

class Order(models.Model):
    fcuser = models.ForeignKey('fcuser.Fcuser', on_delete=models.CASCADE, verbose_name='사용자')
    product = models.ForeignKey('product.Product', on_delete=models.CASCADE, verbose_name='상품')
    quantity = models.IntegerField(verbose_name='수량')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='등록날짜')

    def __str__(name):
      return str(self.fcuser) + ' ' + str(self.product)

    class Meta:
        db_table = 'fastcampus_order'
        verbose_name = '주문'
        verbose_name_plural = '주문'

앱 등록 및 마이그레이션

# settings.py
INSTALLED_APPS += [
    'fcuser',
    'product',
    'order',
]

이후
python manage.py makemigrations
python manage.py migrate

Admin 사이트 등록

유저

from django.contrib import admin
from .models import Fcuser

class FcuserAdmin(admin.ModelAdmin):
    # 튜플로 인식시켜야함
    list_display = ('email', )

admin.site.register(Fcuser, FcuserAdmin)

상품

from django.contrib import admin
from .models import Product

class ProductAdmin(admin.ModelAdmin):
    # 튜플로 인식시켜야함
    list_display = ('name', 'price')

admin.site.register(Product, ProductAdmin)

주문

from django.contrib import admin
from .models import Order

class OrderAdmin(admin.ModelAdmin):
    # 튜플로 인식시켜야함
    list_display = ('fcuser', 'product')

admin.site.register(Order, OrderAdmin)

Class-based View

재사용성을 늘리기 위해 클래스 뷰를 사용.

로그인, 회원가입 페이지 생성

forms 파일 생성

from django import forms
from django.contrib.auth.hashers import check_password, make_password
from .models import Fcuser

class RegisterForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    re_password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호 확인'
    )

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')
        re_password = cleaned_data.get('re_password')

        if password and re_password:
            if password != re_password:
                self.add_error('password', '비밀번호가 서로 다릅니다.')
                self.add_error('re_password', '비밀번호가 서로 다릅니다.')
            else:
                fcuser = Fcuser(
                    email=email,
                    password=make_password(password)
                )
                fcuser.save()

class LoginForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')

        if email and password:
            try:
                fcuser = Fcuser.objects.get(email=email)
            except Fcuser.DoesNotExist:
                self.add_error('email', '아이디가 없습니다')
                return

            if not check_password(password, fcuser.password):
                self.add_error('password', '비밀번호를 틀렸습니다')
            else:
                self.email = fcuser.email

views에 클래스 뷰 클래스 생성

from django.shortcuts import render
from django.views.generic.edit import FormView
from .forms import RegisterForm, LoginForm

def index(request):
    return render(request, 'index.html', {'email': request.session.get('user')})

class RegisterView(FormView):
    template_name = 'register.html'
    form_class = RegisterForm
    success_url = '/'

class LoginView(FormView):
    template_name = 'login.html'
    form_class = LoginForm
    success_url = '/'

    def form_valid(self, form):
        self.request.session['user'] = form.email

        return super().form_valid(form)

Product 리스트 불러오기

ListView를 제공하기에 모델을 불러와서 템플릿에 적용해줄 수 있다.

views파일

from django.shortcuts import render
from django.views.generic import ListView
from .models import Product

class ProductList(ListView):
    model = Product
    template_name = 'product.html'
    # 템플릿 파일에서 object_list 대신 사용할 이름
    context_object_name = 'product_list'

html 파일


{% extends "base.html" %}
{% load humanize %}
{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <table class="table table-light">
            <thead class="thead-light">
            <tr>
                <th scope="col">#</th>
                <th scope="col">상품명</th>
                <th scope="col">가격</th>
                <th scope="col">등록날짜</th>
            </tr>
            </thead>
            <tbody class="text-dark">
            {% for product in product_list %}
                <tr>
                    <th scope="row">{{product.id}}</th>
                    <th scope="row">{{product.name}}</th>
                    <th scope="row">{{product.price|intcomma}} 원</th>
                    <th scope="row">{{product.register_date|date:'Y-m-d H:i'}}</th>
                </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>
</div>
{% endblock %}

숫자나 시간을 보기 편하게 바꿔주기 위해 humanize를 불러서 적용한다. 프로젝트 settigs파일에 humanize 앱을 추가해줘야 한다.

추가

INSTALLED_APPS += [
    'fcuser',
    'product',
    'order',
    'django.contrib.humanize',
]

제품 생성 페이지 만들기

forms 파일 생성

from django import forms
from django.contrib.auth.hashers import check_password, make_password
from .models import Fcuser

class RegisterForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    re_password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호 확인'
    )

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')
        re_password = cleaned_data.get('re_password')

        if password and re_password:
            if password != re_password:
                self.add_error('password', '비밀번호가 서로 다릅니다.')
                self.add_error('re_password', '비밀번호가 서로 다릅니다.')
            else:
                fcuser = Fcuser(
                    email=email,
                    password=make_password(password)
                )
                fcuser.save()

class LoginForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')

        if email and password:
            try:
                fcuser = Fcuser.objects.get(email=email)
            except Fcuser.DoesNotExist:
                self.add_error('email', '아이디가 없습니다')
                return

            if not check_password(password, fcuser.password):
                self.add_error('password', '비밀번호를 틀렸습니다')
            else:
                self.email = fcuser.email

views 파일

from django.shortcuts import render
from django.views.generic import ListView
from django.views.generic.edit import FormView
from .models import Product
from .forms import RegisterForm

class ProductList(ListView):
    model = Product
    template_name = 'product.html'
    # 템플릿 파일에서 object_list 대신 사용할 이름
    context_object_name = 'product_list'

class ProductCreate(FormView):
    template_name = 'register_product.html'
    form_class = RegisterForm
    success_url = '/product/'

html파일


{% extends "base.html" %}
{% block contents %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>상품 생성</h1>
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        {{ error }}
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        <form method="POST" action=".">
            {% csrf_token %}
            {% for field in form %}
            <div class="form-group">
                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                {% ifequal field.name 'description'%}
                <textarea class="form-control" name="{{field.name}}" id="{{ field.id_for_label }}"></textarea>
                {% else %}
                <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}" />
                {% endifequal %}
            </div>
            {% if field.errors %}
            <span style="color: red">{{field.errors}}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">생성</button>
        </form>
    </div>
</div>
{% endblock %}