Django 5. 기본 개념 4(Inline Formsets, Filter Form)

jiffydev·2020년 9월 30일
1

1. Inline Formsets

장고에서는 폼셋을 제공하는데, 외래키로 연결된 1:N 관계의 데이터를 수정할 때 한번에 여러 데이터를 수정할 수 있게 해준다. 예를 들어 한 고객이 주문한 여러 주문을 한꺼번에 수정할 수 있게 된다. 그 중에서 이번에 다룰 폼셋은 Inline Formset이다.
인라인 폼셋에서 사용할 수 있는 함수는 inlineformset_factory()함수인데, 자식테이블 중 부모테이블과 관련된 데이터를 리턴한다.
inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False, localized_fields=None, labels=None, help_texts=None, error_messages=None, min_num=None, validate_min=False, field_classes=None)
공식 문서에 있는 inlinefomrset_factory()함수의 인자이다. 함수의 긴 이름만큼이나 인자가 많지만 필수로 지정해야 하는 것은 부모 모델과 자식 모델, 필드명이다. 필드명은 튜플형식으로 입력한다. extra는 기본 3으로 지정되어 있어 폼 3개가 뜨고 이를 원해는 대로 바꿀 수 있다. 활용 예제는 아래와 같다.

...
from django.forms import inlineformset_factory
...

def createOrder(request, pk):
    OrderFormSet=inlineformset_factory(Customer, Order,fields=('product', 'status'), extra=10)
    customer=Customer.objects.get(id=pk)
    formset=OrderFormSet(queryset=Order.objects.none(), instance=customer)
    
    if request.method == "POST":
        #print('printing post:', request.POST)
        # 새로 작성한 내용을 저장
        formset=OrderFormSet(request.POST, instance=customer)
        if formset.is_valid():
            formset.save()
            return redirect('/')
    context={'formset':formset}

    return render(request, 'accounts/order_form.html', context)

일단 instance=부모모델명 으로 부모 테이블이 참조하는 자식 테이블의 데이터를 기본으로 가져오게 할 수 있다. 그런데 기존 데이터가 필요 없다면 예제와 같이 queryset=Order.objects.none()으로 자식 테이블의 데이터는 가져오지 않고 새로 입력해서 데이터를 추가하는 방법도 쓸 수 있다.
폼셋도 다른 폼과 마찬가지로 html에서 사용할 때는 {{formset}}으로 불러올 수 있다. 그런데 이렇게 불러오면 줄바꿈이 안돼서 모양이 이상해진다. 따라서 템플릿 태그로 반복문을 사용하여 하나씩 불러올 수 있는데, 이 경우 주의할 점은 {{formset.management_form}}과 같은 형식으로 반복문 위에 불러와야 한다는 점이다. 이 관리 폼은 폼셋에 있는 폼들을 관리하기 위해 반드시 필요하다.

2. Filter Form

필터는 필드명을 통해 조건을 걸어 조건에 부합하는 결과만을 보여주는 도구이다.(쿼리셋의 필터와는 다르다!) 필터를 작성할 때는 filters.py 파일에 작성해야 한다. 또한 필터를 사용하기 위해서는 django_filters모델을 import해야 한다.

import django_filters
from django_filters import DateFilter, CharFilter
from .models import *

class OrderFilter(django_filters.FilterSet):
    start_date=DateFilter(field_name="date_created", lookup_expr='gte')
    end_date=DateFilter(field_name="date_created", lookup_expr='lte')
    note=CharFilter(field_name='note', lookup_expr='icontains')


    class Meta:
        model=Order
        fields='__all__'
        exclude=['customer', 'date_created']

필터의 종류는 매우 많으므로 필요할 때마다 검색해서 사용하는 것을 추천한다. 예제에 나온 DateFilter는 날짜 필드와 함께 사용하는데, lookup_expr로 ~보다 크거나 같은("gte"), ~보다 작거나 같은('lte') 등의 조건을 추가할 수 있다. CharFilter는 문자열을 검색할 때 사용하며, lookup_expr로 contains(case sensitive), icontains(case insensitive) 등이 있다. 메타 클래스는 폼과 마찬가지로 참조할 모델을 지정해주며, 가져올 필드명을 설정할 수 있는데 exclude는 여기서 제외할 필드명이다. 필터를 작성했으면 import하여 view에서 사용하게 된다.

from .filters import OrderFilter

def customer(request, pk):
    customer=Customer.objects.get(id=pk)

    orders=customer.order_set.all()
    order_count=orders.count()

    myFilter=OrderFilter(request.GET, queryset=orders)
    orders=myFilter.qs

    context={'customer':customer, 'orders':orders, 'order_count':order_count, 'myFilter':myFilter}
    return render(request, 'accounts/customer.html', context)

필터를 사용할 때는 기본적으로 데이터베이스에 있는 데이터를 가져오는 작업이기 때문에 request.GET을 인자로 받는다. orders는 처음에 customer와 관계있는 모든 데이터를 받는다. myFilter에는 받은 데이터를 쿼리셋에 넣어서 사용자가 검색한 조건에 맞추어 필터링한 데이터가 저장된다. 그리고 orders의 값을 필터링한 값으로 재지정한다.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글