취미삼아 배우는 프로그래밍

self erp system 만들기 로그.191102 본문

파이썬(장고)

self erp system 만들기 로그.191102

Nadure 2019. 11. 3. 10:48

0. 지금까지의 결과물!

폼 입력 화면.

 

표 출력 화면

 

djang-rest-framework 출력화면.

 

  • 폼을 통해 데이터를 입력

  • Django-Rest-Framework를 통해 json으로 결과 값 확인

  • 클라이언트(html쪽)단에서 json을 표로 출력

 

 

1. 삽질 스토리

0) 폼 입력의 구현과정

- 내장된 폼 팩토리를 이용한다.!

동영상 강좌를 참고했다.

https://www.youtube.com/watch?v=6oOHlcHkX2U&t=221s

 

https://www.youtube.com/watch?v=3XOS_UpJirU&t=855s

 

ㅁ 테스트해본 것(이라 쓰지만 삽질)

기능
느낌
forms.modelformset

간단한 폼을 출력하는거 있어서,

딱 한 줄로 폼셋을 만들어버린다.

예시)

test_modelFormset = forms.modelformset_factory(test_model,exclude=(),extra=1)

단점은 그만큼 추가기능 사용에 제한이 생긴다는 것인데,

어느정도는 옵션등을 사용할 수 있는듯 하다.

* 옵션이 많아지면 가독성이 무지하게 떨어지는 느낌..

FormFactory

마찬가지로 간단히 작성을 해주기는 하지만,

세부적인 조정에 있어선 매우 힘이들었다.

Class Form(사용)

어쩔 수 없이 사용하는 거긴 하지만,

기능이 너무 많아서.. 사용하기 힘들었다.

대게의 예제들이 이것을 사용하고 있었다.

 

** 상기 사실은 모두 주관적인 느낌.

 

ㅁ Form -> View -> Template

사실 폼을 작성함에 있어서, 그냥 넘겨주기만 하면 알아서 잘 뜬다.

test_modelFormset = forms.modelformset_factory(test_model,exclude=(),extra=1)

form_third = test_modelFormset(request.POST or None)

form_third Template에 던져주고

<div>
        <form method='POST' autocomplete="off" > {% csrf_token %}
            {{ form_third }}
            <button class="waves-effect waves-light btn"><i class="material-icons left">cloud</i>저장</button>
        </form>
    </div>

 

그냥 템플릿에 이렇게만 써도

다 나오긴 다 나온다.

문제는 커스터마이징이 힘들다는것에 있었다

나는 Materialize CSS를 사용하고 있는 중인데,

요게 요물이다.

 

bootstrap에 맞춰져 있는 django에서

materialized 를 애초부터 사용하는거 자체가 큰 욕심이었나보다.

간단하게 될 줄 알았지만, 간단한걸 복잡하게되는 사태가 생겼다.

 

Materialized 때문에 편리한 폼셋을 버리고 폼을 받아다가 재가공하는 작업을 해야 했다..

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<div class="container">
    
        <form method='POST' autocomplete="off" class="col s12" > {% csrf_token %}
        <div class="row">        
            {% for field in form.visible_fields %}
                <div class="input-field inline">
                    {{ field.errors }}
                    {{ field.label_tag }} {{ field }}
                </div>
            {% endfor %}
            <div class="divider"></div>
            <div>
            {% for field in form_detailed.visible_fields %}
                <div class="input-field col s6">
                    {{ field.errors }}
                    {{ field.label_tag }} {{ field }}
                </div>
        
            {% endfor %}
            </div>
 
            <div class="col s6">
                <table>
                    <thead>
                        
                    </thead>
                </table>
            </div>
 
            <div>
                <button class="waves-effect waves-light btn"><class="material-icons left">cloud</i>저장</button>
            </div>
            
        </form>
    </div>
    </div>
cs

대충 이런 느낌으로 재가공했다.

 

언뜻 보기에 간단해보이지만,,

시행착오가 많고 무진장 어려웠다.

 

나타난 폼에서의 문제점은

 

체크박스

날짜(DatePicker)

 

 

일단 체크박스를 현 상태에서 해결할 방법이 떠오르지 않아서,

그냥 다 빼버렸다.

그리고, DatePicker는 도저히 빼낼생각이 떠오르지 않다가, 해결하게 됐는데,

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
base_date_format = '%Y/%m/%d'
class DetailedInformation(forms.ModelForm):
 
    #non_standard_coat_complete_chk = forms.BooleanField()
    #non_standard_coat_complete_chk = forms.BooleanField()
 
    final_date = forms.DateField(widget = forms.DateInput(format=base_date_format),
                                   input_formats=(base_date_format,),
                                   required=False, label='납기일')
                                   
    submit_date = forms.DateField(widget = forms.DateInput(attrs={'class'"input-lg"},format=base_date_format,),
                                   input_formats=(base_date_format,),
                                   required=False, label='접수일')
    #non_standard_coat_complete_chk = forms.BooleanField(widget = forms.CheckboxInput(attrs={'checked':'checked'}))
 
    class Meta:
        model = wall_info
        exclude = ('wall_name','non_standard_coat_complete_chk','addings_manufacturing',
                'non_standard_manufacturing_chk','non_standard_outing_chk','standard_complete_chk','notes',
                'final_date','standard_count',)
 
    def __init__(self, *args, **kwargs):
        #이거 작동하긴하는거야?
        super(DetailedInformation, self).__init__(*args, **kwargs)
        self.fields['final_date'].localize = True
        self.fields['final_date'].widget.is_localized = True
cs

 

대충 이런식으로 작성했다.

Meta 쪽에서 체크박스와 기타 불필요한 것들 및 날짜 선택 관련 부분을 모두 뺏다.

윗쪽의 base_date_format은 date에 관하여 폼을 입력받는 형식을 지정하는 것이다.

이 양식과 다르면 똑바로 적으라고 오류가 생기는 그런 식으로 된다.

뷰 단과 맞춰줘야한다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var set_months=['1월',  '2월',  '3월',  '4월',  '5월',  '6월',  '7월',  '8월',  '9월',  '10월',  '11월',  '12월'];
var set_weekdayShort = [  '일',  '월',  '화',  '수',  '목',  '금',  '토'];
 
$(document).ready(function(){
    $('#id_final_date').datepicker({
        format:"yyyy/mm/dd",
        autoClose:true,
        i18n:{
            months:set_months,
            weekdaysShort:set_weekdayShort,
            monthsShort:set_months,
        },
    });
    
var elem = $('#id_submit_date');
$('#id_submit_date').datepicker({
    format:"yyyy/mm/dd",
    autoClose:true,
    defaultDate:new Date(),
    setDefaultDate:true,
    i18n:{
        months:set_months,
        weekdaysShort:set_weekdayShort,
        monthsShort:set_months,
    },
});
 
});
cs

HTML쪽에서 DatePicker를 사용하도록 설정해준다.

이때 폼셋의 element 아이디는 id_필드명

이 돼고 이걸로 Materialize DatePicker를 사용해주면 된다.

여기 yyyy/mm/dd 는 입력하는 양식 이다.

요렇게 켜진다.

 

 

- 그치만 또 문제가 생겼다.

폼셋이야 그냥 저렇게 어찌어찌 만든다 쳐도.

Foreing Key를 통한 데이터 저장은 해본 적이 없다.

검색해보면 데이터 모델에 대한 설명과

shell로 이렇게 써라.. 만 있을 뿐

somefield.objects.filter(pk=5)

막 이런식으로밖에 없었다.

어려웠다.

 

 

그치만, 그냥 내 마음대로 해냈다.

문제는 다음에 생각하자..

 

일단 지금 작성하고자 하는건 방법론 그 자체이기 때문에,

꼼수는 없이, 일단은 코드부터

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def drawings_submit_view(request):
    form = Drawings_input(request.POST or None)
    form_is_success = 'false'
 
    form_detailed = DetailedInformation(request.POST or None)
    form_third = test_modelFormset()
 
    if(request.method=='POST'):
        if(all([form.is_valid(),form_detailed.is_valid()])):
 
            obj,created = test_model.objects.get_or_create(**form.cleaned_data)
            obj_pk = obj.id
            
            #get_instance = wall_info.objects.get(wall_name__id=obj.id)
            obj_info, created = wall_info.objects.get_or_create(wall_name__id=obj.id)
            
            
            if(created):
                obj_info.wall_name_id = obj_pk
            else:
                obj_info.__dict__.update(**form_detailed.cleaned_data)
 
            
            obj_info.save()
 
            form_is_success='true'
 
            form = Drawings_input()
            form_detailed = DetailedInformation()
        else:
            form_is_success = 'not'
    context = {
        'form':form,
        'form_detailed':form_detailed,
        'form_is_success':form_is_success,
        'form_third':form_third,
    }
 
    return render(request, "modeltest/modeltest_index.html",context)
cs

 

form_is_success 부분은 미래의 내가 없앨 부분이라 무시하면 된다.(그치?)

시행착오들의 흔적들이 남아있는 그런 코드라, 지금은 좀 너저분하다.

그치만 볼 흐름은 딱 두 개다.

form, form_detailed

Some Field A 는 Model 과 foreign Key로 연결돼 있다.

obj,created = test_model.objects.get_or_create(**form.cleaned_data)

obj_info, created = wall_info.objects.get_or_create(wall_name__id=obj.id)

if(created):
                obj_info.wall_name_id = obj_pk
            else:
                obj_info.__dict__.update(**form_detailed.cleaned_data)

메인 코드는 이거다.

get_or_create 를 통해

해당 데이터들이 있다면, obj 에는 그 결과값(get이니 무조건 한 개)에 대한

인스턴스를 잡고 created에는 false를 넣는다.

 

그게 아니라면 obj에는 만들어서 그 인스턴스를 잡고 created에는 True를 넣는다.

그리고 어쨋든 잡게된 obj 인스턴스에서의 id값을 pk 값으로 wall_name(Foreing Key 사용 모델)에 입력시킨다.

 

그럼 cool 끝난다.

 

 

Model A에 대한 id 값은 결국 Unique 하게 사용 때문에, 뻘짓만 안 한다면(해봤기때문에 암),

get을 통해 이상한 데이터 값이 잡히는 오류는 생기지 않는다.

 

정상적으로 돌아갈 수 밖에 없는 이유는 이 때문,,

검색 하다 보니, get or created에서 Transaction이 생긴다고 하는데

걍 무시하려 한다. 내 예상으로 우리쪽 사용자들은 그렇게 동시다발적으로 데이터를 입력하진 않는다... 아마도.?

 

그럼 이제 업데이트 부분이 남았는데,

업데이트는

if(created):
                obj_info.wall_name_id = obj_pk
            else:
                obj_info.__dict__.update(**form_detailed.cleaned_data)

결국 마지막에 있는 created의 값이 foreign key 가 들어 간 인스턴스를 잡았는지 새로 만들었는지에 대한 여부기 때문에,

이걸로 판단하여, 업데이트를 시킨다.(처음에는 삭제하고 다시만들게 했었다.)

 

 

 

1) Django-Rest-Framework를 통해 json으로 결과 값 확인

- 처음엔 생각했다.

일단 Django로 모든걸 할 수 있고나서 부터 저걸 건드리자 !

list 에는 순서가 있는법 !

 

 : 그래서 정말 큰 삽질이 시작됐다.

 : Django로는 간단한 표 밖에 만들지 못한다.

아니, 만들 수는 있지만, 만약 스케일이 커질 경우 사용자의 요청에 대해 모든 작업을 서버에서 부담하고

그 부담은 다시 사용자에게 가는 셈이 된다.

서버는 데이터만 전달하고, 보는쪽에서 그 데이터를 처리하는게 맞지 않을까?

 

 

- 그러고보니 결국 분산돼야 하는셈이다.

사용자도 어느정도의 부담을 동반한다는 것인데, 딱히 문제는 없는것 같다.

양이 많으면 렉이야 원래 걸리는것 아닌가.

 

그치만, 분산되야 한다는 건 나도 여러모로 html을 건드렸다가, python 을 건드렸다가.

다각화 적인 고통을 동반한다는 뜻이었다.

 

쨌뜬, 그러했다.

 

 

생각보다. Django 를 Serialize 시켜서 json으로 보내는건 간단했다.

django-rest-framework가 이름이 길어서 어려운줄 알았는데,

어려운 일을 쉽게 만들어주는 도구였다. 물론 이것도 복잡하게 하면 어렵긴 하겠지..

 

#views.py

1
2
3
4
5
6
class dataDetaileddataView(viewsets.ModelViewSet):
    queryset = wall_info.objects.all()
    serializer_class = detail_InfoSerializer
 
    def perform_create(self, serializer):
        serializer.save()
cs

 

# serializers.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from rest_framework import serializers
from .models import test_model, wall_info
 
class baseInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = test_model
        fields = ('company_name','location_name','cat_1','cat_2')
        
class detail_InfoSerializer(serializers.ModelSerializer):
 
    wall_name = baseInfoSerializer(required=True)
    class Meta:
        model = wall_info
        fields = '__all__'
 
    def to_representation(self, instance):
        my_fields = {'manufacturing_id''non_stnadard_area''standard_count''second_manufacturing',
                     'addings_manufacturing''final_date''non_standard_coat_complete_chk',
                     'non_standard_manufacturing_chk''non_standard_outing_chk''standard_complete_chk',
                     'standard_complete_chk','notes','salesman_name','drawing_designer'}
        data = super().to_representation(instance)
        for field in my_fields:
            try:
                if not data[field]:
                    data[field] = ""
            except KeyError:
                pass
        return data
        
    ## 일단 이건 안됨!!
    ### Foreing Key를 쓰는 바람에 험난해짐.
    def create(self, validated_data):
        return wall_info.objects.create(**validated_data)
cs

 

detail_InfoSerializer 내의 to_representaition

보내줄 json 데이터를 교정해줄 때 사용해주는 함수인듯 하다.

따로 사용하라는 말을  안 했는데, 알아서 사용한다.

저거는 데이터 내에 값이 없을 경우

null 지옥을 보게 되는데, null을 없애게 하려 넣었다.

 

3. 앞으로.

 

이제 표 내부에서 체크박스 클릭하는 걸 ajax처리로 하는것 을 넣고

표를 조금 더 다듬고

 

규격 부분에는

표 안의 표를 집어넣어서 조금 더 크게 하여 표현하려 한다.

 

삽질이 하도 많아서 되게 글이 길 줄 알았는데

뭐가 된게 없어서 그런지 별 내용이 없는것 같다.

 

 

 

 

Comments