장고/점프투장고

forms.py 에서 폼디자인( form.as_p 폼위젯, 수동폼 사용)

나도초딩 2023. 11. 12.

0. views

context 와 return render() 들여쓰기 주의!!!!

def question_create(request):

    form = POST 입력받은 폼 / 빈 폼 저장.

 

 

    context = {'form': form}

    return render()

 

{{ form.as_p }} 는 html 코드가 자동 생성되므로, 디자인 측면에서 제한이 많다. 수동폼으로

1. 폼 위젯과 레이블

class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = ['subject', 'content']

        # widgets = {
        #     'subject': forms.TextInput(attrs={'class': 'form-control'}),
        #     'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
        # }

class QuestionForm(forms.ModelForm):
class Meta:
model = Question
fields = ['subject', 'content']

widgets = {
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
}

class QuestionForm(forms.ModelForm):
class Meta:
model = Question
fields = ['subject', 'content']

widgets = {
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
}
# subject, content 한글로 레이블
labels = {
'subject': '제목',
'content': '내용',
}

 

2.  폼 템플릿.html 에서 {{ form.as_p }} 대신, 폼을 수동으로 작성하자.

forms.py 에서 widgets 항목 삭제.

 

{% extends 'base.html' %}

{% block content %}
<div class="container">
    <h5 class="my-3 border-bottom pb-2">질문등록</h5>
    <form method="post">
        {% csrf_token %}
        <!-- 오류표시 Start -->
        {% if form.errors %}
        <div class="alert alert-danger" role="alert">
            {% for field in form %}
            {% if field.errors %}
            <div>
                <strong>{{ field.label }}</strong>
                {{ field.errors }}
            </div>
            {% endif %}
            {% endfor %}
        </div>
        {% endif %}
        <!-- 오류표시 End -->
        <div class="mb-3">
            <label for="subject" class="form-label">제목</label>
            <input type="text" class="form-control" name="subject" id="subject"
                   value="{{ form.subject.value|default_if_none:'' }}">
        </div>
        <div class="mb-3">
            <label for="content" class="form-label">내용</label>
            <textarea class="form-control" name="content"
                      id="content" rows="10">{{ form.content.value|default_if_none:'' }}</textarea>
        </div>
        <button type="submit" class="btn btn-primary">저장하기</button>
    </form>
</div>
{% endblock %}

 

detail.html 답변 등록도 업그레이드 ( forms.py 사용 + 유효성으로 오류 표시 등)

<form action="{% url 'b2b:answer_create' q.id %}" method="post">
{% csrf_token %}
    <input type="text" name="subject" id="subject">
<textarea name="content" id="content" rows="15"></textarea>
<input type="submit" value="댓글 등록">
</form>

 

views.py 도 forms.py 사용하는 방식으로

변경전)

def answer_create(request, question_id):
    q = get_object_or_404(Question, pk=question_id)
    q.answer_set.create(subject=request.POST.get('subject'), content=request.POST.get('content'),
                        create_date=timezone.now())
    return redirect('b2b:detail', q.id)

 

삽질 많이 했다.... views.py 의 def answer_create 에서 return redirect('b2b:detail', question_id=q.id) 에서 삽질.

urls.py 에서, answer/<int:pk> 인데, views.py 에서 question_id 로 인자를 저장해서 redirect 하니 오류가 생겼다.

매개변수 값만 전달하면 오류가 안났을텐데... 아니면, pk = q.id 라던가

def answer_create(request, question_id):
    q = get_object_or_404(Question, pk=question_id)
    # q.answer_set.create(subject=request.POST.get('subject'), content=request.POST.get('content'), create_date=timezone.now())
    if request.method == 'POST':
        form = AnswerForm(request.POST)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.create_date = timezone.now()
            answer.question = q
            answer.save()
            return redirect('b2b:detail', q.id)
            # urls.py 의 detail 에서, /<int:pk>/ 이므로, pk=q.id 또는 매개변수로 q.id 만 써야한다.
    else:
        return HttpResponseNotAllowed('답변은 GET 방식 불가.')

    # return redirect('b2b:detail', q.id)
    # 'POST" 지만, form.is_valid()하지 않으면, 진행.
    context = {'question': q, 'form': form}
    return render(request, 'b2b:detail.html', context)

 

페이징

기존의 리스트(question_list = Question.objects.order_by('-create_date'))는 전체 리스트였다.

==> 페이지네이터( 기존의 전체 리스트 + get_page(page))

 

##views.py

def index(request):
    # return HttpResponse("안녕하세요 ")
    page = request.GET.get('page', '1')
    question_list = Question.objects.order_by('-create_date')
    paginator = Paginator(question_list, 10)
    page_obj = paginator.get_page(page)
    # 전체 리스트 --> 페이징
    # context = {'question_list': question_list}
    context = {'question_list': page_obj}
    return render(request, 'b2b/question_list.html', context)

## template

{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
    <table class="table">
        <thead>
        <tr class="table-dark">
            <th>번호</th>
            <th>제목</th>
            <th>작성일시</th>
        </tr>
        </thead>
        <tbody>
        {% if question_list %}
        {% for question in question_list %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>
                <a href="{% url 'pybo:detail' question.id %}">{{ question.subject }}</a>
            </td>
            <td>{{ question.create_date }}</td>
        </tr>
        {% endfor %}
        {% else %}
        <tr>
            <td colspan="3">질문이 없습니다.</td>
        </tr>
        {% endif %}
        </tbody>
    </table>
    <!-- 페이징처리 시작 -->
    <ul class="pagination justify-content-center">
        <!-- 이전페이지 -->
        {% if question_list.has_previous %}
        <li class="page-item">
            <a class="page-link" href="?page={{ question_list.previous_page_number }}">이전</a>
        </li>
        {% else %}
        <li class="page-item disabled">
            <a class="page-link" tabindex="-1" aria-disabled="true" href="#">이전</a>
        </li>
        {% endif %}
        <!-- 페이지리스트 -->
        {% for page_number in question_list.paginator.page_range %}
        {% if page_number >= question_list.number|add:-5 and page_number <= question_list.number|add:5 %}
        {% if page_number == question_list.number %}
        <li class="page-item active" aria-current="page">
            <a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>
        </li>
        {% else %}
        <li class="page-item">
            <a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>
        </li>
        {% endif %}
        {% endif %}
        {% endfor %}
        <!-- 다음페이지 -->
        {% if question_list.has_next %}
        <li class="page-item">
            <a class="page-link" href="?page={{ question_list.next_page_number }}">다음</a>
        </li>
        {% else %}
        <li class="page-item disabled">
            <a class="page-link" tabindex="-1" aria-disabled="true" href="#">다음</a>
        </li>
        {% endif %}
    </ul>
    <!-- 페이징처리 끝 -->
    <a href="{% url 'pybo:question_create' %}" class="btn btn-primary">질문 등록하기</a>
</div>
{% endblock %}

 

03-3 템플릿 필터 사용하기 :: add 대응하는 sub(빼기필터) 만들기

 

templatetags/pybo_filter.py

from django import template

register = template.Library()

@register.filter
def sub(value, arg):
	return value - arg

 

## 템플릿에서 사용.

{% load pybo_filter %}

{{ question_list_paginator.count|sub:question_list.start_index|sub:forloop.counter0|add:1)

 

답변 개수 표시하기. / 카테고리 수 표시

 

댓글