프로그래밍/생각나는대로 프로젝트

[프로젝트] 04. Django 무신사 재고 관리 시스템

Churnobyl 2023. 4. 10. 01:22
728x90
반응형

무신사 재고 관리 시스템 접속 화면

개요: Django를 이용한 무신사 재고 관리 시스템 만들어보기

 


프로젝트 정보


  • 프로젝트: 무신사 재고 관리 시스템 (파이썬 Django 세션 개인 프로젝트)
  • 개발기간: 2023.04.05 - 2023.04.07 ( 3일 )
  • 역할
    • all of it
  • 사용 언어: Python
  • 사용 라이브러리:
    • Python
      1. Django (웹개발 프레임워크)
    • Django HTML
      1. Bootstrap (프레임워크)

 


프로젝트 구성


★ 파일구조

유저 정보를 관리하는 accounts앱과 재고 관리하는 erp앱으로 구성되어 있다

musinsa  accounts
         ┣ migrations
         ┣ admin.py
         ┣ apps.py
         ┣ models.py  (M)
         ┣ tests.py
         ┣ urls.py
         ┣ views.py  (V)
         ┗ __init__.py

         erp
         ┣ migrations
         ┣ admin.py
         ┣ apps.py
         ┣ models.py  (M)
         ┣ tests.py
         ┣ urls.py
         ┣ views.py  (V)
         ┗ __init__.py

         musinsa
         ┣ asgi.py
         ┣ settings.py (세팅파일)
         ┣ urls.py
         ┣ wsgi.py
         ┗ __init__.py

         templates  (T)
         ┣ accounts
         ┃ ┣ signin.html
         ┃ ┗ signup.html
         ┣ erp
         ┃ ┣ erp_detail.html
         ┃ ┣ erp_goods_new.html
         ┃ ┣ erp_stock_new.html
         ┃ ┗ home.html
         ┗ base.html

         db.sqlite3
         manage.py

 

★ 화면구성 및 코드

erp화면 구성

1. 재고 현황

현재 등록된 상품의 정보 및 재고를 보여주는 부분이다.

 

 

아래의 코드를 보자

<!-- templates/erp/home.html -->

<div class="media-body">
    <div>
        <h5 class="mt-0" style="float: left">상품 별 재고 현황</h5>
        <a href="/erp/new" class="btn btn-outline-primary btn-sm" style="float: right">신규</a>
    </div>
    <table class="table table-hover">
        <thead class="table-dark">
        <tr>
            <th scope="col">No.</th>
            <th scope="col">제품이름</th>
            <th scope="col">카테고리</th>
            <th scope="col">색상</th>
            <th scope="col">사이즈</th>
            <th scope="col">재고</th>
        </tr>
        </thead>
        <tbody class="table-group-divider">
        {% for stuff in all_stuff %}
            <tr onclick="window.location='/erp/{{ stuff.goods }}';">
                <th scope="row">{{ stuff.goods }}</th>
                <td>{{ stuff.goods_name }}</td>
                <td>{{ stuff.goods_category }}</td>
                <td>{{ stuff.goods_color }}</td>
                <td>{{ stuff.goods_size }}</td>
                <td>{{ stuff.inventory.stock }}</td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
</div>

 

 

위 코드는 Django HTML로 구현되어 있으며 간단하게 부트스트랩의 테이블에서 코드를 따왔다.

무신사의 검정톤을 유지하기 위해 thead태그에는 table-dark 클래스를 줬다.

tr에 onclick이벤트로 클릭 시 각 제품의 상세정보 화면으로 이동할 수 있도록 장고 템플릿 문법을 구성했다

 

# erp/views.py

def home(request):
    user = request.user.is_authenticated
    if user:
        return redirect('/erp')
    else:
        return redirect('/sign-in')

이 부분의 view는 더 간단한데, 접속한 유저의 인증을 확인 후에 통과하면 erp화면을 보여주고 아니면 로그인 화면으로 돌려보낸다.


2.  신규 아이템 작성

erp 메인 화면에서 신규 버튼을 눌렀을 때 바뀌는 화면은 이런 식으로 구성돼 있다

역시 html쪽은 부트스트랩을 이용해 구성했으며 최초 재고를 함께 작성하도록 했다

 

이 부분의 view를 보자

# erp/views.py


@login_required
def erp_goods_new(request):
    user = request.user.is_authenticated
    if user:
        if request.method == 'GET':
            return render(request, 'erp/erp_goods_new.html')
        elif request.method == 'POST':
            goods_name = request.POST.get('goods_name', '')
            goods_category = request.POST.get('goods_category', '')
            goods_color = request.POST.get('goods_color', '')
            goods_size = request.POST.get('goods_size', '')
            image = request.POST.get('goods_image', '')
            stock = int(request.POST.get('stock', ''))
            author = request.user.id

            if goods_name == ''\
                or goods_category == ''\
                or goods_color == ''\
                or goods_size == ''\
                or image == ''\
                    or stock == '':
                return render(request, 'erp/erp_goods_new.html', {'error': "필수 입력 사항입니다."})

            exist_item = Stuff.objects.filter(goods_name=goods_name, goods_color=goods_color, goods_size=goods_size)
            if exist_item:
                return render(request, 'erp/erp_goods_new.html', {'error': "이미 있는 상품입니다."})
            else:
                new_one = Stuff.objects.create(goods_name=goods_name, goods_category=goods_category, goods_color=goods_color, goods_size=goods_size, image=image, user_id_id=author)
                Inventory.objects.create(inventory_id=new_one.goods, stock=stock)
                History.objects.create(goods_id=new_one.goods, count=stock, in_out='N')
                return redirect('/erp')
    else:
        return render(request, 'accounts/signin.html')

기본적으로 GET request가 들어오면 입력폼인 erp_goods_new.html 화면을 보여주고 POST요청이 들어오면 폼이 전부 작성되었는지, DB에 같은 상품이 있는지 체크하고 난 뒤에 stuff, inventory, history 테이블에 각각 정보를 넣어준다

stuff는 각 제품의 상세한 정보를 저장하는 테이블, inventory는 제품의 현 재고를 저장하는 테이블, history는 각 제품의 입출고 현황을 기록하는 테이블이다

따라서 stuff 테이블에 정보를 저장함과 동시에 new_one인스턴스로 받아 새로 저장된 row의 pk값을 inventory와 history테이블에서 이용할 수 있도록 했다.

 

 


3. 스테이터스

로그인 후 접속한 화면 좌측에는 장고 템플릿 문법을 이용해 이름, 이메일, 부서 정보가 뜨도록 했다.


4. 회원가입/로그인/ 로그아웃

 

회원가입 폼은 이런 식으로 무난하게 구성했다

views.py의 signup함수를 보자

 

# accounts/views.py

def signup(request):
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            return redirect('/')
        else:
            return render(request, 'accounts/signup.html')
    elif request.method == 'POST':
        username = request.POST.get('username', '')
        email = request.POST.get('email', '')
        password = request.POST.get('password', '')
        password2 = request.POST.get('password2', '')
        department = request.POST.get('department', '')

        if password != password2:
            return render(request, 'accounts/signup.html', {'error': "패스워드를 확인해주세요!"})
        else:
            if username == '' or password == '':
                return render(request, 'accounts/signup.html', {'error': "이름, 비밀번호는 필수입니다!"})

            exist_user = get_user_model().objects.filter(username=username)
            exist_email = get_user_model().objects.filter(email=email)
            if exist_user:
                return render(request, 'accounts/signup.html', {'error': "이미 있는 이름입니다."})
            elif exist_email:
                return render(request, 'accounts/signup.html', {'error': "이미 있는 이메일입니다."})
            else:
                UserModel.objects.create_user(username=username, password=password, email=email, department=department)
                return redirect('/sign-in')

GET request가 들어오면 user인증 정보를 확인해 메인화면 혹은 회원가입 화면으로 보내준다

POST request가 들어오면 정보들을 받아 패스워드 일치 여부, 공란, DB에 유일성 확인 후 user 테이블에 저장한다

 

 

로그인 화면 또한 간단하게 구성했으며 이 부분의 view를 보면

# accounts/views.py

def signin(request):
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            return redirect('/')
        else:
            return render(request, 'accounts/signin.html')
    elif request.method == 'POST':
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')

        me = auth.authenticate(request, username=username, password=password)

        if me is not None:
            auth.login(request, me)
            return redirect('/')
        else:
            return render(request, 'accounts/signin.html', {'error': "이름과 패스워드를 확인해주세요."})

GET request는 user 인증 여부에 따라 메인화면 혹은 로그인 화면으로 보내주도록 했다

POST request는 django.contrib.auth.authenticate()함수를 이용해 이름과 비밀번호 인증을 하도록 했다

 

 

마지막으로 로그아웃은 django.contrib.auth.logout()함수를 이용해주었다.

 


5. 상세 재고 현황

메인화면에서 제품의 행을 누르면 상세 재고 화면으로 들어올 수 있다

여기서는 제품의 입출고 현황을 볼 수 있다

입출고 현황은 history테이블을 이용해 구현했는데 이 부분의 models.py를 보자

 

# erp/models.py

class History(models.Model):
    class Meta:
        db_table = 'history'

    sets = (
        ('I', '입고'),
        ('O', '출고'),
        ('N', '신규'),
    )

    history = models.AutoField(primary_key=True)
    in_out = models.CharField(choices=sets, max_length=1)
    goods = models.ForeignKey(Stuff, on_delete=models.CASCADE)
    count = models.IntegerField(default=0)
    history_time = models.DateTimeField(default=timezone.localtime())

    def __str__(self):
        return str(self.goods)

in_out변수를 보면 각각 I, O, N으로 구분해 저장하도록 했다

 


프로젝트 결과물

https://github.com/Churnobyl/musinsa_stock

 

GitHub - Churnobyl/musinsa_stock

Contribute to Churnobyl/musinsa_stock development by creating an account on GitHub.

github.com

 


프로젝트 완료 후의 감상

장고를 이용한 첫 프로젝트였다.

이틀만에 강의를 전부 듣고 사흘 동안 만든 프로젝트였는데 개념이 확실하게 잡혀 있지 않아서 많이 헤맸다

주말동안에 django docs를 읽으면서 개념을 조금씩 잡았다

생각했던 것보다 양이 더 방대해서 틈날 때마다 읽으면서 이런 것도 가능하구나 정도로 공부를 해야겠다.

반응형