Sqix

Django 백엔드 개발 1일차 - 간단 파일업로드 구현 본문

개발일지/SOMBRA

Django 백엔드 개발 1일차 - 간단 파일업로드 구현

Sqix_ow 2021. 11. 29. 12:57

스크림에서 나온 txt를 인코딩 처리 및 데이터 정리를 해서 DB에 넣기 위한 작업을 해야 한다. 정규화를 풀로 하지 않았고(성능관계로) 비정규화상태로 stat이랑 상태정보만 구분해서 DB에 넣기위해 간단하게 설계했다.

 

장고는 MVC 패턴에서 조금 변형된 MTV 패턴을 쓴다고 한다. Model - View - Control이던 MVC 패턴이 Model - Template - View가 된 것인데 사실 View == Template / Control == View가 된 부분이라 큰 틀에서는 차이가 크게 없는 것 같다.

 

기존 MVC에서 Model이 데이터, View가 말 그대로 보여지는 부분, Control이 로직과 그것에 대한 흐름제어였다면 MTV는 모델은 같고 Template가 View의 역할(대충 프론트 부분), View가 Control의 역할(대충 백 부분)이라고 보면 된다.

 

django MTV

  • Model -> 데이터에 대한 설계(DB)
  • Template -> 유저가 보는 화면에 대한 설계 및 구현(FE)
  • View -> 나의 웹에서 동작하는 데이터에 대한 핸들링(BE)

+ 여기서 페이지를 이동하기 위한 URL은 django에서 URLConf를 활용한다.

 

일단 DB 설계를 해놨기 때문에 Model 부분에 설계한 DB에 맞게 클래스를 작성해 주었다.

더보기
from django.conf import settings
from django.db import models
from django.db.models.fields import IntegerField
from django.utils import timezone
import os

"""
SCRIM DATA MODELING
"""
class Scriminfo(models.Model):
    matchid= models.CharField(max_length=13, primary_key=True)
    team_one = models.CharField(max_length=4)
    team_two = models.CharField(max_length=4)
    map = models.CharField(max_length=20)

    def __str__(self):
        return self.title

class Playerstat(models.Model):
    
    class Meta:
        unique_together = ('timestamp', 'matchid', 'player', 'hero')

    #FK
    matchid = models.ForeignKey(Scriminfo, on_delete=models.CASCADE)

    #PK
    player = models.CharField(max_length=13)
    timestamp = models.DecimalField(max_digits=6,decimal_places=2)
    hero = models.CharField(max_length=20)

    #team info
    team = models.CharField(max_length=4)

    #map info
    map = models.CharField(max_length=20)
    section = models.IntegerField()
    point = models.DecimalField(max_digits=4, decimal_places=2)

    #stat info
    hero_damage_dealt = models.DecimalField(max_digits=7, decimal_places=2)
    barrier_damage_dealt = models.DecimalField(max_digits=7, decimal_places=2)
    damage_blocked = models.DecimalField(max_digits=7, decimal_places=2)
    damage_taken = models.DecimalField(max_digits=7, decimal_places=2)
    healing_dealt = models.DecimalField(max_digits=7, decimal_places=2)
    healing_received = models.DecimalField(max_digits=7, decimal_places=2)
    deaths = models.IntegerField()
    eliminations = models.IntegerField()
    finalblows = models.IntegerField()
    environmental_deaths = models.IntegerField()
    environmental_kills = models.IntegerField()
    objective_kills = models.IntegerField()
    solokills = models.IntegerField()
    ultimates_earned = models.IntegerField()
    ultimates_used = models.IntegerField()
    maxhealth = models.IntegerField()
    defensive_assists = models.IntegerField()
    offensive_assists = models.IntegerField()

    def __str__(self):
        return self.title

class PlayerStatus(models.Model):
     
    class Meta:
        unique_together = ('timestamp', 'matchid', 'player', 'hero')

    #FK
    matchid = models.ForeignKey(Scriminfo, on_delete=models.CASCADE)

    #PK
    player = models.CharField(max_length=13)
    timestamp = models.DecimalField(max_digits=6,decimal_places=2)
    hero = models.CharField(max_length=20)

    #team info
    team = models.CharField(max_length=4)

    #map info
    map = models.CharField(max_length=20)
    section = models.IntegerField()
    point = models.DecimalField(max_digits=4, decimal_places=2)

    #status info
    death_by_hero = models.CharField(max_length=20)
    death_by_ability = models.CharField(max_length=20)
    death_by_player = models.CharField(max_length=13)
    resurrected = models.CharField(max_length=12)
    duplicated_hero = models.CharField(max_length=20)
    duplicated_status = models.CharField(max_length=12)
    ultimate_charge = models.IntegerField()
    health = models.DecimalField(max_digits=6, decimal_places=2)
    cooldown1 = models.DecimalField(max_digits=4, decimal_places=2)
    cooldown2 = models.DecimalField(max_digits=4, decimal_places=2)
    cooldown_secondaryFire = models.DecimalField(max_digits=4, decimal_places=2)
    colldown_crouching = models.DecimalField(max_digits=4, decimal_places=2)
    is_alive = models.IntegerField() 
    is_burning = models.IntegerField() 
    is_knocked_down = models.IntegerField() 
    is_asleep = models.IntegerField() 
    is_frozen = models.IntegerField() 
    is_unkillable = models.IntegerField() 
    is_invincible = models.IntegerField() 
    is_stunned = models.IntegerField() 
    is_hacked = models.IntegerField() 

    def __str__(self):
        return self.title

 

목표 시스템이 파일이 업로드가 되면 해당 파일에 대한 데이터를 분석해서 사용자에게 출력해 주는 것이다. 

그러려면 파일업로드를 구현해서 파일을 받아오는 부분이 필요한데 그 부분부터 구현해 볼 것이다.

 

유저가 올리는 파일을 받아오는 것이므로 저장할 파일 경로는 media 폴더 내의 scrimdata_txt로 잡는다. 이를 위해서 일단 url 설정을 할 것이다.

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('practiceweb.urls')),
] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

미디어 파일을 저장할 폴더를 만들고, media_root로 지정한다.

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

이후 settings.py에 와서 역시 media url에 대한 경로 설정 및 root 경로 설정을 해 준다.

 

 

이제 이에 대한 모델을 작성해 보면

# practiceweb/models.py
class UploadScrimData(models.Model):
    attached = models.FileField(blank = True, null = True, upload_to='scrimdata_txt')

    def get_filename(self):
        return os.path.basename(self.attached.name)

 

 

FileField 객체를 만들고 업로드 경로를 설정해 놓는다. 사용자로부터 파일을 받기 위한 form이 필요하므로 forms.py 파일을 생성한 뒤 form 코드를 작성한다.

# practiceweb/forms.py
from django import forms
from .models import UploadScrimData

class UploadScrimDataForm(forms.ModelForm):

    class Meta:
        model = UploadScrimData
        fields = ['attached']

작성된 form을 이용해서 view에서는 파일을 저장하도록 코드를 작성한다.

from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from practiceweb.forms import UploadScrimDataForm

from practiceweb.models import UploadScrimData
# Create your views here.

def index(request):
    return render(request, 'practiceweb/index.html', {})

def simple_upload(request):
    if request.method == 'POST' :
        upload_file = request.FILES['attached']
        fileupload = UploadScrimData(
            attached = upload_file
        )
        fileupload.save()
        return redirect('file_list')

    else:
        upload_scrimdata_form = UploadScrimDataForm
        context = {
            'fileuploadForm' : upload_scrimdata_form
        }
        return render(request, 'practiceweb/upload.html', context)

def file_list(request):
    return render(request, 'practiceweb/file_list.html')

POST 형태로 받아온 파일을 scrimdata 클래스를 생성하면서 파일을 저장하도록 하고 파일 업로드가 성공적으로 되었다는 메시지가 있는 페이지로 리다이렉트시킨다. 이외의 다른 형태로 리퀘스트가 들어오면 파일업로드를 하는 페이지를 띄워준다.

 

Template는 다음과 같다.

{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% block content %}

    <div class = "container">
        <form method="post" action = "{% url 'upload' %}" enctype="multipart/form-data">
            {% csrf_token %}
            {% bootstrap_form fileuploadForm %}
            <input type = "submit" class = "btn btn-primary col-12" value="제출">
        </form>
    </div>

{% endblock %}

부트스트랩 코드를 이용해서 파일업로드 폼을 만들었다. 파일업로드를 위한 csrf_token을 넣었고, 이후 {{fileuploadForm}}을 부트스트랩 폼에 적용시켰다.  

 

그 결과 스크림 데이터 파일이 정상적으로 저장되었다.

다음에는 이렇게 저장된 파일의 데이터를 읽어와서 유니코드 인코딩 / 데이터 처리 작업을 하고 DB에 넣는 것 까지 구현해 보도록 하겠다.

Comments