장고:글쓰기
편집하기
Sam
(
토론
|
기여
)
님의 2020년 11월 11일 (수) 06:58 판
(
→글 수정
)
(
차이
)
← 이전 판
|
최신판
(
차이
) |
다음 판 →
(
차이
)
둘러보기로 이동
검색으로 이동
경고: 이 문서의 오래된 판을 편집하고 있습니다.
이것을 게시하면, 이 판 이후로 바뀐 모든 편집이 사라집니다.
경고:
로그인하지 않았습니다. 편집을 하면 IP 주소가 공개되게 됩니다.
로그인
하거나
계정을 생성하면
편집자가 사용자 이름으로 기록되고, 다른 장점도 있습니다.
스팸 방지 검사입니다. 이것을 입력하지
마세요
!
{{장고}} =개요= 글쓰기 기능을 구현하기 위해 정리한 문서. 기본적으로 포스팅 기능이지만, 모델명을 Post로 하기엔 전송방식인 post와 혼동이 오곤 해서 question으로 구현한다. =url 작성= urls.py 안에 필요한 기능을 다 담아주어야 한다. 예컨대, 글을 쓴다면 만들기, 편집, 삭제기능을 만들어주어야 하기에 다음과 같이 기입한다. (뷰의 설정이나, import방식에 따라 뷰를 불러오는 방식이 달라질 수 있다.) {| class="wikitable" |+ !일반적으로 코드를 짜는 경우 !제네릭 뷰를 사용하는 경우 |- |<syntaxhighlight lang="python"> from django.urls import path from . import views #해당 앱의 뷰를 불러온다. app_name = 'pool' urlpatterns = [ path('question/', views.list, name='list'),#글의 리스트롤 보여주는 화면. path('question/detail/<int:question_id>/', views.detail, name='detail'),#글의 내용을 보여주는 화면. path('question/create/', views.create, name='create'),#글의 작성화면 path('question/modify/<int:question_id>/', views.modify, name='modify'),#글의 수정화면 path('question/delete/<int:question_id>/', views.delete, name='delete'),#글의 삭제화면 ] </syntaxhighlight> |함수명을 바꾸어주어야 한다. views.클래스뷰명.as_view()), 형태로. 클래스형 뷰임을 지정해주기 위해. |} <nowiki>#</nowiki>아직 함수를 짜주진 않았지만, 앞으로 만들 함수에 대해 연결해두자. =모델 작성= 글에서 포함해야 할 것들이 있다. 작성자, 작성일자, 내용 등의 요소를 포함하여 모델을 작성한다. /앱/models.py 안에 작성한다.<syntaxhighlight lang="python"> from django.contrib.auth.models import User from django.db import models class Question(models.Model):#세부내용은 필요에 따라.. author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author_question') subject = models.CharField(max_length=200) content = models.TextField() create_date = models.DateTimeField() modify_date = models.DateTimeField(null=True, blank=True) def __str__(self):#관리자페이지에 나타낼 객체 이름. return self.subject#이 객체의 subject를 이름으로 쓰겠다는 의미. </syntaxhighlight> ===모델반영=== makemigrations 와 migrate를 실행해 DB를 만든다. ===관리자 기능에서 확인=== 모델이 제대로 작성되고, 글이 제대로 생성될 수 있는지 관리자기능에 등록한 후 작성해보자. 관리자 등록은 [[장고:관리자페이지]] 참조. 이상이 없으면 뷰 작성으로 넘어가자. =뷰, 탬플릿 작성= 탬플릿과 연관이 깊으니, 같이 짜주자. 한 단계, 한 단계 차근차근 작동을 확인하며 넘어가자. ==리스트 보기== {| class="wikitable" ! !일반적으로 코드를 짤 경우 !제네릭뷰(클래스형 뷰)를 쓰는 경우 |- |view 작성 |전체 글을 본다.<syntaxhighlight lang="python"> from django.shortcuts import render from .models import * #모델을 불러온다. def list(request): question_list=Question.objects.order_by('-create_date') #만들어진 순서의 역순으로 정렬 context={'question_list':question_list, } #템플릿으로 보내줄 내용을 담는다. return render(request, 'list.html', context) #list.html로 보낸다. </syntaxhighlight> |전체 글을 본다.<syntaxhighlight lang="python"> from django.views import generic from .models import Question class ListView(generic.ListView): def get_queryset(self): return Question.objects.order_by('-create_date') #템플릿 명이 명시적으로 지정되지 않은 경우에는 자동으로 모델명_list.html을 템플릿명으로 사용. </syntaxhighlight> |- |template작성 |경로에 맞게 해주면 되는데, 위 뷰의 경우엔 /앱이름/template/list.html 에 만들어준다. 만들어진 html파일의 body에 다음과 같이 넣어준다. 상황에 맞게 표를 만들든, 목차를 만들든 html을 짜면 될 터.<syntaxhighlight lang="html"> {% if question_list %} <ul> {% for question in question_list %} <li><a href="{% url 'pool:detail' question.id %}">{{question.subject}}</a></li> {% endfor %} </ul> {% else %} {% endif %} </syntaxhighlight> |템플릿 명이 명시적으로 지정되지 않은 경우에는 자동으로 모델명_list.html을 템플릿명으로 사용. /앱이름/template/question_list.html 을 좌측과 같이 짜주면 된다.<syntaxhighlight lang="python"> #따로 탬플릿 명을 명시하려면 다음의 변수를 get_queryset함수 안에 넣는다. template='앱이름/list.html' </syntaxhighlight>탬플릿에 전달되는 리스트는 question_list라는 이름이었는데, 전달할 이름을 바꾸려면 다음과 같이 넣어준다.<syntaxhighlight lang="python"> #탬플릿에 전달될 리스트 이름을 바꾸려면 get_queryset함수 안에 이 변수를 넣는다. context_object_name='바꿀리스트명' </syntaxhighlight> |} ==글 보기== 리스트에서 글을 클릭하면 글의 상세내용을 볼 수 있어야 한다. {| class="wikitable" ! !일반적으로 코드를 짤 경우 !제네릭뷰(클래스형 뷰)를 쓰는 경우 |- |view 작성 |글 하나의 내용을 본다. get_object_or_404를 쓴다. id에 해당하는 객체가 없으면 서버에서 에러를 일으키는데, 이는 서버에러로 잡힌다. 에러의 방향성을 명확히 지정해주기 위해. 404에러를 부르게끔!<syntaxhighlight lang="python"> from django.shortcuts import render, get_object_or_404 from .models import * #모델을 불러온다. def detail(request,question_id):#url에서 매핑된 question_id가 전달된다. question=get_object_or_404(Question, pk=question_id) context={'question':question} return render(request, 'detail.html', context) </syntaxhighlight> |<syntaxhighlight lang="python"> from django.views import generic class DetailView(generic.DetailView): model = Question #템플릿 명이 명시적으로 지정되지 않은 경우에는 자동으로 모델명_detail.html을 템플릿명으로 사용 </syntaxhighlight>urls.py에서 기입할 때 .as_view를 붙여야 함에 유의하고, 제네릭뷰에 경우, 변수명이 지정되어 있다. urls.py에서 <int:pk> 형태로 기입받는다. |- |template작성 |경로에 맞게 해주면 되는데, 위 뷰의 경우엔 /앱이름/template/detail.html 에 만들어준다. 만들어진 html파일의 body에 다음과 같이 넣어준다. 상황에 맞게 표를 만들든, 목차를 만들든 html을 짜면 될 터. <nowiki>{{변수.속성}}</nowiki> 형태로 필요한 데이터를 가져온다.<syntaxhighlight lang="html"> <h1>{{ question.subject }}</h1> <div> {{ question.content }} </div> </syntaxhighlight> |제네릭 뷰를 쓸 땐 탬플릿에 모델이 object라는 이름으로 전달된다. <code><nowiki>{{object.subject}}</nowiki></code> 형태의 이름을 써서 변수를 사용할 수 있다. |} ==글 작성(폼에러 띄우기)== 장고에서 제공하는 form이 내부에서 어떻게 작동하는지 잘 보이지 않아 쓰지 않으려 했지만.. 자동으로 에러검사도 해주고 편리해 사용하지 않을 이유가 없어 form을 적극적으로 활용한다. {| class="wikitable" ! !일반적으로 코드를 짤 경우 !제네릭뷰(클래스형 뷰)를 쓰는 경우 |- |template작성 | colspan="2" |위에서 만든 list.html을 수정한다. 글 작성을 위한 링크를 추가.<syntaxhighlight lang="html"> <a href="{% url '앱이름:create' %}" class="btn btn-primary">등록</a> </syntaxhighlight> |- |form 작성 |장고에선 form이라는 형태로 입력을 받을 수 있게 고안해두었다. 앱 디렉터리 안에 form.py 파일을 새로 작성한다. 모델과 연결된 모델폼을 사용한다.<syntaxhighlight lang="python"> from django import forms from .models import Question class QuestionForm(forms.ModelForm):#모델폼 상속 class Meta: model = Question#연결할 모델 지정 fields = ['subject', 'content']#모델의 어떤 부분을 입력할지 지정 </syntaxhighlight><br /> |<br /> |- |view 작성 |글을 작성하기 위한 공간 만들기. 일반적으로 페이지를 불러오는 것과 데이터를 저장하는 데 같은 링크를 사용한다. 페이지 요청방식에 따라 달리 작동하게 하면 되는데, POST요청이면 데이터를 저장. (링크를 타고 오는 경우, GET 요청으로 들어온다.) 폼을 사용하는 경우.<syntaxhighlight lang="python"> from .forms import QuestionForm#위에서 작성한 폼 불러오기 from ..models import Question #모델을 불러온다. def create(request): if request.method == 'POST':#포스트로 요청이 들어온다면... 글을 저장. form = QuestionForm(request.POST) #폼을 불러와 내용입력을 받는다. if form.is_valid():#내용이 모델에 맞지 않는 경우, 에러 반환. question = form.save(commit=False) #commit=False 옵션은 저장하지 않고 불러오기만 한다는 의미. #question모델에서 create_date옵션이 null=False이기 때문에 그냥 저장하면 에러가 난다. question.author = request.user # 추가한 속성 author 적용 question.create_date = timezone.now()#현재시간으로 자동 적용 question.save() return redirect('pool:list') #작성이 끝나면 목록화면으로 보낸다. else:#포스트 요청이 아니라면 내용작성 화면으로. form = QuestionForm() context = {'form': form} #폼이 valid하지 않으면 이 안에 에러메시지와 기존 입력값이 담겨 create.html로 보내진다. #어떤 부분이 모델에서의 제약과 맞지 않는지 친절하게 안내한다. return render(request, 'create.html', context) </syntaxhighlight> |<syntaxhighlight lang="python"> from django.views import generic from django.urls import reverse_lazy class create(generic.CreateView): model = Question feilds = ['필드명', '필드명',...]#작성할 필드명 지정 success_url=reverse_lazy('앱이름:detail')#작성에 성공한 경우 보낼 탬플릿. template_name_suffix='_create' #사용하는 탬플릿 명을 '모델명_create.html'로 바꾼다는 의미. 접미사만 바꾼다. #기본 탬플릿은 '모델명_form.html'로 나타난다. </syntaxhighlight> |- |template작성 | colspan="2" |view에서 보내는 탬플릿을 작성한다. 폼을 사용하는 경우.<syntaxhighlight lang="html"> {% extends '연장할탬플릿.html' %} {% block content %} <form method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value='나타낼 텍스트'> </form> {% endblock %} </syntaxhighlight>[아마 action옵션 값을 비워두면 해당 링크로 요청을 보내는 모양이다.] <nowiki>{{form.as_p}}</nowiki>는 폼으로부터 전달된 객체. 입력을 위한 코드를 자동으로 작성한다. 자동으로 <nowiki><p>태그로 감싸준다. 자동으로 생성되기 때문에 CSS를 적용할 수 없는데...</nowiki> forms.py를 수정하면 가능하다. |} ===form.as_p 태그 조작하기=== 자동으로 태그로 감싸주기 때문에 탬플릿에서 편집이 안된다. 이렇게 되면 디자인측면에서 제약이 생기게 되는데.. 개개 입력칸을 편집하기 위해선 form의 수정이 필요하다.<syntaxhighlight lang="python"> class QuestionForm(forms.ModelForm):#모델폼 상속 class Meta: widgets = { #이 속성의 추가로 입력항목에 부트스트랩의 클래스를 추가해 넣을 수 있다. 'subject': forms.TextInput(attrs={'class': 'form-control'}), 'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}), } </syntaxhighlight>그런데, 위처럼 작업하면 디자이너가 .py를 만져야 한다는 점에서 디자이너와 개발자간 역할분리가 흐려지기도 한다는 단점이 있다. 이를 탬플릿에서 직접 다루기 위해선 다시 widgets 속성을 없애고, 탬플릿을 수정해준다. 다음과 같은 형식으로 폼 내부의 속성에 접근할 수 있고, 이들을 html에서 직접 편집할 수 있다.<syntaxhighlight lang="html"> <label for="subject">제목</label> <input type="text" class="form-control" name="subject" id="subject" value="{{ form.subject.value|default_if_none:'' }}"> </syntaxhighlight><nowiki>{{form.subject}}</nowiki>로 접근할 수 있지만, <nowiki>{{form.subject.value}}</nowiki>를 써준 것은, 에러가 나고 페이지를 새로 불러들일 때 기존 입력값을 보존하기 위함이다. <nowiki>|</nowiki>default_if_none:<nowiki>''</nowiki> 은 값을 쓰지 않았을 때 <nowiki>''</nowiki>를 담아주기 위한 것. 초기값을 지정할 수도 있다.(이걸 지정하지 않으면 입력하지 않은 값들에 'none'이라는 문자열이 담겨 꼴이 우스워진다) ===폼 라벨 붙이기=== 모델에서 사용한 subject라는 이름이 웹페이지에 그대로 드러나는데, 내부에서 처리하는 변수명과 외부에 드러나는 라벨이 달라야 할 때가 있다. 이 역시 form을 수정함으로써 기능한다.<syntaxhighlight lang="python"> class QuestionForm(forms.ModelForm):#모델폼 상속 class Meta: labels = { #이 속성의 추가로 나타내는 내용을 표시할 수 있음. 'subject': '제목', 'content': '내용', } </syntaxhighlight> ===에러표시 하기=== 적합한 제목형태가 아니거나, 모델에서 제약한 데이터 형식에 맞지 않는 경우 에러가 뜨는데, 어떤 부분에서 에러가 났는지 사용자에게 알려주어야 한다. 탬플릿에 다음과 같이 추가하자. {% csrf_token %} 태그 아래에 넣으면 적당하다.<syntaxhighlight lang="html"> {% if form.errors %} <!--폼에서 에러가 난 경우--> <div class="alert alert-danger" role="alert"> {% for field in form %}<!--모든 필드를 뒤지며 에러를 찾는다.--> {% if field.errors %} <strong>{{ field.label }}</strong><!--에러가 난 필드의 라벨에 굵음 처리--> {{ field.errors }}<!--어떤 에러인지 보여준다.--> {% endif %} {% endfor %} </div> {% endif %} </syntaxhighlight> ===폼을 안쓸 때 에러표시하기=== 딱히 방법은 없다. view의 context 안에 error_message라고 담아서 보내면 된다. view에서 발생한 에러는 탬플릿에 <nowiki>{{error_message}}</nowiki> 변수로 전달된다. view에서 발생한 에러메시지를 그대로 보여주는 것이다. view에서 try-except를 이용하거나 if를 이용해 에러메시지를 보낸다.<syntaxhighlight lang="python"> try: 어쩌구저쩌구 except(KeyError, 조건):#에러가 나면서 조건을 만족시키면 실행 context={'error_message':'담을메시지'} return render(어쩌구,저쩌구,저쩌구) else:#에러가 발생하지 않거나 위에 해당하는 에러가 아니면 실행 명령 </syntaxhighlight>template에선 아래와 같이 사용한다.<syntaxhighlight lang="html"> {% if error_message %} <!-- view에서 에러가 난 경우--> <p>{{error_message}}</p><!-- 에러메시지를 보여준다. --> {% endif %} </syntaxhighlight>입력값을 저장이 아닌, 그대로 반환하게 하면 form을 사용할 때처럼 페이지가 새로고침 되어도 기존 입력내용들을 보존할 수 있다.<br /> ==글 수정== {| class="wikitable" ! !일반적으로 코드를 짤 경우 !제네릭뷰(클래스형 뷰)를 쓰는 경우 |- |template작성 | colspan="2" |위에서 만든 detail.html을 수정한다. 글 수정을 위한 링크를 추가.<syntaxhighlight lang="html"> {% if request.user == question.author %}<!--해당 작성자 일때만 수정버튼이 보이게끔!--> <a href="{% url '앱이름:modify' question.id%}" class="btn btn-primary">수정</a> {% endif %} </syntaxhighlight>제네릭 뷰를 쓰는 경우 넘겨주는 변수에 <code>pk=question.id</code> 형태로 넣어주어야 한다. |- |view 작성 |글 작성과 동일하게 get처리와 post 처리를 한 함수에서 다룬다.<syntaxhighlight lang="python"> from .forms import QuestionForm#위에서 작성한 폼 불러오기 from .models import Question #모델을 불러온다. from django.contrib import messages#넌필드 오류를 반환하기 위한 것 def modify(request, question_id):#이름을 update로 해도 괜찮았을 듯하다. question = get_object_or_404(Question, pk=question_id) if request.user != question.author: messages.error(request, '수정권한이 없습니다') return redirect('앱이름:detail', question_id=question.id) if request.method == "POST": form = QuestionForm(request.POST, instance=question)#받은 내용을 객체에 담는다. if form.is_valid(): question = form.save(commit=False) question.author = request.user question.modify_date = timezone.now() # 수정일시 자동 저장 question.save() return redirect('앱이름:detail', question_id=question.id) else:#GET으로 요청된 경우. form = QuestionForm(instance=question)#해당 모델의 내용을 가져온다! context = {'form': form} return render(request, 'create.html', context) </syntaxhighlight>messages.error 가 있으면 넌필드 오류를 반환한다. |<syntaxhighlight lang="python"> from django.views import generic from django.urls import reverse_lazy class create(generic.UpdateView): model = Question feilds = ['필드명', '필드명',...]#수정할 수 있는 필드명 지정 success_url=reverse_lazy('앱이름:modify')#작성에 성공한 경우 보낼 탬플릿. template_name_suffix='_update' #사용하는 탬플릿 명을 '모델명_update.html'로 바꾼다는 의미. 접미사만 바꾼다. #기본 탬플릿은 '모델명_form.html'로 나타난다. </syntaxhighlight> |- |template작성 | colspan="2" |글 작성과 동일한 탬플릿을 사용한다. 생성칸에 기존의 데이터만 form에 담아서 보내면 되니까!<syntaxhighlight lang="html"> {% extends '연장할탬플릿.html' %} {% block content %} <form method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value='나타낼 텍스트'> </form> {% endblock %} </syntaxhighlight> |} ==글 삭제(+에러메시지 띄우기)== {| class="wikitable" ! !일반적으로 코드를 짤 경우 !제네릭뷰(클래스형 뷰)를 쓰는 경우 |- |template작성 +JQuery | colspan="2" |위에서 만든 detail.html을 수정한다. 글 수정을 위한 링크를 추가. 글 수정과 동일하게, 편집자만 접근할 수 있게끔 if 태그 안에 넣어준다.<syntaxhighlight lang="html"> <a href="{% url '앱이름:delete' question.id%}" class="btn btn-primary">삭제</a> </syntaxhighlight>제네릭 뷰를 쓰는 경우, 넘겨주는 변수에 <code>pk=question.id</code> 형태로 넣어주어야 한다. 이번엔 특수한 기능을 위해 JQuery도 함께 작성한다. 에러 메시지를 띄우기 위한 확인창을 구현하기 위해서. 기존의 작성법과 조금 다르다. 다음과 같이 수정해주자.<syntaxhighlight lang="html"> <a href="#" data-uri="{% url '앱이름:delete' question.id %}" class="delete">삭제</a> <!-- data-uri 속성은 jQuery에서 $(this).data('uri') 형태로 받아가기 위해서 작성--> <script type='text/javascript'> $(document).ready(function(){ $(".delete").on('click', function() { //delete를 포함하는 클래스가 있으면 작동한다. if(confirm("님, 정말로 지움??")) { location.href = $(this).data('uri'); } }); }); </script> <!-- 취소를 누르면 아무 일도 하지 않는다.--> </syntaxhighlight>JQuery가 포함되어있지 않으면 사용할 수 없다. |- |view 작성 |<syntaxhighlight lang="python"> from .models import Question #모델을 불러온다. @login_required(login_url='membership:login') def delete(request, question_id): question = get_object_or_404(Question, pk=question_id) if request.user != question.author: messages.error(request, '삭제권한이 없습니다') return redirect('앱이름:detail', question_id=question.id) question.delete() return redirect('앱이름:index') </syntaxhighlight> |<syntaxhighlight lang="python"> from django.views import generic from django.urls import reverse_lazy class create(generic.DeleteView): model = Question success_url=reverse_lazy('앱이름:list')#작성에 성공한 경우 보낼 탬플릿. template_name_suffix='_delete' #사용하는 탬플릿 명을 '모델명_delete.html'로 바꾼다는 의미. 접미사만 바꾼다. #기본 탬플릿은 '모델명_confirm_delete.html'로 나타난다. </syntaxhighlight> |- |template작성 |글 삭제 확인페이지.(JQuery를 이용한 경우엔 해당사항 없음.) 글 작성에서 사용한 탬플릿에서 버튼 이름만 바꾼다. (post요청을 통해 들어온 경우에만 삭제하게끔 view 변형 필요.)<syntaxhighlight lang="html"> {% extends '연장할탬플릿.html' %} {% block content %} <form method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value='나타낼 텍스트'> </form> {% endblock %} </syntaxhighlight> |글 삭제 확인페이지로 이동한다.(JQuery를 이용한 경우엔 해당사항 없음.)<syntaxhighlight lang="html"> {% extends '연장할탬플릿.html' %} {% block content%} <form action="" method='post'> {% csrf_token %} <div class="alert alert-danget">나타낼텍스트 {{나타낼변수}} 자유롭게 사용.</div> <input type="submit" value="Delete" class="btn btn-danget" </form> {% endblock %} </syntaxhighlight> |} ==tip== 뷰가 많아지면 /앱/views/posting_view.py 따위의 이름으로 뷰를 나누어 작성하는 것도 전략이다.
요약:
학교의 모든 지식. SMwiki에서의 모든 기여는 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 라이선스로 배포된다는 점을 유의해 주세요(자세한 내용에 대해서는
학교의 모든 지식. SMwiki:저작권
문서를 읽어주세요). 만약 여기에 동의하지 않는다면 문서를 저장하지 말아 주세요.
또한, 직접 작성했거나 퍼블릭 도메인과 같은 자유 문서에서 가져왔다는 것을 보증해야 합니다.
저작권이 있는 내용을 허가 없이 저장하지 마세요!
취소
편집 도움말
(새 창에서 열림)
이 문서에서 사용한 틀:
틀:장고
(
원본 보기
) (보호됨)
둘러보기 메뉴
개인 도구
로그인하지 않음
토론
기여
로그인
이름공간
문서
토론
한국어
보기
읽기
편집
원본 편집
역사 보기
더 보기
검색
둘러보기
대문
최근 바뀜
임의의 문서로
미디어위키 도움말
도구
여기를 가리키는 문서
가리키는 글의 최근 바뀜
특수 문서 목록
문서 정보